Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Spell check.
[simgrid.git] / src / xbt / dynar.cpp
1 /* a generic DYNamic ARray implementation.                                  */
2
3 /* Copyright (c) 2004-2019. The SimGrid Team.
4  * All rights reserved.                                                     */
5
6 /* This program is free software; you can redistribute it and/or modify it
7  * under the terms of the license (GNU LGPL) which comes with this package. */
8
9 #include "xbt/dynar.h"
10 #include "simgrid/Exception.hpp"
11 #include "xbt/ex.h"
12 #include "xbt/log.h"
13 #include "xbt/misc.h"
14 #include "xbt/string.hpp"
15 #include "xbt/sysdep.h"
16 #include <sys/types.h>
17
18 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(xbt_dyn, xbt, "Dynamic arrays");
19
20 static inline void _sanity_check_dynar(xbt_dynar_t dynar)
21 {
22   xbt_assert(dynar, "dynar is nullptr");
23 }
24
25 static inline void _sanity_check_idx(int idx)
26 {
27   xbt_assert(idx >= 0, "dynar idx(=%d) < 0", idx);
28 }
29
30 static inline void _check_inbound_idx(xbt_dynar_t dynar, int idx)
31 {
32   if (idx < 0 || idx >= static_cast<int>(dynar->used)) {
33     throw std::out_of_range(simgrid::xbt::string_printf("dynar is not that long. You asked %d, but it's only %lu long",
34                                                         idx, static_cast<unsigned long>(dynar->used)));
35   }
36 }
37
38 static inline void _check_populated_dynar(xbt_dynar_t dynar)
39 {
40   if (dynar->used == 0) {
41     throw std::out_of_range(simgrid::xbt::string_printf("dynar %p is empty", dynar));
42   }
43 }
44
45 static inline void _xbt_dynar_resize(xbt_dynar_t dynar, unsigned long new_size)
46 {
47   if (new_size != dynar->size) {
48     dynar->size = new_size;
49     dynar->data = xbt_realloc(dynar->data, new_size * dynar->elmsize);
50   }
51 }
52
53 static inline void _xbt_dynar_expand(xbt_dynar_t const dynar, const unsigned long nb)
54 {
55   const unsigned long old_size = dynar->size;
56
57   if (nb > old_size) {
58     const unsigned long expand = 2 * (old_size + 1);
59     _xbt_dynar_resize(dynar, (nb > expand ? nb : expand));
60     XBT_DEBUG("expand %p from %lu to %lu elements", dynar, old_size, dynar->size);
61   }
62 }
63
64 static inline void *_xbt_dynar_elm(const xbt_dynar_t dynar, const unsigned long idx)
65 {
66   char *const data = (char *) dynar->data;
67   const unsigned long elmsize = dynar->elmsize;
68
69   return data + idx * elmsize;
70 }
71
72 static inline void _xbt_dynar_get_elm(void *const dst, const xbt_dynar_t dynar, const unsigned long idx)
73 {
74   void *const elm = _xbt_dynar_elm(dynar, idx);
75
76   memcpy(dst, elm, dynar->elmsize);
77 }
78
79 void xbt_dynar_dump(xbt_dynar_t dynar)
80 {
81   XBT_INFO("Dynar dump: size=%lu; used=%lu; elmsize=%lu; data=%p; free_f=%p",
82         dynar->size, dynar->used, dynar->elmsize, dynar->data, dynar->free_f);
83 }
84
85 /** @brief Constructor
86  *
87  * @param elmsize size of each element in the dynar
88  * @param free_f function to call each time we want to get rid of an element (or nullptr if nothing to do).
89  *
90  * Creates a new dynar. If a free_func is provided, the elements have to be pointer of pointer. That is to say that
91  * dynars can contain either base types (int, char, double, etc) or pointer of pointers (struct **).
92  */
93 xbt_dynar_t xbt_dynar_new(const unsigned long elmsize, void_f_pvoid_t const free_f)
94 {
95   xbt_dynar_t dynar = xbt_new0(s_xbt_dynar_t, 1);
96
97   dynar->size = 0;
98   dynar->used = 0;
99   dynar->elmsize = elmsize;
100   dynar->data = nullptr;
101   dynar->free_f = free_f;
102
103   return dynar;
104 }
105
106 /** @brief Initialize a dynar structure that was not malloc'ed
107  * This can be useful to keep temporary dynars on the stack
108  */
109 void xbt_dynar_init(xbt_dynar_t dynar, const unsigned long elmsize, void_f_pvoid_t const free_f)
110 {
111   dynar->size    = 0;
112   dynar->used    = 0;
113   dynar->elmsize = elmsize;
114   dynar->data    = nullptr;
115   dynar->free_f  = free_f;
116 }
117
118 /** @brief Destroy a dynar that was created with xbt_dynar_init */
119 void xbt_dynar_free_data(xbt_dynar_t dynar)
120 {
121   xbt_dynar_reset(dynar);
122   if (dynar)
123     xbt_free(dynar->data);
124 }
125
126 /** @brief Destructor of the structure not touching to the content
127  *
128  * @param dynar poor victim
129  *
130  * kilkil a dynar BUT NOT its content. Ie, the array is freed, but the content is not touched (the @a free_f function
131  * is not used)
132  */
133 void xbt_dynar_free_container(xbt_dynar_t* dynar)
134 {
135   if (dynar && *dynar) {
136     xbt_dynar_t d = *dynar;
137     xbt_free(d->data);
138     xbt_free(d);
139     *dynar = nullptr;
140   }
141 }
142
143 /** @brief Frees the content and set the size to 0
144  *
145  * @param dynar who to squeeze
146  */
147 void xbt_dynar_reset(xbt_dynar_t const dynar)
148 {
149   _sanity_check_dynar(dynar);
150
151   XBT_CDEBUG(xbt_dyn, "Reset the dynar %p", (void *) dynar);
152   if (dynar->free_f) {
153     xbt_dynar_map(dynar, dynar->free_f);
154   }
155   dynar->used = 0;
156 }
157
158 /** @brief Merge dynar d2 into d1
159  *
160  * @param d1 dynar to keep
161  * @param d2 dynar to merge into d1. This dynar is free at end.
162  */
163 void xbt_dynar_merge(xbt_dynar_t* d1, xbt_dynar_t* d2)
164 {
165   if((*d1)->elmsize != (*d2)->elmsize)
166     xbt_die("Element size must are not equal");
167
168   const unsigned long elmsize = (*d1)->elmsize;
169
170   void *ptr = _xbt_dynar_elm((*d2), 0);
171   _xbt_dynar_resize(*d1, (*d1)->size + (*d2)->size);
172   void *elm = _xbt_dynar_elm((*d1), (*d1)->used);
173
174   memcpy(elm, ptr, ((*d2)->size)*elmsize);
175   (*d1)->used += (*d2)->used;
176   (*d2)->used = 0;
177   xbt_dynar_free(d2);
178 }
179
180 /**
181  * @brief Shrink the dynar by removing empty slots at the end of the internal array
182  * @param dynar a dynar
183  * @param empty_slots_wanted number of empty slots you want to keep at the end of the internal array for further
184  * insertions
185  *
186  * Reduces the internal array size of the dynar to the number of elements plus @a empty_slots_wanted.
187  * After removing elements from the dynar, you can call this function to make the dynar use less memory.
188  * Set @a empty_slots_wanted to zero to reduce the dynar internal array as much as possible.
189  * Note that if @a empty_slots_wanted is greater than the array size, the internal array is expanded instead of shrunk.
190  */
191 void xbt_dynar_shrink(xbt_dynar_t dynar, int empty_slots_wanted)
192 {
193   _xbt_dynar_resize(dynar, dynar->used + empty_slots_wanted);
194 }
195
196 /** @brief Destructor
197  *
198  * @param dynar poor victim
199  *
200  * kilkil a dynar and its content
201  */
202 void xbt_dynar_free(xbt_dynar_t* dynar)
203 {
204   if (dynar && *dynar) {
205     xbt_dynar_reset(*dynar);
206     xbt_dynar_free_container(dynar);
207   }
208 }
209
210 /** @brief free a dynar passed as void* (handy to store dynar in dynars or dict) */
211 void xbt_dynar_free_voidp(void* d)
212 {
213   xbt_dynar_t dynar = (xbt_dynar_t)d;
214   xbt_dynar_free(&dynar);
215 }
216
217 /** @brief Count of dynar's elements
218  *
219  * @param dynar the dynar we want to measure
220  */
221 unsigned long xbt_dynar_length(const xbt_dynar_t dynar)
222 {
223   return (dynar ? (unsigned long) dynar->used : (unsigned long) 0);
224 }
225
226 /**@brief check if a dynar is empty
227  *
228  *@param dynar the dynat we want to check
229  */
230 int xbt_dynar_is_empty(const xbt_dynar_t dynar)
231 {
232   return (xbt_dynar_length(dynar) == 0);
233 }
234
235 /** @brief Retrieve a copy of the Nth element of a dynar.
236  *
237  * @param dynar information dealer
238  * @param idx index of the slot we want to retrieve
239  * @param[out] dst where to put the result to.
240  */
241 void xbt_dynar_get_cpy(const xbt_dynar_t dynar, const unsigned long idx, void* const dst)
242 {
243   _sanity_check_dynar(dynar);
244   _check_inbound_idx(dynar, idx);
245
246   _xbt_dynar_get_elm(dst, dynar, idx);
247 }
248
249 /** @brief Retrieve a pointer to the Nth element of a dynar.
250  *
251  * @param dynar information dealer
252  * @param idx index of the slot we want to retrieve
253  * @return the @a idx-th element of @a dynar.
254  *
255  * @warning The returned value is the actual content of the dynar.
256  * Make a copy before fooling with it.
257  */
258 void* xbt_dynar_get_ptr(const xbt_dynar_t dynar, const unsigned long idx)
259 {
260   void *res;
261   _sanity_check_dynar(dynar);
262   _check_inbound_idx(dynar, idx);
263
264   res = _xbt_dynar_elm(dynar, idx);
265   return res;
266 }
267
268 void* xbt_dynar_set_at_ptr(const xbt_dynar_t dynar, const unsigned long idx)
269 {
270   _sanity_check_dynar(dynar);
271
272   if (idx >= dynar->used) {
273     _xbt_dynar_expand(dynar, idx + 1);
274     if (idx > dynar->used) {
275       memset(_xbt_dynar_elm(dynar, dynar->used), 0, (idx - dynar->used) * dynar->elmsize);
276     }
277     dynar->used = idx + 1;
278   }
279   return _xbt_dynar_elm(dynar, idx);
280 }
281
282 /** @brief Set the Nth element of a dynar (expanded if needed). Previous value at this position is NOT freed
283  *
284  * @param dynar information dealer
285  * @param idx index of the slot we want to modify
286  * @param src What will be feeded to the dynar
287  *
288  * If you want to free the previous content, use xbt_dynar_replace().
289  */
290 void xbt_dynar_set(xbt_dynar_t dynar, const int idx, const void* const src)
291 {
292   memcpy(xbt_dynar_set_at_ptr(dynar, idx), src, dynar->elmsize);
293 }
294
295 /** @brief Set the Nth element of a dynar (expanded if needed). Previous value is freed
296  *
297  * @param dynar
298  * @param idx
299  * @param object
300  *
301  * Set the Nth element of a dynar, expanding the dynar if needed, AND DO free the previous value at this position. If
302  * you don't want to free the previous content, use xbt_dynar_set().
303  */
304 void xbt_dynar_replace(xbt_dynar_t dynar, const unsigned long idx, const void* const object)
305 {
306   _sanity_check_dynar(dynar);
307
308   if (idx < dynar->used && dynar->free_f) {
309     void *const old_object = _xbt_dynar_elm(dynar, idx);
310
311     dynar->free_f(old_object);
312   }
313
314   xbt_dynar_set(dynar, idx, object);
315 }
316
317 /** @brief Make room for a new element, and return a pointer to it
318  *
319  * You can then use regular affectation to set its value instead of relying on the slow memcpy. This is what
320  * xbt_dynar_insert_at_as() does.
321  */
322 void* xbt_dynar_insert_at_ptr(xbt_dynar_t const dynar, const int idx)
323 {
324   void *res;
325   unsigned long old_used;
326   unsigned long new_used;
327   long nb_shift;
328
329   _sanity_check_dynar(dynar);
330   _sanity_check_idx(idx);
331
332   old_used = dynar->used;
333   new_used = old_used + 1;
334
335   _xbt_dynar_expand(dynar, new_used);
336
337   nb_shift = old_used - idx;
338
339   if (nb_shift>0) {
340     memmove(_xbt_dynar_elm(dynar, idx + 1), _xbt_dynar_elm(dynar, idx), nb_shift * dynar->elmsize);
341   }
342
343   dynar->used = new_used;
344   res = _xbt_dynar_elm(dynar, idx);
345   return res;
346 }
347
348 /** @brief Set the Nth dynar's element, expanding the dynar and sliding the previous values to the right
349  *
350  * Set the Nth element of a dynar, expanding the dynar if needed, and moving the previously existing value and all
351  * subsequent ones to one position right in the dynar.
352  */
353 void xbt_dynar_insert_at(xbt_dynar_t const dynar, const int idx, const void* const src)
354 {
355   /* checks done in xbt_dynar_insert_at_ptr */
356   memcpy(xbt_dynar_insert_at_ptr(dynar, idx), src, dynar->elmsize);
357 }
358
359 /** @brief Remove the Nth dynar's element, sliding the previous values to the left
360  *
361  * Get the Nth element of a dynar, removing it from the dynar and moving all subsequent values to one position left in
362  * the dynar.
363  *
364  * If the object argument of this function is a non-null pointer, the removed element is copied to this address. If not,
365  * the element is freed using the free_f function passed at dynar creation.
366  */
367 void xbt_dynar_remove_at(xbt_dynar_t const dynar, const int idx, void* const object)
368 {
369   _sanity_check_dynar(dynar);
370   _check_inbound_idx(dynar, idx);
371
372   if (object) {
373     _xbt_dynar_get_elm(object, dynar, idx);
374   } else if (dynar->free_f) {
375     dynar->free_f(_xbt_dynar_elm(dynar, idx));
376   }
377
378   unsigned long nb_shift = dynar->used - 1 - idx;
379
380   if (nb_shift) {
381     unsigned long offset = nb_shift * dynar->elmsize;
382     memmove(_xbt_dynar_elm(dynar, idx), _xbt_dynar_elm(dynar, idx + 1), offset);
383   }
384
385   dynar->used--;
386 }
387
388 /** @brief Remove a slice of the dynar, sliding the rest of the values to the left
389  *
390  * This function removes an n-sized slice that starts at element idx. It is equivalent to xbt_dynar_remove_at with a
391  * nullptr object argument if n equals to 1.
392  *
393  * Each of the removed elements is freed using the free_f function passed at dynar creation.
394  */
395 void xbt_dynar_remove_n_at(xbt_dynar_t const dynar, const unsigned int n, const int idx)
396 {
397   if (not n)
398     return;
399
400   _sanity_check_dynar(dynar);
401   _check_inbound_idx(dynar, idx);
402   _check_inbound_idx(dynar, idx + n - 1);
403
404   if (dynar->free_f) {
405     for (unsigned long cur = idx; cur < idx + n; cur++) {
406       dynar->free_f(_xbt_dynar_elm(dynar, cur));
407     }
408   }
409
410   unsigned long nb_shift = dynar->used - n - idx;
411
412   if (nb_shift) {
413     unsigned long offset = nb_shift * dynar->elmsize;
414     memmove(_xbt_dynar_elm(dynar, idx), _xbt_dynar_elm(dynar, idx + n), offset);
415   }
416
417   dynar->used -= n;
418 }
419
420 /** @brief Returns the position of the element in the dynar
421  *
422  * Beware that if your dynar contains pointed values (such as strings) instead of scalar, this function compares the
423  * pointer value, not what's pointed. The only solution to search for a pointed value is then to write the foreach loop
424  * yourself:
425  * @code
426  * signed int position = -1;
427  * xbt_dynar_foreach(dynar, iter, elem) {
428  *    if (not memcmp(elem, searched_element, sizeof(*elem))) {
429  *        position = iter;
430  *        break;
431  *    }
432  * }
433  * @endcode
434  *
435  * Raises std::out_of_range if not found. If you have less than 2 millions elements, you probably want to use
436  * #xbt_dynar_search_or_negative() instead, so that you don't have to try/catch on element not found.
437  */
438 unsigned int xbt_dynar_search(xbt_dynar_t const dynar, void* const elem)
439 {
440   unsigned long it;
441
442   for (it = 0; it < dynar->used; it++)
443     if (not memcmp(_xbt_dynar_elm(dynar, it), elem, dynar->elmsize)) {
444       return it;
445     }
446
447   throw std::out_of_range(simgrid::xbt::string_printf("Element %p not part of dynar %p", elem, dynar));
448 }
449
450 /** @brief Returns the position of the element in the dynar (or -1 if not found)
451  *
452  * Beware that if your dynar contains pointed values (such as strings) instead of scalar, this function is probably not
453  * what you want. Check the documentation of xbt_dynar_search() for more info.
454  *
455  * Note that usually, the dynar indices are unsigned integers. If you have more than 2 million elements in your dynar,
456  * this very function will not work (but the other will).
457  */
458 signed int xbt_dynar_search_or_negative(xbt_dynar_t const dynar, void* const elem)
459 {
460   unsigned long it;
461
462   for (it = 0; it < dynar->used; it++)
463     if (not memcmp(_xbt_dynar_elm(dynar, it), elem, dynar->elmsize)) {
464       return it;
465     }
466
467   return -1;
468 }
469
470 /** @brief Returns a boolean indicating whether the element is part of the dynar
471  *
472  * Beware that if your dynar contains pointed values (such as strings) instead of scalar, this function is probably not
473  * what you want. Check the documentation of xbt_dynar_search() for more info.
474  */
475 int xbt_dynar_member(xbt_dynar_t const dynar, void* const elem)
476 {
477   unsigned long it;
478
479   for (it = 0; it < dynar->used; it++)
480     if (not memcmp(_xbt_dynar_elm(dynar, it), elem, dynar->elmsize)) {
481       return 1;
482     }
483
484   return 0;
485 }
486
487 /** @brief Make room at the end of the dynar for a new element, and return a pointer to it.
488  *
489  * You can then use regular affectation to set its value instead of relying on the slow memcpy. This is what
490  * xbt_dynar_push_as() does.
491  */
492 void* xbt_dynar_push_ptr(xbt_dynar_t const dynar)
493 {
494   return xbt_dynar_insert_at_ptr(dynar, dynar->used);
495 }
496
497 /** @brief Add an element at the end of the dynar */
498 void xbt_dynar_push(xbt_dynar_t const dynar, const void* const src)
499 {
500   /* checks done in xbt_dynar_insert_at_ptr */
501   memcpy(xbt_dynar_insert_at_ptr(dynar, dynar->used), src, dynar->elmsize);
502 }
503
504 /** @brief Mark the last dynar's element as unused and return a pointer to it.
505  *
506  * You can then use regular affectation to set its value instead of relying on the slow memcpy. This is what
507  * xbt_dynar_pop_as() does.
508  */
509 void* xbt_dynar_pop_ptr(xbt_dynar_t const dynar)
510 {
511   _check_populated_dynar(dynar);
512   XBT_CDEBUG(xbt_dyn, "Pop %p", (void *) dynar);
513   dynar->used--;
514   return _xbt_dynar_elm(dynar, dynar->used);
515 }
516
517 /** @brief Get and remove the last element of the dynar */
518 void xbt_dynar_pop(xbt_dynar_t const dynar, void* const dst)
519 {
520   /* sanity checks done by remove_at */
521   XBT_CDEBUG(xbt_dyn, "Pop %p", (void *) dynar);
522   xbt_dynar_remove_at(dynar, dynar->used - 1, dst);
523 }
524
525 /** @brief Add an element at the beginning of the dynar.
526  *
527  * This is less efficient than xbt_dynar_push()
528  */
529 void xbt_dynar_unshift(xbt_dynar_t const dynar, const void* const src)
530 {
531   /* sanity checks done by insert_at */
532   xbt_dynar_insert_at(dynar, 0, src);
533 }
534
535 /** @brief Get and remove the first element of the dynar.
536  *
537  * This is less efficient than xbt_dynar_pop()
538  */
539 void xbt_dynar_shift(xbt_dynar_t const dynar, void* const dst)
540 {
541   /* sanity checks done by remove_at */
542   xbt_dynar_remove_at(dynar, 0, dst);
543 }
544
545 /** @brief Apply a function to each member of a dynar
546  *
547  * The mapped function may change the value of the element itself, but should not mess with the structure of the dynar.
548  */
549 void xbt_dynar_map(const xbt_dynar_t dynar, void_f_pvoid_t const op)
550 {
551   char *const data = (char *) dynar->data;
552   const unsigned long elmsize = dynar->elmsize;
553   const unsigned long used = dynar->used;
554   unsigned long i;
555
556   _sanity_check_dynar(dynar);
557
558   for (i = 0; i < used; i++) {
559     char* elm = (char*) data + i * elmsize;
560     op(elm);
561   }
562 }
563
564 /** @brief Removes and free the entry pointed by the cursor
565  *
566  * This function can be used while traversing without problem.
567  */
568 void xbt_dynar_cursor_rm(xbt_dynar_t dynar, unsigned int* const cursor)
569 {
570   xbt_dynar_remove_at(dynar, *cursor, nullptr);
571   *cursor -= 1;
572 }
573
574 /** @brief Sorts a dynar according to the function <tt>compar_fn</tt>
575  *
576  * This function simply apply the classical qsort(3) function to the data stored in the dynar.
577  * You should thus refer to the libc documentation, or to some online tutorial on how to write
578  * a comparison function. Here is a quick example if you have integers in your dynar:
579  *
580  * @verbatim
581  * int cmpfunc (const void * a, const void * b) {
582  *   int intA = *(int*)a;
583  *   int intB = *(int*)b;
584  *   return intA - intB;
585  * }
586  * @endverbatim
587  *
588  * and now to sort a dynar of MSG hosts depending on their speed:
589  * @verbatim
590  * int cmpfunc(const MSG_host_t a, const MSG_host_t b) {
591  *   MSG_host_t hostA = *(MSG_host_t*)a;
592  *   MSG_host_t hostB = *(MSG_host_t*)b;
593  *   return MSG_host_get_speed(hostA) - MSG_host_get_speed(hostB);
594  * }
595  * @endverbatim
596  *
597  * @param dynar the dynar to sort
598  * @param compar_fn comparison function of type (int (compar_fn*) (const void*) (const void*)).
599  */
600 void xbt_dynar_sort(xbt_dynar_t dynar, int_f_cpvoid_cpvoid_t compar_fn)
601 {
602   if (dynar->data != nullptr)
603     qsort(dynar->data, dynar->used, dynar->elmsize, compar_fn);
604 }
605
606 /** @brief Transform a dynar into a nullptr terminated array.
607  *
608  *  @param dynar the dynar to transform
609  *  @return pointer to the first element of the array
610  *
611  *  Note: The dynar won't be usable afterwards.
612  */
613 void* xbt_dynar_to_array(xbt_dynar_t dynar)
614 {
615   void *res;
616   xbt_dynar_shrink(dynar, 1);
617   memset(xbt_dynar_push_ptr(dynar), 0, dynar->elmsize);
618   res = dynar->data;
619   xbt_free(dynar);
620   return res;
621 }
622
623 /** @brief Compare two dynars
624  *
625  *  @param d1 first dynar to compare
626  *  @param d2 second dynar to compare
627  *  @param compar function to use to compare elements
628  *  @return 0 if d1 and d2 are equal and 1 if not equal
629  *
630  *  d1 and d2 should be dynars of pointers. The compar function takes two  elements and returns 0 when they are
631  *  considered equal, and a value different of zero when they are considered different. Finally, d2 is destroyed
632  *  afterwards.
633  */
634 int xbt_dynar_compare(xbt_dynar_t d1, xbt_dynar_t d2, int (*compar)(const void*, const void*))
635 {
636   int i ;
637   int size;
638   if ((not d1) && (not d2))
639     return 0;
640   if ((not d1) || (not d2)) {
641     XBT_DEBUG("nullptr dynar d1=%p d2=%p",d1,d2);
642     xbt_dynar_free(&d2);
643     return 1;
644   }
645   if((d1->elmsize)!=(d2->elmsize)) {
646     XBT_DEBUG("Size of elmsize d1=%lu d2=%lu",d1->elmsize,d2->elmsize);
647     xbt_dynar_free(&d2);
648     return 1; // xbt_die
649   }
650   if(xbt_dynar_length(d1) != xbt_dynar_length(d2)) {
651     XBT_DEBUG("Size of dynar d1=%lu d2=%lu",xbt_dynar_length(d1),xbt_dynar_length(d2));
652     xbt_dynar_free(&d2);
653     return 1;
654   }
655
656   size = xbt_dynar_length(d1);
657   for(i=0;i<size;i++) {
658     void *data1 = xbt_dynar_get_as(d1, i, void *);
659     void *data2 = xbt_dynar_get_as(d2, i, void *);
660     XBT_DEBUG("link[%d] d1=%p d2=%p",i,data1,data2);
661     if(compar(data1,data2)){
662       xbt_dynar_free(&d2);
663       return 1;
664     }
665   }
666   xbt_dynar_free(&d2);
667   return 0;
668 }