4 * Date: Thu Apr 1 17:07:00 2010
6 * GNU recutils - Ordered Heterogeneous Set
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/>.
34 #include <gl_array_list.h>
43 struct rec_mset_elem_s
48 gl_list_node_t list_node
;
50 /* Containing multi-set. */
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
];
66 size_t count
[MAX_NTYPES
];
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
,
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
,
93 /* Destroy the resources used by a mset element, freeing any used
94 memory. The element reference becomes invalid after executing this
97 static void rec_mset_elem_destroy (rec_mset_elem_t elem
);
109 new = malloc (sizeof (struct rec_mset_s
));
116 for (i
= 0; i
< MAX_NTYPES
; i
++)
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
,
129 rec_mset_elem_dispose_fn
,
132 if (new->elem_list
== NULL
)
135 rec_mset_destroy (new);
144 rec_mset_destroy (rec_mset_t mset
)
150 for (i
= 0; i
< mset
->ntypes
; i
++)
152 gl_list_free (mset
->elem_list
);
158 rec_mset_dup (rec_mset_t mset
)
161 rec_mset_elem_t elem
;
162 gl_list_iterator_t iter
;
165 new = rec_mset_new ();
169 /* Register the types. */
170 new->ntypes
= mset
->ntypes
;
171 for (i
= 0; i
< new->ntypes
; i
++)
176 new->name
[i
] = strdup (mset
->name
[i
]);
180 rec_mset_destroy (new);
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
))
198 if (new->dup_fn
[elem
->type
])
200 data
= (new->dup_fn
[elem
->type
]) (elem
->data
);
204 rec_mset_destroy (new);
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
);
225 rec_mset_sort (rec_mset_t mset
)
227 rec_mset_elem_t elem
;
228 gl_list_iterator_t iter
;
231 /* Save a reference to the old gnulib list and create a new, empty
234 list
= mset
->elem_list
;
235 mset
->elem_list
= gl_list_nx_create_empty (GL_ARRAY_LIST
,
236 rec_mset_elem_equal_fn
,
238 rec_mset_elem_dispose_fn
,
240 if (!mset
->elem_list
)
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
256 if (!rec_mset_add_sorted (mset
, elem
->type
, elem
->data
))
258 /* Out of memory. Delete the new list and restore the old
261 gl_list_free (mset
->elem_list
);
262 mset
->elem_list
= list
;
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. */
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!. */
285 rec_mset_type_p (rec_mset_t mset
,
286 rec_mset_type_t type
)
288 return type
< mset
->ntypes
;
292 rec_mset_register_type (rec_mset_t mset
,
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
;
313 rec_mset_count (rec_mset_t mset
,
314 rec_mset_type_t type
)
316 return mset
->count
[type
];
320 rec_mset_get_at (rec_mset_t mset
,
321 rec_mset_type_t type
,
325 rec_mset_elem_t elem
;
327 if ((position
< 0) || (position
>= mset
->count
[type
]))
329 /* Invalid position. */
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
,
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
;
350 gl_list_iterator_t iter
;
351 int count
[MAX_NTYPES
];
355 for (i
= 0; i
< MAX_NTYPES
; i
++)
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
)))
371 count
[cur_elem
->type
]++;
390 rec_mset_remove_at (rec_mset_t mset
,
391 rec_mset_type_t type
,
394 rec_mset_elem_t elem
;
396 bool removed
= false;
398 if (mset
->count
[type
] > 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
))
421 rec_mset_insert_at (rec_mset_t mset
,
422 rec_mset_type_t type
,
426 rec_mset_elem_t elem
= 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
);
440 /* Insert the element at the proper place in the list. */
444 node
= gl_list_nx_add_first (mset
->elem_list
,
447 else if (position
>= mset
->count
[0])
449 node
= gl_list_nx_add_last (mset
->elem_list
,
454 node
= gl_list_nx_add_at (mset
->elem_list
,
461 rec_mset_elem_destroy (elem
);
466 elem
->list_node
= node
;
469 if (elem
->type
!= MSET_ANY
)
471 mset
->count
[elem
->type
]++;
479 rec_mset_append (rec_mset_t mset
,
480 rec_mset_type_t elem_type
,
482 rec_mset_type_t type
)
484 return rec_mset_insert_at (mset
,
487 rec_mset_count (mset
, type
));
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
);
498 /* Update statistics. */
501 if (type
!= MSET_ANY
)
503 mset
->count
[MSET_ANY
]--;
511 rec_mset_insert_after (rec_mset_t mset
,
512 rec_mset_type_t type
,
514 rec_mset_elem_t elem
)
516 rec_mset_elem_t new_elem
;
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
);
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
);
535 node
= gl_list_nx_add_after (mset
->elem_list
,
541 rec_mset_elem_destroy (new_elem
);
545 new_elem
->list_node
= node
;
548 if (new_elem
->type
!= MSET_ANY
)
550 mset
->count
[new_elem
->type
]++;
555 node
= gl_list_nx_add_last (mset
->elem_list
, (void *) elem
);
559 rec_mset_elem_destroy (new_elem
);
563 new_elem
->list_node
= node
;
570 rec_mset_search (rec_mset_t mset
,
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
)
587 gl_list_iterator_free (&iter
);
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
);
611 rec_mset_iterator_next (rec_mset_iterator_t
*iterator
,
612 rec_mset_type_t type
,
614 rec_mset_elem_t
*elem
)
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
628 while ((found
= gl_list_iterator_next (&list_iter
, (const void**) &mset_elem
, &list_node
))
629 && (type
!= 0) && (mset_elem
->type
!= type
));
633 /* Update the multi-set iterator and set both DATA and ELEM. */
635 iterator
->list_iter
= rec_mset_iter_gl2mset (list_iter
);
637 *data
= mset_elem
->data
;
640 mset_elem
->list_node
= list_node
;
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
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
)
668 rec_mset_elem_set_type (rec_mset_elem_t elem
,
669 rec_mset_type_t type
)
671 elem
->mset
->count
[elem
->type
]--;
673 elem
->mset
->count
[type
]++;
677 rec_mset_elem_data (rec_mset_elem_t elem
)
683 rec_mset_elem_set_data (rec_mset_elem_t elem
,
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
,
698 rec_mset_elem_dup_data (rec_mset_elem_t elem
)
700 return elem
->mset
->dup_fn
[elem
->type
] (elem
->data
);
704 rec_mset_dump (rec_mset_t mset
)
706 gl_list_iterator_t iter
;
708 rec_mset_elem_t elem
;
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
]);
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
);
732 printf("END MSET\n");
736 rec_mset_add_sorted (rec_mset_t mset
,
737 rec_mset_type_t type
,
740 rec_mset_elem_t elem
;
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
);
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
,
759 rec_mset_elem_destroy (elem
);
763 elem
->list_node
= node
;
766 if (elem
->type
!= MSET_ANY
)
768 mset
->count
[elem
->type
]++;
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
));
789 rec_mset_elem_equal_fn (const void *e1
,
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
))
804 return (elem1
->mset
->equal_fn
[elem1
->type
]) (elem1
->data
,
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
);
818 rec_mset_elem_compare_fn (const void *e1
,
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
,
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
;
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
;
870 static rec_mset_elem_t
871 rec_mset_elem_new (rec_mset_t mset
,
872 rec_mset_type_t type
,
877 if (type
>= mset
->ntypes
)
882 new = malloc (sizeof (struct rec_mset_elem_s
));
888 new->list_node
= NULL
;
895 rec_mset_elem_destroy (rec_mset_elem_t 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
);
912 /* End of rec-mset.c */