Freeciv21
Develop your civilization from humble roots to a global empire
genlist.cpp
Go to the documentation of this file.
1 /*__ ___ ***************************************
2 / \ / \ Copyright (c) 1996-2021 Freeciv21 and Freeciv
3 \_ \ / __/ contributors. This file is part of Freeciv21.
4  _\ \ / /__ Freeciv21 is free software: you can redistribute it
5  \___ \____/ __/ and/or modify it under the terms of the GNU General
6  \_ _/ Public License as published by the Free Software
7  | @ @ \_ Foundation, either version 3 of the License,
8  | or (at your option) any later version.
9  _/ /\ You should have received a copy of the GNU
10  /o) (o/\ \_ General Public License along with Freeciv21.
11  \_____/ / If not, see https://www.gnu.org/licenses/.
12  \____/ ********************************************************/
13 
14 #include <cstdlib>
15 
16 // utility
17 #include "fcthread.h"
18 #include "log.h"
19 #include "rand.h"
20 
21 #include "genlist.h"
25 struct genlist *genlist_new() { return genlist_new_full(nullptr); }
26 
31 {
32  genlist *pgenlist = new genlist;
33 
34  pgenlist->nelements = 0;
35  pgenlist->head_link = nullptr;
36  pgenlist->tail_link = nullptr;
37  pgenlist->free_data_func = free_data_func;
38 
39  return pgenlist;
40 }
41 
45 void genlist_destroy(struct genlist *pgenlist)
46 {
47  if (pgenlist == nullptr) {
48  return;
49  }
50 
51  genlist_clear(pgenlist);
52  delete pgenlist;
53 }
54 
58 static void genlist_link_new(struct genlist *pgenlist, void *dataptr,
59  struct genlist_link *prev,
60  struct genlist_link *next)
61 {
62  genlist_link *plink = new genlist_link;
63 
64  plink->dataptr = dataptr;
65  plink->prev = prev;
66  if (nullptr != prev) {
67  prev->next = plink;
68  } else {
69  pgenlist->head_link = plink;
70  }
71  plink->next = next;
72  if (nullptr != next) {
73  next->prev = plink;
74  } else {
75  pgenlist->tail_link = plink;
76  }
77  pgenlist->nelements++;
78 }
79 
83 static void genlist_link_destroy(struct genlist *pgenlist,
84  struct genlist_link *plink)
85 {
86  if (pgenlist->head_link == plink) {
87  pgenlist->head_link = plink->next;
88  } else {
89  plink->prev->next = plink->next;
90  }
91 
92  if (pgenlist->tail_link == plink) {
93  pgenlist->tail_link = plink->prev;
94  } else {
95  plink->next->prev = plink->prev;
96  }
97 
98  pgenlist->nelements--;
99 
100  /* NB: detach the link before calling the free function for avoiding
101  * re-entrant code. */
102  if (nullptr != pgenlist->free_data_func) {
103  pgenlist->free_data_func(plink->dataptr);
104  }
105  delete plink;
106 }
107 
114 static struct genlist_link *
115 genlist_link_at_pos(const struct genlist *pgenlist, int pos)
116 {
117  struct genlist_link *plink;
118 
119  if (pos == 0) {
120  return pgenlist->head_link;
121  } else if (pos == -1) {
122  return pgenlist->tail_link;
123  } else if (pos < -1 || pos >= pgenlist->nelements) {
124  return nullptr;
125  }
126 
127  if (pos < pgenlist->nelements / 2) { // fastest to do forward search
128  for (plink = pgenlist->head_link; pos != 0; pos--) {
129  plink = plink->next;
130  }
131  } else { // fastest to do backward search
132  for (plink = pgenlist->tail_link, pos = pgenlist->nelements - pos - 1;
133  pos != 0; pos--) {
134  plink = plink->prev;
135  }
136  }
137 
138  return plink;
139 }
140 
144 struct genlist *genlist_copy(const struct genlist *pgenlist)
145 {
146  return genlist_copy_full(pgenlist, nullptr, pgenlist->free_data_func);
147 }
148 
152 struct genlist *genlist_copy_full(const struct genlist *pgenlist,
153  genlist_copy_fn_t copy_data_func,
155 {
156  struct genlist *pcopy = genlist_new_full(free_data_func);
157 
158  if (pgenlist) {
159  struct genlist_link *plink;
160 
161  if (nullptr != copy_data_func) {
162  for (plink = pgenlist->head_link; plink; plink = plink->next) {
163  genlist_link_new(pcopy, copy_data_func(plink->dataptr),
164  pcopy->tail_link, nullptr);
165  }
166  } else {
167  for (plink = pgenlist->head_link; plink; plink = plink->next) {
168  genlist_link_new(pcopy, plink->dataptr, pcopy->tail_link, nullptr);
169  }
170  }
171  }
172 
173  return pcopy;
174 }
175 
179 int genlist_size(const struct genlist *pgenlist)
180 {
181  fc_assert_ret_val(nullptr != pgenlist, 0);
182  return pgenlist->nelements;
183 }
184 
190 struct genlist_link *genlist_link_get(const struct genlist *pgenlist,
191  int idx)
192 {
193  fc_assert_ret_val(nullptr != pgenlist, nullptr);
194 
195  return genlist_link_at_pos(pgenlist, idx);
196 }
197 
201 struct genlist_link *genlist_tail(const struct genlist *pgenlist)
202 {
203  return (nullptr != pgenlist ? pgenlist->tail_link : nullptr);
204 }
205 
212 void *genlist_get(const struct genlist *pgenlist, int idx)
213 {
214  return genlist_link_data(genlist_link_get(pgenlist, idx));
215 }
216 
220 void *genlist_front(const struct genlist *pgenlist)
221 {
222  return genlist_link_data(genlist_head(pgenlist));
223 }
224 
228 void *genlist_back(const struct genlist *pgenlist)
229 {
230  return genlist_link_data(genlist_tail(pgenlist));
231 }
232 
238 void genlist_clear(struct genlist *pgenlist)
239 {
240  fc_assert_ret(nullptr != pgenlist);
241 
242  if (0 < pgenlist->nelements) {
243  genlist_free_fn_t free_data_func = pgenlist->free_data_func;
244  struct genlist_link *plink = pgenlist->head_link, *plink2;
245 
246  pgenlist->head_link = nullptr;
247  pgenlist->tail_link = nullptr;
248 
249  pgenlist->nelements = 0;
250 
251  if (nullptr != free_data_func) {
252  do {
253  plink2 = plink->next;
254  free_data_func(plink->dataptr);
255  delete plink;
256  } while (nullptr != (plink = plink2));
257  } else {
258  do {
259  plink2 = plink->next;
260  delete plink;
261  } while (nullptr != (plink = plink2));
262  }
263  }
264 }
265 
270 void genlist_unique(struct genlist *pgenlist)
271 {
272  genlist_unique_full(pgenlist, nullptr);
273 }
274 
280 void genlist_unique_full(struct genlist *pgenlist,
281  genlist_comp_fn_t comp_data_func)
282 {
283  fc_assert_ret(nullptr != pgenlist);
284 
285  if (2 <= pgenlist->nelements) {
286  struct genlist_link *plink = pgenlist->head_link, *plink2;
287 
288  if (nullptr != comp_data_func) {
289  do {
290  plink2 = plink->next;
291  if (nullptr != plink2
292  && comp_data_func(plink->dataptr, plink2->dataptr)) {
293  // Remove this element.
294  genlist_link_destroy(pgenlist, plink);
295  }
296  } while ((plink = plink2) != nullptr);
297  } else {
298  do {
299  plink2 = plink->next;
300  if (nullptr != plink2 && plink->dataptr == plink2->dataptr) {
301  // Remove this element.
302  genlist_link_destroy(pgenlist, plink);
303  }
304  } while ((plink = plink2) != nullptr);
305  }
306  }
307 }
308 
317 bool genlist_remove(struct genlist *pgenlist, const void *punlink)
318 {
319  struct genlist_link *plink;
320 
321  fc_assert_ret_val(nullptr != pgenlist, false);
322 
323  for (plink = pgenlist->head_link; nullptr != plink; plink = plink->next) {
324  if (plink->dataptr == punlink) {
325  genlist_link_destroy(pgenlist, plink);
326  return true;
327  }
328  }
329 
330  return false;
331 }
332 
339 int genlist_remove_all(struct genlist *pgenlist, const void *punlink)
340 {
341  struct genlist_link *plink;
342  int count = 0;
343 
344  fc_assert_ret_val(nullptr != pgenlist, 0);
345 
346  for (plink = pgenlist->head_link; nullptr != plink;) {
347  if (plink->dataptr == punlink) {
348  struct genlist_link *pnext = plink->next;
349 
350  genlist_link_destroy(pgenlist, plink);
351  count++;
352  plink = pnext;
353  } else {
354  plink = plink->next;
355  }
356  }
357 
358  return count;
359 }
360 
367 bool genlist_remove_if(struct genlist *pgenlist,
368  genlist_cond_fn_t cond_data_func)
369 {
370  fc_assert_ret_val(nullptr != pgenlist, false);
371 
372  if (nullptr != cond_data_func) {
373  struct genlist_link *plink = pgenlist->head_link;
374 
375  for (; nullptr != plink; plink = plink->next) {
376  if (cond_data_func(plink->dataptr)) {
377  genlist_link_destroy(pgenlist, plink);
378  return true;
379  }
380  }
381  }
382 
383  return false;
384 }
385 
392 int genlist_remove_all_if(struct genlist *pgenlist,
393  genlist_cond_fn_t cond_data_func)
394 {
395  fc_assert_ret_val(nullptr != pgenlist, 0);
396 
397  if (nullptr != cond_data_func) {
398  struct genlist_link *plink = pgenlist->head_link;
399  int count = 0;
400 
401  while (nullptr != plink) {
402  if (cond_data_func(plink->dataptr)) {
403  struct genlist_link *pnext = plink->next;
404 
405  genlist_link_destroy(pgenlist, plink);
406  count++;
407  plink = pnext;
408  } else {
409  plink = plink->next;
410  }
411  }
412  return count;
413  }
414 
415  return 0;
416 }
417 
424 void genlist_erase(struct genlist *pgenlist, struct genlist_link *plink)
425 {
426  fc_assert_ret(nullptr != pgenlist);
427 
428  if (nullptr != plink) {
429  genlist_link_destroy(pgenlist, plink);
430  }
431 }
432 
436 void genlist_pop_front(struct genlist *pgenlist)
437 {
438  fc_assert_ret(nullptr != pgenlist);
439 
440  if (nullptr != pgenlist->head_link) {
441  genlist_link_destroy(pgenlist, pgenlist->head_link);
442  }
443 }
444 
448 void genlist_pop_back(struct genlist *pgenlist)
449 {
450  fc_assert_ret(nullptr != pgenlist);
451 
452  if (nullptr != pgenlist->tail_link) {
453  genlist_link_destroy(pgenlist, pgenlist->tail_link);
454  }
455 }
456 
465 void genlist_insert(struct genlist *pgenlist, void *data, int pos)
466 {
467  fc_assert_ret(nullptr != pgenlist);
468 
469  if (0 == pgenlist->nelements) {
470  // List is empty, ignore pos.
471  genlist_link_new(pgenlist, data, nullptr, nullptr);
472  } else if (0 == pos) {
473  // Prepend.
474  genlist_link_new(pgenlist, data, nullptr, pgenlist->head_link);
475  } else if (-1 >= pos || pos >= pgenlist->nelements) {
476  // Append.
477  genlist_link_new(pgenlist, data, pgenlist->tail_link, nullptr);
478  } else {
479  // Insert before plink.
480  struct genlist_link *plink = genlist_link_at_pos(pgenlist, pos);
481 
482  fc_assert_ret(nullptr != plink);
483  genlist_link_new(pgenlist, data, plink->prev, plink);
484  }
485 }
486 
490 void genlist_insert_after(struct genlist *pgenlist, void *data,
491  struct genlist_link *plink)
492 {
493  fc_assert_ret(nullptr != pgenlist);
494 
495  genlist_link_new(pgenlist, data, plink,
496  nullptr != plink ? plink->next : pgenlist->head_link);
497 }
498 
502 void genlist_insert_before(struct genlist *pgenlist, void *data,
503  struct genlist_link *plink)
504 {
505  fc_assert_ret(nullptr != pgenlist);
506 
507  genlist_link_new(pgenlist, data,
508  nullptr != plink ? plink->prev : pgenlist->tail_link,
509  plink);
510 }
511 
515 void genlist_prepend(struct genlist *pgenlist, void *data)
516 {
517  fc_assert_ret(nullptr != pgenlist);
518 
519  genlist_link_new(pgenlist, data, nullptr, pgenlist->head_link);
520 }
521 
525 void genlist_append(struct genlist *pgenlist, void *data)
526 {
527  fc_assert_ret(nullptr != pgenlist);
528 
529  genlist_link_new(pgenlist, data, pgenlist->tail_link, nullptr);
530 }
531 
538 struct genlist_link *genlist_search(const struct genlist *pgenlist,
539  const void *data)
540 {
541  struct genlist_link *plink;
542 
543  fc_assert_ret_val(nullptr != pgenlist, nullptr);
544 
545  for (plink = pgenlist->head_link; plink; plink = plink->next) {
546  if (plink->dataptr == data) {
547  return plink;
548  }
549  }
550 
551  return nullptr;
552 }
553 
558 struct genlist_link *genlist_search_if(const struct genlist *pgenlist,
559  genlist_cond_fn_t cond_data_func)
560 {
561  fc_assert_ret_val(nullptr != pgenlist, nullptr);
562 
563  if (nullptr != cond_data_func) {
564  struct genlist_link *plink = pgenlist->head_link;
565 
566  for (; nullptr != plink; plink = plink->next) {
567  if (cond_data_func(plink->dataptr)) {
568  return plink;
569  }
570  }
571  }
572 
573  return nullptr;
574 }
575 
587 void genlist_sort(struct genlist *pgenlist,
588  int (*compar)(const void *, const void *))
589 {
590  const size_t n = genlist_size(pgenlist);
591  void **sortbuf;
592  struct genlist_link *myiter;
593  unsigned int i;
594 
595  if (n <= 1) {
596  return;
597  }
598 
599  sortbuf = new void *[n];
600  myiter = genlist_head(pgenlist);
601  for (i = 0; i < n; i++, myiter = myiter->next) {
602  sortbuf[i] = myiter->dataptr;
603  }
604 
605  qsort(sortbuf, n, sizeof(*sortbuf), compar);
606 
607  myiter = genlist_head(pgenlist);
608  for (i = 0; i < n; i++, myiter = myiter->next) {
609  myiter->dataptr = sortbuf[i];
610  }
611  delete[] sortbuf;
612 }
613 
619 void genlist_shuffle(struct genlist *pgenlist)
620 {
621  const int n = genlist_size(pgenlist);
622  std::vector<void *> sortbuf;
623  struct genlist_link *myiter;
624  std::vector<int> shuffle;
625  int i;
626  sortbuf.resize(n);
627  shuffle.resize(n);
628 
629  if (n <= 1) {
630  return;
631  }
632 
633  myiter = genlist_head(pgenlist);
634  for (i = 0; i < n; i++, myiter = myiter->next) {
635  sortbuf[i] = myiter->dataptr;
636  // also create the shuffle list
637  shuffle[i] = i;
638  }
639 
640  // randomize it
641  std::shuffle(shuffle.begin(), shuffle.end(), fc_rand_state());
642 
643  // create the shuffled list
644  myiter = genlist_head(pgenlist);
645  for (i = 0; i < n; i++, myiter = myiter->next) {
646  myiter->dataptr = sortbuf[shuffle[i]];
647  }
648 }
649 
653 void genlist_reverse(struct genlist *pgenlist)
654 {
655  struct genlist_link *head, *tail;
656  int counter;
657 
658  fc_assert_ret(nullptr != pgenlist);
659 
660  head = pgenlist->head_link;
661  tail = pgenlist->tail_link;
662  for (counter = pgenlist->nelements / 2; 0 < counter; counter--) {
663  // Swap.
664  void *temp = head->dataptr;
665  head->dataptr = tail->dataptr;
666  tail->dataptr = temp;
667 
668  head = head->next;
669  tail = tail->prev;
670  }
671 }
672 
676 void genlist_allocate_mutex(struct genlist *pgenlist)
677 {
678  pgenlist->mutex.lock();
679 }
680 
684 void genlist_release_mutex(struct genlist *pgenlist)
685 {
686  pgenlist->mutex.unlock();
687 }
bool genlist_remove(struct genlist *pgenlist, const void *punlink)
Remove an element of the genlist with the specified user-data pointer given by 'punlink'.
Definition: genlist.cpp:317
void genlist_allocate_mutex(struct genlist *pgenlist)
Allocates list mutex.
Definition: genlist.cpp:676
void genlist_insert(struct genlist *pgenlist, void *data, int pos)
Insert a new element in the list, at position 'pos', with the specified user-data pointer 'data'.
Definition: genlist.cpp:465
void genlist_release_mutex(struct genlist *pgenlist)
Releases list mutex.
Definition: genlist.cpp:684
void * genlist_get(const struct genlist *pgenlist, int idx)
Returns the user-data pointer stored in the genlist at the position given by 'idx'.
Definition: genlist.cpp:212
struct genlist_link * genlist_search_if(const struct genlist *pgenlist, genlist_cond_fn_t cond_data_func)
Return the link which fit the conditional function.
Definition: genlist.cpp:558
void * genlist_back(const struct genlist *pgenlist)
Returns the user-data pointer stored in the last element of the genlist.
Definition: genlist.cpp:228
int genlist_remove_all(struct genlist *pgenlist, const void *punlink)
Remove all elements of the genlist with the specified user-data pointer given by 'punlink'.
Definition: genlist.cpp:339
void genlist_erase(struct genlist *pgenlist, struct genlist_link *plink)
Remove the element pointed to plink.
Definition: genlist.cpp:424
void genlist_clear(struct genlist *pgenlist)
Frees all the internal data used by the genlist (but doesn't touch the user-data).
Definition: genlist.cpp:238
void genlist_prepend(struct genlist *pgenlist, void *data)
Insert an item at the start of the list.
Definition: genlist.cpp:515
bool genlist_remove_if(struct genlist *pgenlist, genlist_cond_fn_t cond_data_func)
Remove the first element of the genlist which fit the function (the function return TRUE).
Definition: genlist.cpp:367
void genlist_append(struct genlist *pgenlist, void *data)
Insert an item at the end of the list.
Definition: genlist.cpp:525
void genlist_unique_full(struct genlist *pgenlist, genlist_comp_fn_t comp_data_func)
Remove all duplicates of element from every consecutive group of equal elements in the list (equality...
Definition: genlist.cpp:280
struct genlist_link * genlist_tail(const struct genlist *pgenlist)
Returns the tail link of the genlist.
Definition: genlist.cpp:201
static void genlist_link_new(struct genlist *pgenlist, void *dataptr, struct genlist_link *prev, struct genlist_link *next)
Create a new link.
Definition: genlist.cpp:58
struct genlist_link * genlist_link_get(const struct genlist *pgenlist, int idx)
Returns the link in the genlist at the position given by 'idx'.
Definition: genlist.cpp:190
void genlist_destroy(struct genlist *pgenlist)
Destroys the genlist.
Definition: genlist.cpp:45
void genlist_pop_front(struct genlist *pgenlist)
Remove the first element of the genlist.
Definition: genlist.cpp:436
void * genlist_front(const struct genlist *pgenlist)
Returns the user-data pointer stored in the first element of the genlist.
Definition: genlist.cpp:220
static struct genlist_link * genlist_link_at_pos(const struct genlist *pgenlist, int pos)
Returns a pointer to the genlist link structure at the specified position.
Definition: genlist.cpp:115
void genlist_insert_before(struct genlist *pgenlist, void *data, struct genlist_link *plink)
Insert an item before the link.
Definition: genlist.cpp:502
int genlist_remove_all_if(struct genlist *pgenlist, genlist_cond_fn_t cond_data_func)
Remove all elements of the genlist which fit the function (the function return TRUE).
Definition: genlist.cpp:392
void genlist_unique(struct genlist *pgenlist)
Remove all duplicates of element from every consecutive group of equal elements in the list.
Definition: genlist.cpp:270
void genlist_insert_after(struct genlist *pgenlist, void *data, struct genlist_link *plink)
Insert an item after the link.
Definition: genlist.cpp:490
struct genlist * genlist_copy(const struct genlist *pgenlist)
Returns a new genlist that's a copy of the existing one.
Definition: genlist.cpp:144
void genlist_sort(struct genlist *pgenlist, int(*compar)(const void *, const void *))
Sort the elements of a genlist.
Definition: genlist.cpp:587
void genlist_reverse(struct genlist *pgenlist)
Reverse the order of the elements in the genlist.
Definition: genlist.cpp:653
struct genlist_link * genlist_search(const struct genlist *pgenlist, const void *data)
Return the link where data is equal to 'data'.
Definition: genlist.cpp:538
void genlist_pop_back(struct genlist *pgenlist)
Remove the last element of the genlist.
Definition: genlist.cpp:448
struct genlist * genlist_new()
Create a new empty genlist.
Definition: genlist.cpp:25
struct genlist * genlist_new_full(genlist_free_fn_t free_data_func)
Create a new empty genlist with a free data function.
Definition: genlist.cpp:30
struct genlist * genlist_copy_full(const struct genlist *pgenlist, genlist_copy_fn_t copy_data_func, genlist_free_fn_t free_data_func)
Returns a new genlist that's a copy of the existing one.
Definition: genlist.cpp:152
int genlist_size(const struct genlist *pgenlist)
Returns the number of elements stored in the genlist.
Definition: genlist.cpp:179
void genlist_shuffle(struct genlist *pgenlist)
Randomize the elements of a genlist using the Fisher-Yates shuffle.
Definition: genlist.cpp:619
static void genlist_link_destroy(struct genlist *pgenlist, struct genlist_link *plink)
Free a link.
Definition: genlist.cpp:83
static struct genlist_link * genlist_head(const struct genlist *pgenlist)
Definition: genlist.h:104
static void * genlist_link_data(const struct genlist_link *plink)
Definition: genlist.h:133
bool(* genlist_cond_fn_t)(const void *)
Definition: genlist.h:47
bool(* genlist_comp_fn_t)(const void *, const void *)
Definition: genlist.h:48
void(* genlist_free_fn_t)(void *)
Definition: genlist.h:45
void *(* genlist_copy_fn_t)(const void *)
Definition: genlist.h:46
#define fc_assert_ret(condition)
Definition: log.h:112
#define fc_assert_ret_val(condition, val)
Definition: log.h:114
std::mt19937 & fc_rand_state()
Returns a reference to the current random generator state; eg for save/restore.
Definition: rand.cpp:123
struct genlist_link * tail_link
Definition: genlist.h:57
struct genlist_link * head_link
Definition: genlist.h:56
QMutex mutex
Definition: genlist.h:55
int nelements
Definition: genlist.h:54
genlist_free_fn_t free_data_func
Definition: genlist.h:58