first version upgrade
[devspec.git] / devspec.en_US / project / recutils / src / rec-mset.c
blobb30286bc7cf9cbbbe46a36e775f6be7ec1da21fd
1 /* -*- mode: C -*-
3 * File: rec-mset.c
4 * Date: Thu Apr 1 17:07:00 2010
6 * GNU recutils - Ordered Heterogeneous Set
8 */
10 /* Copyright (C) 2010-2019 Jose E. Marchesi */
12 /* This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, either version 3 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include <config.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdio.h>
32 #include <rec.h>
34 #include <gl_array_list.h>
35 #include <gl_list.h>
38 * Data types.
41 #define MAX_NTYPES 4
43 struct rec_mset_elem_s
45 rec_mset_type_t type;
46 void *data;
48 gl_list_node_t list_node;
50 /* Containing multi-set. */
51 rec_mset_t mset;
54 struct rec_mset_s
56 int ntypes;
58 /* Properties of the element types. */
59 char *name[MAX_NTYPES];
60 rec_mset_disp_fn_t disp_fn[MAX_NTYPES];
61 rec_mset_equal_fn_t equal_fn[MAX_NTYPES];
62 rec_mset_dup_fn_t dup_fn[MAX_NTYPES];
63 rec_mset_compare_fn_t compare_fn[MAX_NTYPES];
65 /* Statistics. */
66 size_t count[MAX_NTYPES];
68 gl_list_t elem_list;
72 * Forward declarations of static functions.
75 static void rec_mset_init (rec_mset_t mset);
77 static bool rec_mset_elem_equal_fn (const void *e1,
78 const void *e2);
79 static void rec_mset_elem_dispose_fn (const void *e);
80 static int rec_mset_elem_compare_fn (const void *e1, const void *e2);
82 static rec_mset_list_iter_t rec_mset_iter_gl2mset (gl_list_iterator_t list_iter);
83 static gl_list_iterator_t rec_mset_iter_mset2gl (rec_mset_list_iter_t mset_iter);
85 /* Create a new element to be stored in a given mset, of the givent
86 type, and return it. NULL is returned if there is no enough memory
87 to perform the operation. */
89 static rec_mset_elem_t rec_mset_elem_new (rec_mset_t mset,
90 rec_mset_type_t type,
91 void *data);
93 /* Destroy the resources used by a mset element, freeing any used
94 memory. The element reference becomes invalid after executing this
95 function. */
97 static void rec_mset_elem_destroy (rec_mset_elem_t elem);
100 * Public functions.
103 rec_mset_t
104 rec_mset_new (void)
106 rec_mset_t new;
107 int i;
109 new = malloc (sizeof (struct rec_mset_s));
110 if (new)
112 rec_mset_init (new);
114 new->ntypes = 1;
116 for (i = 0; i < MAX_NTYPES; i++)
118 new->count[i] = 0;
119 new->name[i] = NULL;
120 new->equal_fn[i] = NULL;
121 new->disp_fn[i] = NULL;
122 new->dup_fn[i] = NULL;
123 new->compare_fn[i] = NULL;
126 new->elem_list = gl_list_nx_create_empty (GL_ARRAY_LIST,
127 rec_mset_elem_equal_fn,
128 NULL,
129 rec_mset_elem_dispose_fn,
130 true);
132 if (new->elem_list == NULL)
134 /* Out of memory. */
135 rec_mset_destroy (new);
136 new = NULL;
140 return new;
143 void
144 rec_mset_destroy (rec_mset_t mset)
146 if (mset)
148 int i;
150 for (i = 0; i < mset->ntypes; i++)
151 free(mset->name[i]);
152 gl_list_free (mset->elem_list);
153 free (mset);
157 rec_mset_t
158 rec_mset_dup (rec_mset_t mset)
160 rec_mset_t new;
161 rec_mset_elem_t elem;
162 gl_list_iterator_t iter;
163 int i;
165 new = rec_mset_new ();
167 if (new)
169 /* Register the types. */
170 new->ntypes = mset->ntypes;
171 for (i = 0; i < new->ntypes; i++)
173 new->count[i] = 0;
174 if (mset->name[i])
176 new->name[i] = strdup (mset->name[i]);
177 if (!new->name[i])
179 /* Out of memory. */
180 rec_mset_destroy (new);
181 return NULL;
184 new->disp_fn[i] = mset->disp_fn[i];
185 new->equal_fn[i] = mset->equal_fn[i];
186 new->dup_fn[i] = mset->dup_fn[i];
187 new->compare_fn[i] = mset->compare_fn[i];
190 /* Duplicate the elements. */
192 iter = gl_list_iterator (mset->elem_list);
193 while (gl_list_iterator_next (&iter, (const void **) &elem, NULL))
195 void *data = NULL;
197 /* Set the data. */
198 if (new->dup_fn[elem->type])
200 data = (new->dup_fn[elem->type]) (elem->data);
201 if (!data)
203 /* Out of memory. */
204 rec_mset_destroy (new);
205 return NULL;
208 else
210 data = elem->data;
213 /* Append the new data into a new element. */
215 rec_mset_append (new, elem->type, data, MSET_ANY);
218 gl_list_iterator_free (&iter);
221 return new;
224 rec_mset_t
225 rec_mset_sort (rec_mset_t mset)
227 rec_mset_elem_t elem;
228 gl_list_iterator_t iter;
229 gl_list_t list;
231 /* Save a reference to the old gnulib list and create a new, empty
232 one. */
234 list = mset->elem_list;
235 mset->elem_list = gl_list_nx_create_empty (GL_ARRAY_LIST,
236 rec_mset_elem_equal_fn,
237 NULL,
238 rec_mset_elem_dispose_fn,
239 true);
240 if (!mset->elem_list)
242 /* Out of memory. */
243 return NULL;
246 /* Iterate on the old list getting the data of the elements and
247 inserting it into the new sorted gl_list. */
249 iter = gl_list_iterator (list);
250 while (gl_list_iterator_next (&iter, (const void **) &elem, NULL))
252 /* Create a new node list with the proper data and insert it
253 into the list using whatever sorting criteria is implemented
254 by compare_fn. */
256 if (!rec_mset_add_sorted (mset, elem->type, elem->data))
258 /* Out of memory. Delete the new list and restore the old
259 one. */
261 gl_list_free (mset->elem_list);
262 mset->elem_list = list;
263 return NULL;
266 /* We don't want the memory used by the element to be disposed
267 when the old list gets destroyed. The generic element
268 disposal function always checks if the data is NULL before
269 invoking the corresponding disp_fn callback. */
271 elem->data = NULL;
273 gl_list_iterator_free (&iter);
275 /* Destroy the memory used by the old list, but removing the
276 dispose_fn callbacks first for the proper types in order to avoid
277 the disposal of the elements!. */
279 gl_list_free (list);
281 return mset;
284 bool
285 rec_mset_type_p (rec_mset_t mset,
286 rec_mset_type_t type)
288 return type < mset->ntypes;
291 rec_mset_type_t
292 rec_mset_register_type (rec_mset_t mset,
293 char *name,
294 rec_mset_disp_fn_t disp_fn,
295 rec_mset_equal_fn_t equal_fn,
296 rec_mset_dup_fn_t dup_fn,
297 rec_mset_compare_fn_t compare_fn)
299 rec_mset_type_t new_type;
301 new_type = mset->ntypes++;
302 mset->count[new_type] = 0;
303 mset->name[new_type] = strdup (name);
304 mset->disp_fn[new_type] = disp_fn;
305 mset->equal_fn[new_type] = equal_fn;
306 mset->dup_fn[new_type] = dup_fn;
307 mset->compare_fn[new_type] = compare_fn;
309 return new_type;
312 size_t
313 rec_mset_count (rec_mset_t mset,
314 rec_mset_type_t type)
316 return mset->count[type];
319 void *
320 rec_mset_get_at (rec_mset_t mset,
321 rec_mset_type_t type,
322 size_t position)
324 void *result;
325 rec_mset_elem_t elem;
327 if ((position < 0) || (position >= mset->count[type]))
329 /* Invalid position. */
330 return NULL;
333 if (type == MSET_ANY)
335 /* An element of any type was requested. Simply call the gnulib
336 list get_at function, that will use the most efficient way to
337 retrieve the element. */
339 elem = (rec_mset_elem_t) gl_list_get_at (mset->elem_list,
340 position);
343 else
345 /* Iterate on the elements in the gnulib list until the
346 POSITIONth element of the specified type is found. */
348 rec_mset_elem_t cur_elem;
349 gl_list_node_t node;
350 gl_list_iterator_t iter;
351 int count[MAX_NTYPES];
352 int i = 0;
354 elem = NULL;
355 for (i = 0; i < MAX_NTYPES; i++)
357 count[i] = 0;
360 iter = gl_list_iterator (mset->elem_list);
361 while (gl_list_iterator_next (&iter, (const void **) &cur_elem, &node))
363 if ((type == MSET_ANY)
364 || ((type == cur_elem->type) && (count[cur_elem->type] == position)))
366 elem = cur_elem;
367 break;
369 else
371 count[cur_elem->type]++;
372 count[0]++;
377 if (elem)
379 result = elem->data;
381 else
383 result = NULL;
386 return result;
389 bool
390 rec_mset_remove_at (rec_mset_t mset,
391 rec_mset_type_t type,
392 size_t position)
394 rec_mset_elem_t elem;
395 void *data;
396 bool removed = false;
398 if (mset->count[type] > 0)
400 if (position < 0)
402 position = 0;
404 if (position >= mset->count[type])
406 position = mset->count[type] - 1;
409 data = rec_mset_get_at (mset, type, position);
410 elem = rec_mset_search (mset, data);
411 if (rec_mset_remove_elem (mset, elem))
413 removed = true;
417 return removed;
420 rec_mset_elem_t
421 rec_mset_insert_at (rec_mset_t mset,
422 rec_mset_type_t type,
423 void *data,
424 size_t position)
426 rec_mset_elem_t elem = NULL;
427 gl_list_node_t node;
429 node = NULL;
431 /* Create the mset element to insert in the gl_list, returning NULL
432 if there is no enough memory. */
434 elem = rec_mset_elem_new (mset, type, data);
435 if (!elem)
437 return NULL;
440 /* Insert the element at the proper place in the list. */
442 if (position < 0)
444 node = gl_list_nx_add_first (mset->elem_list,
445 (void *) elem);
447 else if (position >= mset->count[0])
449 node = gl_list_nx_add_last (mset->elem_list,
450 (void *) elem);
452 else
454 node = gl_list_nx_add_at (mset->elem_list,
455 position,
456 (void *) elem);
459 if (node == NULL)
461 rec_mset_elem_destroy (elem);
462 elem = NULL;
464 else
466 elem->list_node = node;
468 mset->count[0]++;
469 if (elem->type != MSET_ANY)
471 mset->count[elem->type]++;
475 return elem;
478 rec_mset_elem_t
479 rec_mset_append (rec_mset_t mset,
480 rec_mset_type_t elem_type,
481 void *data,
482 rec_mset_type_t type)
484 return rec_mset_insert_at (mset,
485 elem_type,
486 data,
487 rec_mset_count (mset, type));
490 bool
491 rec_mset_remove_elem (rec_mset_t mset,
492 rec_mset_elem_t elem)
494 rec_mset_type_t type = elem->type;
495 bool res = gl_list_remove_node (mset->elem_list, elem->list_node);
496 if (res)
498 /* Update statistics. */
500 mset->count[type]--;
501 if (type != MSET_ANY)
503 mset->count[MSET_ANY]--;
507 return res;
510 rec_mset_elem_t
511 rec_mset_insert_after (rec_mset_t mset,
512 rec_mset_type_t type,
513 void *data,
514 rec_mset_elem_t elem)
516 rec_mset_elem_t new_elem;
517 gl_list_node_t node;
519 /* Create the mset element to insert in the gl_list, returning NULL
520 if there is no enough memory. */
522 new_elem = rec_mset_elem_new (mset, type, data);
523 if (!new_elem)
525 return NULL;
528 /* Find the requested place where to insert the new element. If
529 ELEM is not found in the multi-set then the new element is
530 appended to the multi-set. */
532 node = gl_list_search (mset->elem_list, (void *) elem);
533 if (node)
535 node = gl_list_nx_add_after (mset->elem_list,
536 node,
537 (void *) new_elem);
538 if (!node)
540 /* Out of memory. */
541 rec_mset_elem_destroy (new_elem);
542 return NULL;
545 new_elem->list_node = node;
547 mset->count[0]++;
548 if (new_elem->type != MSET_ANY)
550 mset->count[new_elem->type]++;
553 else
555 node = gl_list_nx_add_last (mset->elem_list, (void *) elem);
556 if (!node)
558 /* Out of memory. */
559 rec_mset_elem_destroy (new_elem);
560 return NULL;
563 new_elem->list_node = node;
566 return new_elem;
569 rec_mset_elem_t
570 rec_mset_search (rec_mset_t mset,
571 void *data)
573 rec_mset_elem_t result = NULL;
574 rec_mset_elem_t elem;
575 gl_list_iterator_t iter;
577 iter = gl_list_iterator (mset->elem_list);
578 while (gl_list_iterator_next (&iter, (const void **) &elem, NULL))
580 if (elem->data == data)
582 result = elem;
583 break;
587 gl_list_iterator_free (&iter);
589 return result;
592 rec_mset_iterator_t
593 rec_mset_iterator (rec_mset_t mset)
595 gl_list_iterator_t list_iter;
596 rec_mset_iterator_t mset_iter;
598 /* Fill the mset iterator structure. Note that the list_iter field
599 of the mset iterator must have the same size and structure than
600 the gl_list_iterator_t structure. */
602 mset_iter.mset = mset;
604 list_iter = gl_list_iterator (mset->elem_list);
605 mset_iter.list_iter = rec_mset_iter_gl2mset (list_iter);
607 return mset_iter;
610 bool
611 rec_mset_iterator_next (rec_mset_iterator_t *iterator,
612 rec_mset_type_t type,
613 const void **data,
614 rec_mset_elem_t *elem)
616 bool found = true;
617 rec_mset_elem_t mset_elem;
618 gl_list_iterator_t list_iter;
619 gl_list_node_t list_node;
621 /* Extract the list iterator from the multi-set iterator. */
623 list_iter = rec_mset_iter_mset2gl (iterator->list_iter);
625 /* Advance the list iterator until an element of the proper type is
626 found. */
628 while ((found = gl_list_iterator_next (&list_iter, (const void**) &mset_elem, &list_node))
629 && (type != 0) && (mset_elem->type != type));
631 if (found)
633 /* Update the multi-set iterator and set both DATA and ELEM. */
635 iterator->list_iter = rec_mset_iter_gl2mset (list_iter);
636 if (data)
637 *data = mset_elem->data;
638 if (elem)
640 mset_elem->list_node = list_node;
641 *elem = mset_elem;
645 return found;
648 void
649 rec_mset_iterator_free (rec_mset_iterator_t *iterator)
651 gl_list_iterator_t list_iter;
653 /* Extract the list iterator, free it and copy it back to the mset
654 iterator. */
656 list_iter = rec_mset_iter_mset2gl (iterator->list_iter);
657 gl_list_iterator_free (&list_iter);
658 iterator->list_iter = rec_mset_iter_gl2mset (list_iter);
662 rec_mset_elem_type (rec_mset_elem_t elem)
664 return elem->type;
667 void
668 rec_mset_elem_set_type (rec_mset_elem_t elem,
669 rec_mset_type_t type)
671 elem->mset->count[elem->type]--;
672 elem->type = type;
673 elem->mset->count[type]++;
676 void *
677 rec_mset_elem_data (rec_mset_elem_t elem)
679 return elem->data;
682 void
683 rec_mset_elem_set_data (rec_mset_elem_t elem,
684 void *data)
686 elem->data = data;
689 bool
690 rec_mset_elem_equal_p (rec_mset_elem_t elem1,
691 rec_mset_elem_t elem2)
693 return rec_mset_elem_equal_fn ((void *) elem1,
694 (void *) elem2);
697 void *
698 rec_mset_elem_dup_data (rec_mset_elem_t elem)
700 return elem->mset->dup_fn[elem->type] (elem->data);
703 void
704 rec_mset_dump (rec_mset_t mset)
706 gl_list_iterator_t iter;
707 gl_list_node_t node;
708 rec_mset_elem_t elem;
709 int i;
711 printf ("MSET:\n");
712 printf (" ntypes: %d\n", mset->ntypes);
714 for (i = 0; i < mset->ntypes; i++)
716 printf(" type %d:\n", i);
717 printf(" count: %zd\n", mset->count[i]);
718 printf(" disp_fn: %p\n", mset->disp_fn[i]);
719 printf(" equal_fn: %p\n", mset->equal_fn[i]);
720 printf(" dup_fn: %p\n", mset->dup_fn[i]);
723 printf(" nodes:\n");
724 iter = gl_list_iterator (mset->elem_list);
725 while (gl_list_iterator_next (&iter, (const void **) &elem, &node))
727 printf(" node=%p elem=%p elem->type=%d elem->data=%p contained=%p\n", node, elem,
728 elem->type, elem->data, elem->mset);
729 i++;
732 printf("END MSET\n");
735 rec_mset_elem_t
736 rec_mset_add_sorted (rec_mset_t mset,
737 rec_mset_type_t type,
738 void *data)
740 rec_mset_elem_t elem;
741 gl_list_node_t node;
743 /* Create the mset element to insert in the gl_list, returning NULL
744 if there is no enough memory. */
746 elem = rec_mset_elem_new (mset, type, data);
747 if (!elem)
749 return NULL;
752 /* Insert the element at the proper place in the list. */
754 node = gl_sortedlist_nx_add (mset->elem_list,
755 rec_mset_elem_compare_fn,
756 (void *) elem);
757 if (!node)
759 rec_mset_elem_destroy (elem);
760 return NULL;
763 elem->list_node = node;
765 mset->count[0]++;
766 if (elem->type != MSET_ANY)
768 mset->count[elem->type]++;
771 return elem;
775 * Private functions.
778 static void
779 rec_mset_init (rec_mset_t mset)
781 /* Initialize the mset structure so it can be safely passed to
782 rec_mset_destroy even if its contents are not completely
783 initialized with real values. */
785 memset (mset, 0 /* NULL */, sizeof (struct rec_mset_s));
788 static bool
789 rec_mset_elem_equal_fn (const void *e1,
790 const void *e2)
792 rec_mset_elem_t elem1;
793 rec_mset_elem_t elem2;
795 elem1 = (rec_mset_elem_t) e1;
796 elem2 = (rec_mset_elem_t) e2;
798 if ((elem1->mset != elem2->mset)
799 || (elem1->type != elem2->type))
801 return false;
804 return (elem1->mset->equal_fn[elem1->type]) (elem1->data,
805 elem2->data);
808 static void
809 rec_mset_elem_dispose_fn (const void *e)
811 rec_mset_elem_t elem;
813 elem = (rec_mset_elem_t) e;
814 rec_mset_elem_destroy (elem);
817 static int
818 rec_mset_elem_compare_fn (const void *e1,
819 const void *e2)
821 int result = 0;
822 rec_mset_elem_t elem1;
823 rec_mset_elem_t elem2;
825 elem1 = (rec_mset_elem_t) e1;
826 elem2 = (rec_mset_elem_t) e2;
828 if (elem1->mset->compare_fn)
830 result = (elem1->mset->compare_fn[elem1->type]) (elem1->data,
831 elem2->data,
832 elem2->type);
835 return result;
838 static rec_mset_list_iter_t
839 rec_mset_iter_gl2mset (gl_list_iterator_t list_iter)
841 rec_mset_list_iter_t mset_iter;
843 mset_iter.vtable = (void *) list_iter.vtable;
844 mset_iter.list = (void *) list_iter.list;
845 mset_iter.count = list_iter.count;
846 mset_iter.p = list_iter.p;
847 mset_iter.q = list_iter.q;
848 mset_iter.i = list_iter.i;
849 mset_iter.j = list_iter.j;
851 return mset_iter;
854 static gl_list_iterator_t
855 rec_mset_iter_mset2gl (rec_mset_list_iter_t mset_iter)
857 gl_list_iterator_t list_iter;
859 list_iter.vtable = (const struct gl_list_implementation *) mset_iter.vtable;
860 list_iter.list = (gl_list_t) mset_iter.list;
861 list_iter.count = mset_iter.count;
862 list_iter.p = mset_iter.p;
863 list_iter.q = mset_iter.q;
864 list_iter.i = mset_iter.i;
865 list_iter.j = mset_iter.j;
867 return list_iter;
870 static rec_mset_elem_t
871 rec_mset_elem_new (rec_mset_t mset,
872 rec_mset_type_t type,
873 void *data)
875 rec_mset_elem_t new;
877 if (type >= mset->ntypes)
879 return NULL;
882 new = malloc (sizeof (struct rec_mset_elem_s));
883 if (new)
885 new->type = type;
886 new->data = data;
887 new->mset = mset;
888 new->list_node = NULL;
891 return new;
894 static void
895 rec_mset_elem_destroy (rec_mset_elem_t elem)
897 if (elem)
899 /* Dispose the data stored in the element if a disposal callback
900 function was configured by the user. The callback is never
901 invoked if the stored data is NULL. */
903 if (elem->data && elem->mset->disp_fn[elem->type])
905 elem->mset->disp_fn[elem->type] (elem->data);
908 free (elem);
912 /* End of rec-mset.c */