Newer
Older
libkc / modules / src / kc_list_array.c
  1. /**
  2. * @file kc_list_array.c
  3. * @brief 配列リストモジュール
  4. * @copyright 2003 - 2023 Nomura Kei
  5. */
  6. #include <kc.h>
  7. #if defined(__GNUC__)
  8. #define _GNU_SOURCE 1
  9. #if !(KC_IS_WINDOWS)
  10. #define qsort_s qsort_r
  11. #endif
  12. #endif // defined(__GNUC__)
  13.  
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <stdint.h>
  17. #include <string.h>
  18. #include <errno.h>
  19.  
  20. #include <kc_lock_guard.h>
  21. #include <kc_memory.h>
  22. #include <kc_list.h>
  23. #include <kc_iterator.h>
  24.  
  25. /**
  26. * KcArrayList 管理情報
  27. */
  28. typedef struct
  29. {
  30. mtx_t mutex; //!< ロック用
  31. size_t element_size; //!< 要素のサイズ
  32. int init_capacity; //!< 初期指定容量
  33. int capacity; //!< 現在の容量
  34. int size; //!< 現在の要素数
  35. int padding[2]; //!< パディング
  36. void *data; //!< データ格納用バッファ
  37. } KcArrayListInfo;
  38.  
  39. // =============================================================================
  40. // プロトタイプ宣言
  41. // =============================================================================
  42. static int KcArrayList_size(KcList *list);
  43. static bool KcArrayList_is_empty(KcList *list);
  44. static bool KcArrayList_contains(KcList *list, const void *element, size_t size);
  45. static bool KcArrayList_add(KcList *list, int index, const void *element, size_t size);
  46. static bool KcArrayList_remove(KcList *list, int index, void *element, size_t *size);
  47. static void KcArrayList_sort(KcList *list,
  48. int (*comparator)(const void *element1, size_t size1,
  49. const void *element2, size_t size2, void *args),
  50. void *args);
  51. static void KcArrayList_clear(KcList *list);
  52. static void *KcArrayList_get(KcList *list, int index, size_t *size);
  53. static bool KcArrayList_set(KcList *list, int index, const void *element, size_t size,
  54. void *org_element, size_t *org_size);
  55. static int KcArrayList_index_of(KcList *list, const void *element, size_t size);
  56. static int KcArrayList_last_index_of(KcList *list, const void *element, size_t size);
  57. static KcIterator *KcArrayList_iterator(KcList *list, int index);
  58. static void KcArrayList_cleanup_info(KcList *list);
  59.  
  60. static bool KcArrayList_increase_capacity(KcArrayListInfo *info);
  61. static void KcArrayList_reduce_capacity(KcArrayListInfo *info);
  62. static bool KcArrayList_set_capacity(KcArrayListInfo *info, int capacity);
  63.  
  64. /**
  65. * 指定されたサイズの要素を扱う ArrayList を構築します。
  66. *
  67. * @param size 要素のサイズ
  68. * @param cap リストの初期容量
  69. * @return ArrayList
  70. */
  71. KcList *KcList_new_ArrayList(size_t size, int cap)
  72. {
  73. // KcArrayList の管理構造
  74. // +--------------+
  75. // | KcList |
  76. // | ... |
  77. // | _info -----------+
  78. // +--------------+ |
  79. // | <_info> | <---+
  80. // | element_size |
  81. // | capacity |
  82. // | data ----------->+--------------+
  83. // +--------------+ |<data> |
  84. // | element[0] |
  85. // | : |
  86. KcList *list = (KcList *)malloc(sizeof(KcList) + sizeof(KcArrayListInfo));
  87. void *data = malloc(size * cap);
  88. if ((list != NULL) && (data != NULL))
  89. {
  90. list->size = KcArrayList_size;
  91. list->is_empty = KcArrayList_is_empty;
  92. list->contains = KcArrayList_contains;
  93. list->add = KcArrayList_add;
  94. list->remove = KcArrayList_remove;
  95. list->sort = KcArrayList_sort;
  96. list->clear = KcArrayList_clear;
  97. list->get = KcArrayList_get;
  98. list->set = KcArrayList_set;
  99. list->index_of = KcArrayList_index_of;
  100. list->last_index_of = KcArrayList_last_index_of;
  101. list->iterator = KcArrayList_iterator;
  102. list->cleanup_info = KcArrayList_cleanup_info;
  103. list->_info = (list + 1);
  104.  
  105. KcArrayListInfo *info = (KcArrayListInfo *)list->_info;
  106. mtx_init(&(info->mutex), mtx_plain | mtx_recursive);
  107. info->element_size = size;
  108. info->init_capacity = cap;
  109. info->capacity = cap;
  110. info->size = 0;
  111. info->data = data;
  112. }
  113. else
  114. { // 何れかのメモリ確保に失敗したら、メモリを解放する。
  115. free(list);
  116. list = NULL;
  117. free(data);
  118. data = NULL;
  119. }
  120. return list;
  121. }
  122.  
  123. // -----------------------------------------------------------------------------
  124. // size
  125. // -----------------------------------------------------------------------------
  126. /**
  127. * 対象リスト内にある要素の数を返します。
  128. *
  129. * @param list 対象リスト
  130. * @return 対象リスト内の要素数
  131. */
  132. static int KcArrayList_size(KcList *list)
  133. {
  134. KcArrayListInfo *info = (KcArrayListInfo *)list->_info;
  135. int size = -1;
  136. kc_lock_guard(&(info->mutex))
  137. {
  138. size = info->size;
  139. }
  140. return size;
  141. }
  142.  
  143. // -----------------------------------------------------------------------------
  144. // is_empty
  145. // -----------------------------------------------------------------------------
  146. /**
  147. * 対象リストに要素がない場合に true を返します。
  148. *
  149. * @param list 対象リスト
  150. * @return 対象リストに要素が含まれていない場合は true
  151. */
  152. static bool KcArrayList_is_empty(KcList *list)
  153. {
  154. KcArrayListInfo *info = (KcArrayListInfo *)list->_info;
  155. bool is_empty = true;
  156. kc_lock_guard(&(info->mutex))
  157. {
  158. is_empty = (info->size == 0);
  159. }
  160. return is_empty;
  161. }
  162.  
  163. // -----------------------------------------------------------------------------
  164. // contains
  165. // -----------------------------------------------------------------------------
  166. /**
  167. * 指定の要素が対象リストに含まれている場合に true を返します。
  168. * 要素サイズ固定のリストを扱う場合、size の値は無視されます。
  169. *
  170. * @param list 対象リスト
  171. * @param element 対象リスト内にあるかどうか判定される要素
  172. * @param size 要素のサイズ
  173. * @return 指定された要素が対象リスト内にある場合は true
  174. */
  175. static bool KcArrayList_contains(KcList *list, const void *element, size_t size)
  176. {
  177. UNUSED_VARIABLE(size);
  178. KcArrayListInfo *info = (KcArrayListInfo *)list->_info;
  179. typedef uint8_t element_type[info->element_size];
  180. element_type *info_data = (element_type *)info->data;
  181. bool is_contains = false;
  182. kc_lock_guard(&(info->mutex))
  183. {
  184. for (int idx = 0; idx < info->size; idx++)
  185. {
  186. if (memcmp(&info_data[idx], element, info->element_size) == 0)
  187. {
  188. is_contains = true;
  189. break;
  190. }
  191. }
  192. }
  193. return is_contains;
  194. }
  195.  
  196. // -----------------------------------------------------------------------------
  197. // add
  198. // -----------------------------------------------------------------------------
  199. /**
  200. * 対象リスト内の指定された位置に、指定された要素を挿入します。
  201. * index に負値を指定した場合、末尾に要素を追加します。
  202. * その位置の現在の要素(ある場合)とそれ以降の要素を右に移動します。(インデックスに1を加算)。
  203. * 要素サイズ固定のリストを扱う場合、size の値は無視されます。
  204. *
  205. * @param list 対象リスト
  206. * @param index 指定の要素が挿入される位置のインデックス
  207. * @param element 挿入される要素
  208. * @param size 要素のサイズ
  209. * @return true/false (格納成功/失敗)
  210. */
  211. static bool KcArrayList_add(KcList *list, int index, const void *element, size_t size)
  212. {
  213. UNUSED_VARIABLE(size);
  214. KcArrayListInfo *info = (KcArrayListInfo *)list->_info;
  215. typedef uint8_t element_type[info->element_size];
  216.  
  217. // insert_index は常に 0 以上
  218. int insert_index = (index < 0) ? info->size : index;
  219. bool is_success = true;
  220. kc_lock_guard(&(info->mutex))
  221. {
  222. is_success = (insert_index <= info->size) && KcArrayList_increase_capacity(info);
  223. element_type *info_data = (element_type *)info->data;
  224. if (is_success)
  225. {
  226. if (insert_index < info->size)
  227. { // index 以降の要素を右に移動
  228. size_t n = (info->size - insert_index) * info->element_size;
  229. memmove(&info_data[insert_index + 1], &info_data[insert_index], n);
  230. }
  231.  
  232. // データを追加
  233. memcpy(&info_data[insert_index], element, info->element_size);
  234. info->size++;
  235. }
  236. }
  237. return is_success;
  238. }
  239.  
  240. // -----------------------------------------------------------------------------
  241. // remove
  242. // -----------------------------------------------------------------------------
  243. /**
  244. * 対象リスト内の指定された位置にある要素を削除します。
  245. * 後続の要素を左に移動します(インデックスから1を減算)。
  246. * element が NULL でない場合、削除された要素のコピーが格納されます。
  247. * size が NULL でない場合、削除された要素のサイズが格納されます。
  248. *
  249. * @param list 対象リスト
  250. * @param index 削除される要素のインデックス
  251. * @param element 削除された要素をコピーが格納されます。
  252. * @param size 削除された要素のサイズが格納されます。
  253. * @return true/false (削除成功/失敗)
  254. */
  255. static bool KcArrayList_remove(KcList *list, int index, void *element, size_t *size)
  256. {
  257. KcArrayListInfo *info = (KcArrayListInfo *)list->_info;
  258. typedef uint8_t element_type[info->element_size];
  259. element_type *info_data = (element_type *)info->data;
  260.  
  261. bool is_success = false;
  262. kc_lock_guard(&(info->mutex))
  263. {
  264. is_success = ((0 <= index) && (index < info->size));
  265. if (is_success)
  266. {
  267. if (element != NULL)
  268. {
  269. memcpy(element, &info_data[index], info->element_size);
  270. }
  271. if (size != NULL)
  272. {
  273. *size = info->element_size;
  274. }
  275.  
  276. if (index != (info->size - 1))
  277. { // index 以降の要素を左に移動
  278. size_t n = (info->size - (index + 1)) * info->element_size;
  279. memmove(&info_data[index], &info_data[index + 1], n);
  280. }
  281. info->size--;
  282. }
  283.  
  284. // 容量削減
  285. KcArrayList_reduce_capacity(info);
  286. }
  287. return is_success;
  288. }
  289.  
  290. // -----------------------------------------------------------------------------
  291. // sort
  292. // -----------------------------------------------------------------------------
  293. /**
  294. * [内部利用]
  295. * ソート情報
  296. */
  297. typedef struct
  298. {
  299. int (*comparator)(const void *element1, size_t size1,
  300. const void *element2, size_t size2, void *args);
  301. size_t element_size;
  302. void *user_args;
  303. } KcListSortInfo;
  304.  
  305. /**
  306. * [内部利用]
  307. * KcArrayList_sort にて利用される、qsort_s に渡される comparator です。
  308. *
  309. * @param x 比較する要素1
  310. * @param y 比較する要素2
  311. * @param context コンテキスト(KcListSortInfo)
  312. * @return 比較結果
  313. */
  314. #if (KC_IS_WINDOWS)
  315. static int KcArrayList_comparator(void *context, const void *x, const void *y)
  316. #else
  317. static int KcArrayList_comparator(const void *x, const void *y, void *context)
  318. #endif
  319. {
  320. KcListSortInfo *sort_info = (KcListSortInfo *)context;
  321. int ret = sort_info->comparator(x, sort_info->element_size, y, sort_info->element_size, sort_info->user_args);
  322. return ret;
  323. }
  324.  
  325. /**
  326. * 指定された comparator が示す順序に従って、対象リストをソートします。
  327. *
  328. * @param list 対象リスト
  329. * @param comparator リスト要素を比較するために使用される comparator
  330. * @param args comparator の第5引数に渡すオブジェクト
  331. */
  332. static void KcArrayList_sort(KcList *list,
  333. int (*comparator)(const void *element1, size_t size1,
  334. const void *element2, size_t size2, void *args),
  335. void *args)
  336. {
  337. KcArrayListInfo *info = (KcArrayListInfo *)list->_info;
  338. typedef uint8_t element_type[info->element_size];
  339. element_type *info_data = (element_type *)info->data;
  340.  
  341. kc_lock_guard(&(info->mutex))
  342. {
  343. KcListSortInfo sort_info;
  344. sort_info.comparator = comparator;
  345. sort_info.element_size = info->element_size;
  346. sort_info.user_args = args;
  347.  
  348. qsort_s(
  349. info_data,
  350. info->size,
  351. info->element_size,
  352. KcArrayList_comparator,
  353. &sort_info);
  354. }
  355. }
  356.  
  357. // -----------------------------------------------------------------------------
  358. // clear
  359. // -----------------------------------------------------------------------------
  360. /**
  361. * すべての要素を対象リストから削除します。
  362. *
  363. * @param list 対象リスト
  364. */
  365. static void KcArrayList_clear(KcList *list)
  366. {
  367. KcArrayListInfo *info = (KcArrayListInfo *)list->_info;
  368.  
  369. kc_lock_guard(&(info->mutex))
  370. {
  371. ((KcArrayListInfo *)list->_info)->size = 0;
  372. KcArrayList_set_capacity(info, info->init_capacity);
  373. }
  374. }
  375.  
  376. // -----------------------------------------------------------------------------
  377. // get
  378. // -----------------------------------------------------------------------------
  379. /**
  380. * 対象リスト内の指定された位置にある要素を返します。
  381. *
  382. * @param list 対象リスト
  383. * @param index 取得する要素のインデックス
  384. * @param size 要素のサイズを格納するポインタ
  385. * @return 対象リスト内の指定された位置にある要素
  386. */
  387. static void *KcArrayList_get(KcList *list, int index, size_t *size)
  388. {
  389. KcArrayListInfo *info = (KcArrayListInfo *)list->_info;
  390. typedef uint8_t element_type[info->element_size];
  391. element_type *info_data = (element_type *)info->data;
  392.  
  393. element_type *res = NULL;
  394. kc_lock_guard(&(info->mutex))
  395. {
  396. if ((0 <= index) && (index < info->size))
  397. {
  398. res = &info_data[index];
  399. if (size != NULL)
  400. {
  401. *size = info->element_size;
  402. }
  403. }
  404. else
  405. {
  406. errno = EINVAL;
  407. }
  408. }
  409. return res;
  410. }
  411.  
  412. // -----------------------------------------------------------------------------
  413. // set
  414. // -----------------------------------------------------------------------------
  415. /**
  416. * 対象リスト内の指定された位置にある要素を、指定された要素に置き換えます。
  417. * 要素サイズ固定のリストを扱う場合、size の値は無視されます。
  418. * org_element が NULL でない場合、置き換え前の要素のコピーが格納されます。
  419. * org_size が NULL でない場合、置き換え前の要素のサイズが格納されます。
  420. *
  421. * @param list 対象リスト
  422. * @param index 置換される要素のインデックス
  423. * @param element 指定された位置に格納される要素
  424. * @param size 指定された位置に格納される要素のサイズ
  425. * @param org_element 指定された位置に以前あった要素のコピーが格納されます。
  426. * @param org_size 指定された位置に以前あった要素のサイズが格納されます。
  427. * @return true/false (置換成功/失敗)
  428. */
  429. static bool KcArrayList_set(KcList *list, int index, const void *element, size_t size,
  430. void *org_element, size_t *org_size)
  431. {
  432. UNUSED_VARIABLE(size);
  433. KcArrayListInfo *info = (KcArrayListInfo *)list->_info;
  434. typedef uint8_t element_type[info->element_size];
  435. element_type *info_data = (element_type *)info->data;
  436.  
  437. bool is_success = false;
  438. kc_lock_guard(&(info->mutex))
  439. {
  440. is_success = ((0 <= index) && (index < info->size));
  441. if (is_success)
  442. {
  443. if (org_element != NULL)
  444. {
  445. memcpy(org_element, &info_data[index], info->element_size);
  446. }
  447. if (org_size != NULL)
  448. {
  449. *org_size = info->element_size;
  450. }
  451. memcpy(&info_data[index], element, info->element_size);
  452. }
  453. }
  454. return is_success;
  455. }
  456.  
  457. // -----------------------------------------------------------------------------
  458. // index_of
  459. // -----------------------------------------------------------------------------
  460. /**
  461. * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。
  462. * 指定された要素がこのリストにない場合は -1 を返します。
  463. * 要素サイズ固定のリストを扱う場合、size の値は無視されます。
  464. *
  465. * @param list 対象リスト
  466. * @param element 検索する要素
  467. * @param size 検索する要素のサイズ
  468. * @return 指定された要素がこのリスト内で最初に検出された位置のインデックス
  469. */
  470. static int KcArrayList_index_of(KcList *list, const void *element, size_t size)
  471. {
  472. UNUSED_VARIABLE(size);
  473. KcArrayListInfo *info = (KcArrayListInfo *)list->_info;
  474. typedef uint8_t element_type[info->element_size];
  475. element_type *info_data = (element_type *)info->data;
  476. int result_index = -1;
  477. kc_lock_guard(&(info->mutex))
  478. {
  479. for (int idx = 0; idx < info->size; idx++)
  480. {
  481. if (memcmp(&info_data[idx], element, info->element_size) == 0)
  482. {
  483. result_index = idx;
  484. break;
  485. }
  486. }
  487. }
  488. return result_index;
  489. }
  490.  
  491. // -----------------------------------------------------------------------------
  492. // last_index_of
  493. // -----------------------------------------------------------------------------
  494. /**
  495. * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。
  496. * 指定された要素がこのリストにない場合は -1 を返します。
  497. * 要素サイズ固定のリストを扱う場合、size の値は無視されます。
  498. *
  499. * @param list 対象リスト
  500. * @param element 検索する要素
  501. * @param size 検索する要素のサイズ
  502. * @return 指定された要素がこのリスト内で最後に検出された位置のインデックス
  503. */
  504. static int KcArrayList_last_index_of(KcList *list, const void *element, size_t size)
  505. {
  506. UNUSED_VARIABLE(size);
  507. KcArrayListInfo *info = (KcArrayListInfo *)list->_info;
  508. typedef uint8_t element_type[info->element_size];
  509. element_type *info_data = (element_type *)info->data;
  510. int result_index = -1;
  511. kc_lock_guard(&(info->mutex))
  512. {
  513. for (int idx = (info->size - 1); idx >= 0; idx--)
  514. {
  515. if (memcmp(&info_data[idx], element, info->element_size) == 0)
  516. {
  517. result_index = idx;
  518. break;
  519. }
  520. }
  521. }
  522. return result_index;
  523. }
  524.  
  525. ////////////////////////////////////////////////////////////////////////////////
  526. //
  527. // Iterator
  528. //
  529.  
  530. /**
  531. * ArrayListe の Iterator 管理用構造体
  532. */
  533. typedef struct KcArrayListIteratorEntry_
  534. {
  535. KcArrayListInfo *info; //!< ArrayList情報
  536. int current; //!< 現在のインデックス
  537. int size; //!< リストのサイズ
  538. } KcArrayListIteratorEntry;
  539.  
  540. /**
  541. * 次の要素があるか否かを返します。
  542. *
  543. * @param ite Iterator
  544. * @return true/false (次の要素が存在する/存在しない)
  545. */
  546. static bool KcArrayListIterator_hasNext(KcIterator *ite)
  547. {
  548. KcArrayListIteratorEntry *entry = (KcArrayListIteratorEntry *)ite->_info;
  549. return (entry->current < entry->size);
  550. }
  551.  
  552. /**
  553. * 次の要素を取得します。
  554. * size が指定されている場合、次の要素が size に格納されます。
  555. * 次の要素がない場合、NULL を返します。
  556. *
  557. * @param ite Iterator
  558. * @param size 要素のサイズ格納用
  559. * @return 次の要素
  560. */
  561. static const void *KcArrayListIterator_next(KcIterator *ite, size_t *size)
  562. {
  563. KcArrayListIteratorEntry *entry = (KcArrayListIteratorEntry *)ite->_info;
  564. typedef uint8_t element_type[entry->info->element_size];
  565. element_type *info_data = (element_type *)entry->info->data;
  566.  
  567. if (size != NULL)
  568. {
  569. *size = entry->info->element_size;
  570. }
  571. int tmp_index = entry->current;
  572. entry->current++;
  573. return info_data[tmp_index];
  574. }
  575.  
  576. /**
  577. * このリスト内の指定された位置で始まる、リスト内の要素を反復するイテレータを返します。
  578. *
  579. * @param list 対象リスト
  580. * @param index イテレータから返される最初の要素のインデックス
  581. * @return リスト内の指定された位置で始まる、リスト内の要素を反復するイテレータ
  582. */
  583. static KcIterator *KcArrayList_iterator(KcList *list, int index)
  584. {
  585. size_t ite_size = sizeof(KcIterator) + sizeof(KcArrayListIteratorEntry);
  586. KcIterator *ite = (KcIterator *)malloc(ite_size);
  587. if (ite)
  588. {
  589. ite->hasNext = KcArrayListIterator_hasNext;
  590. ite->next = KcArrayListIterator_next;
  591. KcArrayListIteratorEntry *entry = (KcArrayListIteratorEntry *)(((KcIterator *)ite) + 1);
  592. ite->_info = entry;
  593.  
  594. entry->info = list->_info;
  595. entry->current = index;
  596. entry->size = list->size(list);
  597. }
  598. return ite;
  599. }
  600.  
  601. /**
  602. * リスト情報をクリアします。
  603. *
  604. * @param list 対象リスト
  605. */
  606. static void KcArrayList_cleanup_info(KcList *list)
  607. {
  608. KcArrayListInfo *info = (KcArrayListInfo *)list->_info;
  609. mtx_destroy(&(info->mutex));
  610. free(info->data);
  611. }
  612.  
  613. /**
  614. * 指定されたリスト情報のデータ容量を増やします。
  615. * 容量を増やす必要がない場合、何もせず true を返します。
  616. * 容量を増やすことができない場合、false を返します。
  617. *
  618. * @param list リスト
  619. * @param info 配列リスト情報
  620. * @return true/false (成功/失敗)
  621. */
  622. static bool KcArrayList_increase_capacity(KcArrayListInfo *info)
  623. {
  624. bool is_success = true;
  625. if (info->size >= info->capacity)
  626. {
  627. int new_capacity = info->capacity * 2;
  628. is_success = KcArrayList_set_capacity(info, new_capacity);
  629. }
  630. return is_success;
  631. }
  632.  
  633. /**
  634. * 指定されたリスト情報のデータ容量を削減します。
  635. *
  636. * @param list リスト
  637. * @param info 配列リスト情報
  638. */
  639. static void KcArrayList_reduce_capacity(KcArrayListInfo *info)
  640. {
  641. if ((info->capacity > info->init_capacity) && (info->size <= (info->capacity / 4)))
  642. { // 初期容量より大きく、要素数が容量の1/4以下となった場合、容量を1/2に減らす。
  643. int new_capacity = info->capacity / 2;
  644. KcArrayList_set_capacity(info, new_capacity);
  645. }
  646. }
  647.  
  648. /**
  649. * 指定されたリスト情報のデータ容量を指定された capacity に変更します。
  650. *
  651. * @param info 配列リスト情報
  652. * @return true/false (成功/失敗)
  653. */
  654. static bool KcArrayList_set_capacity(KcArrayListInfo *info, int capacity)
  655. {
  656. void *ptr = realloc(info->data, (info->element_size * capacity));
  657. if (ptr != NULL)
  658. {
  659. info->data = ptr;
  660. info->capacity = capacity;
  661. return true;
  662. }
  663. return false;
  664. }