2 * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
4 * gstiterator.h: Base class for iterating datastructures.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
22 #include "gst_private.h"
23 #include <gst/gstiterator.h>
26 gst_iterator_init (GstIterator * it,
28 guint32 * master_cookie,
29 GstIteratorNextFunction next,
30 GstIteratorItemFunction item,
31 GstIteratorResyncFunction resync, GstIteratorFreeFunction free)
34 it->master_cookie = master_cookie;
35 it->cookie = *master_cookie;
45 * @size: the size of the iterator structure
46 * @lock: pointer to a #GMutex.
47 * @master_cookie: pointer to a guint32 to protect the iterated object.
48 * @next: function to get next item
49 * @item: function to call on each item retrieved
50 * @resync: function to resync the iterator
51 * @free: function to free the iterator
53 * Create a new iterator. This function is mainly used for objects
54 * implementing the next/resync/free function to iterate a data structure.
56 * For each item retrieved, the @item function is called with the lock
57 * held. The @free function is called when the iterator is freed.
59 * Returns: the new #GstIterator.
64 gst_iterator_new (guint size,
66 guint32 * master_cookie,
67 GstIteratorNextFunction next,
68 GstIteratorItemFunction item,
69 GstIteratorResyncFunction resync, GstIteratorFreeFunction free)
73 g_return_val_if_fail (size >= sizeof (GstIterator), NULL);
74 g_return_val_if_fail (master_cookie != NULL, NULL);
75 g_return_val_if_fail (next != NULL, NULL);
76 g_return_val_if_fail (resync != NULL, NULL);
77 g_return_val_if_fail (free != NULL, NULL);
79 result = g_malloc (size);
80 gst_iterator_init (result, lock, master_cookie, next, item, resync, free);
88 typedef struct _GstListIterator
93 GList *list; /* pointer in list */
94 GstIteratorDisposeFunction freefunc;
97 static GstIteratorResult
98 gst_list_iterator_next (GstListIterator * it, gpointer * elem)
100 if (it->list == NULL)
101 return GST_ITERATOR_DONE;
103 *elem = it->list->data;
104 it->list = g_list_next (it->list);
106 return GST_ITERATOR_OK;
110 gst_list_iterator_resync (GstListIterator * it)
112 it->list = *it->orig;
116 gst_list_iterator_free (GstListIterator * it)
119 it->freefunc (it->owner);
125 * gst_iterator_new_list:
126 * @lock: pointer to a #GMutex protecting the list.
127 * @master_cookie: pointer to a guint32 to protect the list.
128 * @list: pointer to the list
129 * @owner: object owning the list
130 * @item: function to call for each item
131 * @free: function to call when the iterator is freed
133 * Create a new iterator designed for iterating @list.
135 * Returns: the new #GstIterator for @list.
140 gst_iterator_new_list (GMutex * lock,
141 guint32 * master_cookie,
144 GstIteratorItemFunction item, GstIteratorDisposeFunction free)
146 GstListIterator *result;
148 /* no need to lock, nothing can change here */
149 result = (GstListIterator *) gst_iterator_new (sizeof (GstListIterator),
152 (GstIteratorNextFunction) gst_list_iterator_next,
153 (GstIteratorItemFunction) item,
154 (GstIteratorResyncFunction) gst_list_iterator_resync,
155 (GstIteratorFreeFunction) gst_list_iterator_free);
157 result->owner = owner;
159 result->list = *list;
160 result->freefunc = free;
162 return GST_ITERATOR (result);
166 gst_iterator_pop (GstIterator * it)
169 gst_iterator_free (it->pushed);
176 * @it: The #GstIterator to iterate
177 * @elem: pointer to hold next element
179 * Get the next item from the iterator.
181 * Returns: The result of the iteration.
186 gst_iterator_next (GstIterator * it, gpointer * elem)
188 GstIteratorResult result;
190 g_return_val_if_fail (it != NULL, GST_ITERATOR_ERROR);
191 g_return_val_if_fail (elem != NULL, GST_ITERATOR_ERROR);
195 result = gst_iterator_next (it->pushed, elem);
196 if (result == GST_ITERATOR_DONE) {
197 /* we are done with this iterator, pop it and
198 * fallthrough iterating the main iterator again. */
199 gst_iterator_pop (it);
205 if (G_LIKELY (it->lock))
206 g_mutex_lock (it->lock);
208 if (G_UNLIKELY (*it->master_cookie != it->cookie)) {
209 result = GST_ITERATOR_RESYNC;
213 result = it->next (it, elem);
214 if (result == GST_ITERATOR_OK && it->item) {
215 GstIteratorItem itemres;
217 itemres = it->item (it, *elem);
219 case GST_ITERATOR_ITEM_SKIP:
220 if (G_LIKELY (it->lock))
221 g_mutex_unlock (it->lock);
223 case GST_ITERATOR_ITEM_END:
224 result = GST_ITERATOR_DONE;
226 case GST_ITERATOR_ITEM_PASS:
232 if (G_LIKELY (it->lock))
233 g_mutex_unlock (it->lock);
239 * gst_iterator_resync:
240 * @it: The #GstIterator to resync
242 * Resync the iterator. this function is mostly called
243 * after #gst_iterator_next() returned #GST_ITERATOR_RESYNC.
248 gst_iterator_resync (GstIterator * it)
250 g_return_if_fail (it != NULL);
252 gst_iterator_pop (it);
254 if (G_LIKELY (it->lock))
255 g_mutex_lock (it->lock);
257 it->cookie = *it->master_cookie;
258 if (G_LIKELY (it->lock))
259 g_mutex_unlock (it->lock);
264 * @it: The #GstIterator to free
271 gst_iterator_free (GstIterator * it)
273 g_return_if_fail (it != NULL);
275 gst_iterator_pop (it);
282 * @it: The #GstIterator to use
283 * @other: The #GstIterator to push
285 * Pushes @other iterator onto @it. All calls performed on @it are
286 * forwarded tot @other. If @other returns #GST_ITERATOR_DONE, it is
287 * popped again and calls are handled by @it again.
289 * This function is mainly used by objects implementing the iterator
290 * next function to recurse into substructures.
295 gst_iterator_push (GstIterator * it, GstIterator * other)
297 g_return_if_fail (it != NULL);
298 g_return_if_fail (other != NULL);
303 typedef struct _GstIteratorFilter
305 GstIterator iterator;
312 static GstIteratorResult
313 filter_next (GstIteratorFilter * it, gpointer * elem)
315 GstIteratorResult result = GST_ITERATOR_ERROR;
316 gboolean done = FALSE;
320 while (G_LIKELY (!done)) {
323 result = gst_iterator_next (it->slave, &item);
325 case GST_ITERATOR_OK:
326 if (G_LIKELY (GST_ITERATOR (it)->lock))
327 g_mutex_unlock (GST_ITERATOR (it)->lock);
328 if (it->func (item, it->user_data) == 0) {
332 if (G_LIKELY (GST_ITERATOR (it)->lock))
333 g_mutex_lock (GST_ITERATOR (it)->lock);
335 case GST_ITERATOR_RESYNC:
336 case GST_ITERATOR_DONE:
340 g_assert_not_reached ();
348 filter_resync (GstIteratorFilter * it)
350 gst_iterator_resync (it->slave);
354 filter_uninit (GstIteratorFilter * it)
356 it->slave->lock = GST_ITERATOR (it)->lock;
360 filter_free (GstIteratorFilter * it)
363 gst_iterator_free (it->slave);
368 * gst_iterator_filter:
369 * @it: The #GstIterator to filter
370 * @user_data: user data passed to the compare function
371 * @func: the compare function to select elements
373 * Create a new iterator from an existing iterator. The new iterator
374 * will only return those elements that match the given compare function.
375 * The GCompareFunc should return 0 for elements that should be included
378 * When this iterator is freed, @it will also be freed.
380 * Returns: a new #GstIterator.
385 gst_iterator_filter (GstIterator * it, GCompareFunc func, gpointer user_data)
387 GstIteratorFilter *result;
389 g_return_val_if_fail (it != NULL, NULL);
390 g_return_val_if_fail (func != NULL, NULL);
392 result = (GstIteratorFilter *) gst_iterator_new (sizeof (GstIteratorFilter),
393 it->lock, it->master_cookie,
394 (GstIteratorNextFunction) filter_next,
395 (GstIteratorItemFunction) NULL,
396 (GstIteratorResyncFunction) filter_resync,
397 (GstIteratorFreeFunction) filter_free);
400 result->user_data = user_data;
403 return GST_ITERATOR (result);
408 * @iter: The #GstIterator to fold over
409 * @func: the fold function
410 * @ret: the seed value passed to the fold function
411 * @user_data: user data passed to the fold function
413 * Folds @func over the elements of @iter. That is to say, @proc will be called
414 * as @proc (object, @ret, @user_data) for each object in @iter. The normal use
415 * of this procedure is to accumulate the results of operating on the objects in
418 * This procedure can be used (and is used internally) to implement the foreach
419 * and find_custom operations.
421 * The fold will proceed as long as @func returns TRUE. When the iterator has no
422 * more arguments, GST_ITERATOR_DONE will be returned. If @func returns FALSE,
423 * the fold will stop, and GST_ITERATOR_OK will be returned. Errors or resyncs
424 * will cause fold to return GST_ITERATOR_ERROR or GST_ITERATOR_RESYNC as
427 * The iterator will not be freed.
429 * Returns: A #GstIteratorResult, as described above.
434 gst_iterator_fold (GstIterator * iter, GstIteratorFoldFunction func,
435 GValue * ret, gpointer user_data)
438 GstIteratorResult result;
441 result = gst_iterator_next (iter, &item);
443 case GST_ITERATOR_OK:
444 /* fixme: is there a way to ref/unref items? */
445 if (!func (item, ret, user_data))
449 case GST_ITERATOR_RESYNC:
450 case GST_ITERATOR_ERROR:
452 case GST_ITERATOR_DONE:
468 foreach_fold_func (gpointer item, GValue * unused, ForeachFoldData * data)
470 data->func (item, data->user_data);
475 * gst_iterator_foreach:
476 * @iter: The #GstIterator to iterate
477 * @func: the function to call for each element.
478 * @user_data: user data passed to the function
480 * Iterate over all element of @it and call the given function for
483 * Returns: the result call to gst_iterator_fold(). The iterator will not be
489 gst_iterator_foreach (GstIterator * iter, GFunc func, gpointer user_data)
491 ForeachFoldData data;
494 data.user_data = user_data;
496 return gst_iterator_fold (iter, (GstIteratorFoldFunction) foreach_fold_func,
504 } FindCustomFoldData;
507 find_custom_fold_func (gpointer item, GValue * ret, FindCustomFoldData * data)
509 if (data->func (item, data->user_data) == 0) {
510 g_value_set_pointer (ret, item);
518 * gst_iterator_find_custom:
519 * @it: The #GstIterator to iterate
520 * @user_data: user data passed to the compare function
521 * @func: the compare function to use
523 * Find the first element in @it that matches the compare function.
524 * The compare function should return 0 when the element is found.
526 * The iterator will not be freed.
528 * Returns: The element in the iterator that matches the compare
529 * function or NULL when no element matched.
534 gst_iterator_find_custom (GstIterator * iter, GCompareFunc func,
538 GstIteratorResult res;
539 FindCustomFoldData data;
541 g_value_init (&ret, G_TYPE_POINTER);
543 data.user_data = user_data;
546 gst_iterator_fold (iter, (GstIteratorFoldFunction) find_custom_fold_func,
549 /* no need to unset, it's just a pointer */
550 return g_value_get_pointer (&ret);