gst/gstvalue.*: Saves the expensive lookup of the compare function in many cases...
[platform/upstream/gstreamer.git] / gst / gstvalue.c
1 /* GStreamer
2  * Copyright (C) <2003> David A. Schleef <ds@schleef.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:gstvalue
22  * @short_description: GValue implementations specific to GStreamer
23  *
24  * GValue implementations specific to GStreamer.
25  *
26  * Note that operations on the same GstValue (or GValue) from multiple
27  * threads may lead to undefined behaviour. 
28  *
29  * Last reviewed on 2006-03-07 (0.10.4)
30  */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35 #include <math.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39
40 #include "gst_private.h"
41 #include "glib-compat-private.h"
42 #include <gst/gst.h>
43 #include <gobject/gvaluecollector.h>
44
45 typedef struct _GstValueUnionInfo GstValueUnionInfo;
46 struct _GstValueUnionInfo
47 {
48   GType type1;
49   GType type2;
50   GstValueUnionFunc func;
51 };
52
53 typedef struct _GstValueIntersectInfo GstValueIntersectInfo;
54 struct _GstValueIntersectInfo
55 {
56   GType type1;
57   GType type2;
58   GstValueIntersectFunc func;
59 };
60
61 typedef struct _GstValueSubtractInfo GstValueSubtractInfo;
62 struct _GstValueSubtractInfo
63 {
64   GType minuend;
65   GType subtrahend;
66   GstValueSubtractFunc func;
67 };
68
69 GType gst_type_double_range;
70 GType gst_type_fraction_range;
71 GType gst_type_list;
72 GType gst_type_array;
73 GType gst_type_fraction;
74 GType gst_type_date;
75
76 static GArray *gst_value_table;
77 static GArray *gst_value_union_funcs;
78 static GArray *gst_value_intersect_funcs;
79 static GArray *gst_value_subtract_funcs;
80
81 /* Forward declarations */
82 static gint gst_greatest_common_divisor (gint a, gint b);
83 static gchar *gst_value_serialize_fraction (const GValue * value);
84
85 /********
86  * list *
87  ********/
88
89 /* two helper functions to serialize/stringify any type of list
90  * regular lists are done with { }, arrays with < >
91  */
92 static gchar *
93 gst_value_serialize_any_list (const GValue * value, const gchar * begin,
94     const gchar * end)
95 {
96   guint i;
97   GArray *array = value->data[0].v_pointer;
98   GString *s;
99   GValue *v;
100   gchar *s_val;
101
102   s = g_string_new (begin);
103   for (i = 0; i < array->len; i++) {
104     v = &g_array_index (array, GValue, i);
105     s_val = gst_value_serialize (v);
106     g_string_append (s, s_val);
107     g_free (s_val);
108     if (i < array->len - 1) {
109       g_string_append (s, ", ");
110     }
111   }
112   g_string_append (s, end);
113   return g_string_free (s, FALSE);
114 }
115
116 static void
117 gst_value_transform_any_list_string (const GValue * src_value,
118     GValue * dest_value, const gchar * begin, const gchar * end)
119 {
120   GValue *list_value;
121   GArray *array;
122   GString *s;
123   guint i;
124   gchar *list_s;
125
126   array = src_value->data[0].v_pointer;
127
128   s = g_string_new (begin);
129   for (i = 0; i < array->len; i++) {
130     list_value = &g_array_index (array, GValue, i);
131
132     if (i != 0) {
133       g_string_append (s, ", ");
134     }
135     list_s = g_strdup_value_contents (list_value);
136     g_string_append (s, list_s);
137     g_free (list_s);
138   }
139   g_string_append (s, end);
140
141   dest_value->data[0].v_pointer = g_string_free (s, FALSE);
142 }
143
144 /*
145  * helper function to see if a type is fixed. Is used internally here and
146  * there. Do not export, since it doesn't work for types where the content
147  * decides the fixedness (e.g. GST_TYPE_ARRAY).
148  */
149
150 static gboolean
151 gst_type_is_fixed (GType type)
152 {
153   if (type == GST_TYPE_INT_RANGE || type == GST_TYPE_DOUBLE_RANGE ||
154       type == GST_TYPE_LIST) {
155     return FALSE;
156   }
157   if (G_TYPE_FUNDAMENTAL (type) <=
158       G_TYPE_MAKE_FUNDAMENTAL (G_TYPE_RESERVED_GLIB_LAST)) {
159     return TRUE;
160   }
161   if (type == GST_TYPE_BUFFER || type == GST_TYPE_FOURCC
162       || type == GST_TYPE_ARRAY || type == GST_TYPE_FRACTION) {
163     return TRUE;
164   }
165
166   return FALSE;
167 }
168
169 /* GValue functions usable for both regular lists and arrays */
170 static void
171 gst_value_init_list_or_array (GValue * value)
172 {
173   value->data[0].v_pointer = g_array_new (FALSE, TRUE, sizeof (GValue));
174 }
175
176 static GArray *
177 copy_garray_of_gstvalue (const GArray * src)
178 {
179   GArray *dest;
180   guint i;
181
182   dest = g_array_sized_new (FALSE, TRUE, sizeof (GValue), src->len);
183   g_array_set_size (dest, src->len);
184   for (i = 0; i < src->len; i++) {
185     gst_value_init_and_copy (&g_array_index (dest, GValue, i),
186         &g_array_index (src, GValue, i));
187   }
188
189   return dest;
190 }
191
192 static void
193 gst_value_copy_list_or_array (const GValue * src_value, GValue * dest_value)
194 {
195   dest_value->data[0].v_pointer =
196       copy_garray_of_gstvalue ((GArray *) src_value->data[0].v_pointer);
197 }
198
199 static void
200 gst_value_free_list_or_array (GValue * value)
201 {
202   guint i;
203   GArray *src = (GArray *) value->data[0].v_pointer;
204
205   if ((value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS) == 0) {
206     for (i = 0; i < src->len; i++) {
207       g_value_unset (&g_array_index (src, GValue, i));
208     }
209     g_array_free (src, TRUE);
210   }
211 }
212
213 static gpointer
214 gst_value_list_or_array_peek_pointer (const GValue * value)
215 {
216   return value->data[0].v_pointer;
217 }
218
219 static gchar *
220 gst_value_collect_list_or_array (GValue * value, guint n_collect_values,
221     GTypeCValue * collect_values, guint collect_flags)
222 {
223   if (collect_flags & G_VALUE_NOCOPY_CONTENTS) {
224     value->data[0].v_pointer = collect_values[0].v_pointer;
225     value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
226   } else {
227     value->data[0].v_pointer =
228         copy_garray_of_gstvalue ((GArray *) collect_values[0].v_pointer);
229   }
230   return NULL;
231 }
232
233 static gchar *
234 gst_value_lcopy_list_or_array (const GValue * value, guint n_collect_values,
235     GTypeCValue * collect_values, guint collect_flags)
236 {
237   GArray **dest = collect_values[0].v_pointer;
238
239   if (!dest)
240     return g_strdup_printf ("value location for `%s' passed as NULL",
241         G_VALUE_TYPE_NAME (value));
242   if (!value->data[0].v_pointer)
243     return g_strdup_printf ("invalid value given for `%s'",
244         G_VALUE_TYPE_NAME (value));
245   if (collect_flags & G_VALUE_NOCOPY_CONTENTS) {
246     *dest = (GArray *) value->data[0].v_pointer;
247   } else {
248     *dest = copy_garray_of_gstvalue ((GArray *) value->data[0].v_pointer);
249   }
250   return NULL;
251 }
252
253 /**
254  * gst_value_list_append_value:
255  * @value: a #GValue of type #GST_TYPE_LIST
256  * @append_value: the value to append
257  *
258  * Appends @append_value to the GstValueList in @value.
259  */
260 void
261 gst_value_list_append_value (GValue * value, const GValue * append_value)
262 {
263   GValue val = { 0, };
264
265   g_return_if_fail (GST_VALUE_HOLDS_LIST (value));
266
267   gst_value_init_and_copy (&val, append_value);
268   g_array_append_vals ((GArray *) value->data[0].v_pointer, &val, 1);
269 }
270
271 /**
272  * gst_value_list_prepend_value:
273  * @value: a #GValue of type #GST_TYPE_LIST
274  * @prepend_value: the value to prepend
275  *
276  * Prepends @prepend_value to the GstValueList in @value.
277  */
278 void
279 gst_value_list_prepend_value (GValue * value, const GValue * prepend_value)
280 {
281   GValue val = { 0, };
282
283   g_return_if_fail (GST_VALUE_HOLDS_LIST (value));
284
285   gst_value_init_and_copy (&val, prepend_value);
286   g_array_prepend_vals ((GArray *) value->data[0].v_pointer, &val, 1);
287 }
288
289 /**
290  * gst_value_list_concat:
291  * @dest: an uninitialized #GValue to take the result
292  * @value1: a #GValue
293  * @value2: a #GValue
294  *
295  * Concatenates copies of @value1 and @value2 into a list.  Values that are not
296  * of type #GST_TYPE_LIST are treated as if they were lists of length 1.
297  * @dest will be initialized to the type #GST_TYPE_LIST.
298  */
299 void
300 gst_value_list_concat (GValue * dest, const GValue * value1,
301     const GValue * value2)
302 {
303   guint i, value1_length, value2_length;
304   GArray *array;
305
306   g_return_if_fail (dest != NULL);
307   g_return_if_fail (G_VALUE_TYPE (dest) == 0);
308   g_return_if_fail (G_IS_VALUE (value1));
309   g_return_if_fail (G_IS_VALUE (value2));
310
311   value1_length =
312       (GST_VALUE_HOLDS_LIST (value1) ? gst_value_list_get_size (value1) : 1);
313   value2_length =
314       (GST_VALUE_HOLDS_LIST (value2) ? gst_value_list_get_size (value2) : 1);
315   g_value_init (dest, GST_TYPE_LIST);
316   array = (GArray *) dest->data[0].v_pointer;
317   g_array_set_size (array, value1_length + value2_length);
318
319   if (GST_VALUE_HOLDS_LIST (value1)) {
320     for (i = 0; i < value1_length; i++) {
321       gst_value_init_and_copy (&g_array_index (array, GValue, i),
322           gst_value_list_get_value (value1, i));
323     }
324   } else {
325     gst_value_init_and_copy (&g_array_index (array, GValue, 0), value1);
326   }
327
328   if (GST_VALUE_HOLDS_LIST (value2)) {
329     for (i = 0; i < value2_length; i++) {
330       gst_value_init_and_copy (&g_array_index (array, GValue,
331               i + value1_length), gst_value_list_get_value (value2, i));
332     }
333   } else {
334     gst_value_init_and_copy (&g_array_index (array, GValue, value1_length),
335         value2);
336   }
337 }
338
339 /**
340  * gst_value_list_get_size:
341  * @value: a #GValue of type #GST_TYPE_LIST
342  *
343  * Gets the number of values contained in @value.
344  *
345  * Returns: the number of values
346  */
347 guint
348 gst_value_list_get_size (const GValue * value)
349 {
350   g_return_val_if_fail (GST_VALUE_HOLDS_LIST (value), 0);
351
352   return ((GArray *) value->data[0].v_pointer)->len;
353 }
354
355 /**
356  * gst_value_list_get_value:
357  * @value: a #GValue of type #GST_TYPE_LIST
358  * @index: index of value to get from the list
359  *
360  * Gets the value that is a member of the list contained in @value and
361  * has the index @index.
362  *
363  * Returns: the value at the given index
364  */
365 const GValue *
366 gst_value_list_get_value (const GValue * value, guint index)
367 {
368   g_return_val_if_fail (GST_VALUE_HOLDS_LIST (value), NULL);
369   g_return_val_if_fail (index < gst_value_list_get_size (value), NULL);
370
371   return (const GValue *) &g_array_index ((GArray *) value->data[0].v_pointer,
372       GValue, index);
373 }
374
375 /**
376  * gst_value_array_append_value:
377  * @value: a #GValue of type #GST_TYPE_ARRAY
378  * @append_value: the value to append
379  *
380  * Appends @append_value to the GstValueArray in @value.
381  */
382 void
383 gst_value_array_append_value (GValue * value, const GValue * append_value)
384 {
385   GValue val = { 0, };
386
387   g_return_if_fail (GST_VALUE_HOLDS_ARRAY (value));
388
389   gst_value_init_and_copy (&val, append_value);
390   g_array_append_vals ((GArray *) value->data[0].v_pointer, &val, 1);
391 }
392
393 /**
394  * gst_value_array_prepend_value:
395  * @value: a #GValue of type #GST_TYPE_ARRAY
396  * @prepend_value: the value to prepend
397  *
398  * Prepends @prepend_value to the GstValueArray in @value.
399  */
400 void
401 gst_value_array_prepend_value (GValue * value, const GValue * prepend_value)
402 {
403   GValue val = { 0, };
404
405   g_return_if_fail (GST_VALUE_HOLDS_ARRAY (value));
406
407   gst_value_init_and_copy (&val, prepend_value);
408   g_array_prepend_vals ((GArray *) value->data[0].v_pointer, &val, 1);
409 }
410
411 /**
412  * gst_value_array_get_size:
413  * @value: a #GValue of type #GST_TYPE_ARRAY
414  *
415  * Gets the number of values contained in @value.
416  *
417  * Returns: the number of values
418  */
419 guint
420 gst_value_array_get_size (const GValue * value)
421 {
422   g_return_val_if_fail (GST_VALUE_HOLDS_ARRAY (value), 0);
423
424   return ((GArray *) value->data[0].v_pointer)->len;
425 }
426
427 /**
428  * gst_value_array_get_value:
429  * @value: a #GValue of type #GST_TYPE_ARRAY
430  * @index: index of value to get from the array
431  *
432  * Gets the value that is a member of the array contained in @value and
433  * has the index @index.
434  *
435  * Returns: the value at the given index
436  */
437 const GValue *
438 gst_value_array_get_value (const GValue * value, guint index)
439 {
440   g_return_val_if_fail (GST_VALUE_HOLDS_ARRAY (value), NULL);
441   g_return_val_if_fail (index < gst_value_array_get_size (value), NULL);
442
443   return (const GValue *) &g_array_index ((GArray *) value->data[0].v_pointer,
444       GValue, index);
445 }
446
447 static void
448 gst_value_transform_list_string (const GValue * src_value, GValue * dest_value)
449 {
450   gst_value_transform_any_list_string (src_value, dest_value, "{ ", " }");
451 }
452
453 static void
454 gst_value_transform_array_string (const GValue * src_value, GValue * dest_value)
455 {
456   gst_value_transform_any_list_string (src_value, dest_value, "< ", " >");
457 }
458
459 /* Do an unordered compare of the contents of a list */
460 static int
461 gst_value_compare_list (const GValue * value1, const GValue * value2)
462 {
463   guint i, j;
464   GArray *array1 = value1->data[0].v_pointer;
465   GArray *array2 = value2->data[0].v_pointer;
466   GValue *v1;
467   GValue *v2;
468   gint len, to_remove;
469   guint8 *removed;
470   GstValueCompareFunc compare;
471
472   /* get length and do initial length check. */
473   len = array1->len;
474   if (len != array2->len)
475     return GST_VALUE_UNORDERED;
476
477   /* place to mark removed value indices of array2 */
478   removed = g_newa (guint8, len);
479   memset (removed, 0, len);
480   to_remove = len;
481
482   /* loop over array1, all items should be in array2. When we find an
483    * item in array2, remove it from array2 by marking it as removed */
484   for (i = 0; i < len; i++) {
485     v1 = &g_array_index (array1, GValue, i);
486     if ((compare = gst_value_get_compare_func (v1))) {
487       for (j = 0; j < len; j++) {
488         /* item is removed, we can skip it */
489         if (removed[j])
490           continue;
491         v2 = &g_array_index (array2, GValue, j);
492         if (gst_value_compare_with_func (v1, v2, compare) == GST_VALUE_EQUAL) {
493           /* mark item as removed now that we found it in array2 and 
494            * decrement the number of remaining items in array2. */
495           removed[j] = 1;
496           to_remove--;
497           break;
498         }
499       }
500       /* item in array1 and not in array2, UNORDERED */
501       if (j == len)
502         return GST_VALUE_UNORDERED;
503     } else
504       return GST_VALUE_UNORDERED;
505   }
506   /* if not all items were removed, array2 contained something not in array1 */
507   if (to_remove != 0)
508     return GST_VALUE_UNORDERED;
509
510   /* arrays are equal */
511   return GST_VALUE_EQUAL;
512 }
513
514 /* Perform an ordered comparison of the contents of an array */
515 static int
516 gst_value_compare_array (const GValue * value1, const GValue * value2)
517 {
518   guint i;
519   GArray *array1 = value1->data[0].v_pointer;
520   GArray *array2 = value2->data[0].v_pointer;
521   GValue *v1;
522   GValue *v2;
523
524   if (array1->len != array2->len)
525     return GST_VALUE_UNORDERED;
526
527   for (i = 0; i < array1->len; i++) {
528     v1 = &g_array_index (array1, GValue, i);
529     v2 = &g_array_index (array2, GValue, i);
530     if (gst_value_compare (v1, v2) != GST_VALUE_EQUAL)
531       return GST_VALUE_UNORDERED;
532   }
533
534   return GST_VALUE_EQUAL;
535 }
536
537 static gchar *
538 gst_value_serialize_list (const GValue * value)
539 {
540   return gst_value_serialize_any_list (value, "{ ", " }");
541 }
542
543 static gboolean
544 gst_value_deserialize_list (GValue * dest, const gchar * s)
545 {
546   g_warning ("unimplemented");
547   return FALSE;
548 }
549
550 static gchar *
551 gst_value_serialize_array (const GValue * value)
552 {
553   return gst_value_serialize_any_list (value, "< ", " >");
554 }
555
556 static gboolean
557 gst_value_deserialize_array (GValue * dest, const gchar * s)
558 {
559   g_warning ("unimplemented");
560   return FALSE;
561 }
562
563 /**********
564  * fourcc *
565  **********/
566
567 static void
568 gst_value_init_fourcc (GValue * value)
569 {
570   value->data[0].v_int = 0;
571 }
572
573 static void
574 gst_value_copy_fourcc (const GValue * src_value, GValue * dest_value)
575 {
576   dest_value->data[0].v_int = src_value->data[0].v_int;
577 }
578
579 static gchar *
580 gst_value_collect_fourcc (GValue * value, guint n_collect_values,
581     GTypeCValue * collect_values, guint collect_flags)
582 {
583   value->data[0].v_int = collect_values[0].v_int;
584
585   return NULL;
586 }
587
588 static gchar *
589 gst_value_lcopy_fourcc (const GValue * value, guint n_collect_values,
590     GTypeCValue * collect_values, guint collect_flags)
591 {
592   guint32 *fourcc_p = collect_values[0].v_pointer;
593
594   if (!fourcc_p)
595     return g_strdup_printf ("value location for `%s' passed as NULL",
596         G_VALUE_TYPE_NAME (value));
597
598   *fourcc_p = value->data[0].v_int;
599
600   return NULL;
601 }
602
603 /**
604  * gst_value_set_fourcc:
605  * @value: a GValue initialized to #GST_TYPE_FOURCC
606  * @fourcc: the #guint32 fourcc to set
607  *
608  * Sets @value to @fourcc.
609  */
610 void
611 gst_value_set_fourcc (GValue * value, guint32 fourcc)
612 {
613   g_return_if_fail (GST_VALUE_HOLDS_FOURCC (value));
614
615   value->data[0].v_int = fourcc;
616 }
617
618 /**
619  * gst_value_get_fourcc:
620  * @value: a GValue initialized to #GST_TYPE_FOURCC
621  *
622  * Gets the #guint32 fourcc contained in @value.
623  *
624  * Returns: the #guint32 fourcc contained in @value.
625  */
626 guint32
627 gst_value_get_fourcc (const GValue * value)
628 {
629   g_return_val_if_fail (GST_VALUE_HOLDS_FOURCC (value), 0);
630
631   return value->data[0].v_int;
632 }
633
634 static void
635 gst_value_transform_fourcc_string (const GValue * src_value,
636     GValue * dest_value)
637 {
638   guint32 fourcc = src_value->data[0].v_int;
639
640   if (g_ascii_isprint ((fourcc >> 0) & 0xff) &&
641       g_ascii_isprint ((fourcc >> 8) & 0xff) &&
642       g_ascii_isprint ((fourcc >> 16) & 0xff) &&
643       g_ascii_isprint ((fourcc >> 24) & 0xff)) {
644     dest_value->data[0].v_pointer =
645         g_strdup_printf ("%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
646   } else {
647     dest_value->data[0].v_pointer = g_strdup_printf ("0x%08x", fourcc);
648   }
649 }
650
651 static gint
652 gst_value_compare_fourcc (const GValue * value1, const GValue * value2)
653 {
654   if (value2->data[0].v_int == value1->data[0].v_int)
655     return GST_VALUE_EQUAL;
656   return GST_VALUE_UNORDERED;
657 }
658
659 static gchar *
660 gst_value_serialize_fourcc (const GValue * value)
661 {
662   guint32 fourcc = value->data[0].v_int;
663
664   if (g_ascii_isalnum ((fourcc >> 0) & 0xff) &&
665       g_ascii_isalnum ((fourcc >> 8) & 0xff) &&
666       g_ascii_isalnum ((fourcc >> 16) & 0xff) &&
667       g_ascii_isalnum ((fourcc >> 24) & 0xff)) {
668     return g_strdup_printf ("%" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
669   } else {
670     return g_strdup_printf ("0x%08x", fourcc);
671   }
672 }
673
674 static gboolean
675 gst_value_deserialize_fourcc (GValue * dest, const char *s)
676 {
677   gboolean ret = FALSE;
678   guint32 fourcc = 0;
679   char *end;
680
681   if (strlen (s) == 4) {
682     fourcc = GST_MAKE_FOURCC (s[0], s[1], s[2], s[3]);
683     ret = TRUE;
684   } else if (g_ascii_isdigit (*s)) {
685     fourcc = strtoul (s, &end, 0);
686     if (*end == 0) {
687       ret = TRUE;
688     }
689   }
690   gst_value_set_fourcc (dest, fourcc);
691
692   return ret;
693 }
694
695 /*************
696  * int range *
697  *************/
698
699 static void
700 gst_value_init_int_range (GValue * value)
701 {
702   value->data[0].v_int = 0;
703   value->data[1].v_int = 0;
704 }
705
706 static void
707 gst_value_copy_int_range (const GValue * src_value, GValue * dest_value)
708 {
709   dest_value->data[0].v_int = src_value->data[0].v_int;
710   dest_value->data[1].v_int = src_value->data[1].v_int;
711 }
712
713 static gchar *
714 gst_value_collect_int_range (GValue * value, guint n_collect_values,
715     GTypeCValue * collect_values, guint collect_flags)
716 {
717   value->data[0].v_int = collect_values[0].v_int;
718   value->data[1].v_int = collect_values[1].v_int;
719
720   return NULL;
721 }
722
723 static gchar *
724 gst_value_lcopy_int_range (const GValue * value, guint n_collect_values,
725     GTypeCValue * collect_values, guint collect_flags)
726 {
727   guint32 *int_range_start = collect_values[0].v_pointer;
728   guint32 *int_range_end = collect_values[1].v_pointer;
729
730   if (!int_range_start)
731     return g_strdup_printf ("start value location for `%s' passed as NULL",
732         G_VALUE_TYPE_NAME (value));
733   if (!int_range_end)
734     return g_strdup_printf ("end value location for `%s' passed as NULL",
735         G_VALUE_TYPE_NAME (value));
736
737   *int_range_start = value->data[0].v_int;
738   *int_range_end = value->data[1].v_int;
739
740   return NULL;
741 }
742
743 /**
744  * gst_value_set_int_range:
745  * @value: a GValue initialized to GST_TYPE_INT_RANGE
746  * @start: the start of the range
747  * @end: the end of the range
748  *
749  * Sets @value to the range specified by @start and @end.
750  */
751 void
752 gst_value_set_int_range (GValue * value, gint start, gint end)
753 {
754   g_return_if_fail (GST_VALUE_HOLDS_INT_RANGE (value));
755   g_return_if_fail (start < end);
756
757   value->data[0].v_int = start;
758   value->data[1].v_int = end;
759 }
760
761 /**
762  * gst_value_get_int_range_min:
763  * @value: a GValue initialized to GST_TYPE_INT_RANGE
764  *
765  * Gets the minimum of the range specified by @value.
766  *
767  * Returns: the minimum of the range
768  */
769 gint
770 gst_value_get_int_range_min (const GValue * value)
771 {
772   g_return_val_if_fail (GST_VALUE_HOLDS_INT_RANGE (value), 0);
773
774   return value->data[0].v_int;
775 }
776
777 /**
778  * gst_value_get_int_range_max:
779  * @value: a GValue initialized to GST_TYPE_INT_RANGE
780  *
781  * Gets the maximum of the range specified by @value.
782  *
783  * Returns: the maxumum of the range
784  */
785 gint
786 gst_value_get_int_range_max (const GValue * value)
787 {
788   g_return_val_if_fail (GST_VALUE_HOLDS_INT_RANGE (value), 0);
789
790   return value->data[1].v_int;
791 }
792
793 static void
794 gst_value_transform_int_range_string (const GValue * src_value,
795     GValue * dest_value)
796 {
797   dest_value->data[0].v_pointer = g_strdup_printf ("[%d,%d]",
798       (int) src_value->data[0].v_int, (int) src_value->data[1].v_int);
799 }
800
801 static gint
802 gst_value_compare_int_range (const GValue * value1, const GValue * value2)
803 {
804   if (value2->data[0].v_int == value1->data[0].v_int &&
805       value2->data[1].v_int == value1->data[1].v_int)
806     return GST_VALUE_EQUAL;
807   return GST_VALUE_UNORDERED;
808 }
809
810 static gchar *
811 gst_value_serialize_int_range (const GValue * value)
812 {
813   return g_strdup_printf ("[ %d, %d ]", value->data[0].v_int,
814       value->data[1].v_int);
815 }
816
817 static gboolean
818 gst_value_deserialize_int_range (GValue * dest, const gchar * s)
819 {
820   g_warning ("unimplemented");
821   return FALSE;
822 }
823
824 /****************
825  * double range *
826  ****************/
827
828 static void
829 gst_value_init_double_range (GValue * value)
830 {
831   value->data[0].v_double = 0;
832   value->data[1].v_double = 0;
833 }
834
835 static void
836 gst_value_copy_double_range (const GValue * src_value, GValue * dest_value)
837 {
838   dest_value->data[0].v_double = src_value->data[0].v_double;
839   dest_value->data[1].v_double = src_value->data[1].v_double;
840 }
841
842 static gchar *
843 gst_value_collect_double_range (GValue * value, guint n_collect_values,
844     GTypeCValue * collect_values, guint collect_flags)
845 {
846   value->data[0].v_double = collect_values[0].v_double;
847   value->data[1].v_double = collect_values[1].v_double;
848
849   return NULL;
850 }
851
852 static gchar *
853 gst_value_lcopy_double_range (const GValue * value, guint n_collect_values,
854     GTypeCValue * collect_values, guint collect_flags)
855 {
856   gdouble *double_range_start = collect_values[0].v_pointer;
857   gdouble *double_range_end = collect_values[1].v_pointer;
858
859   if (!double_range_start)
860     return g_strdup_printf ("start value location for `%s' passed as NULL",
861         G_VALUE_TYPE_NAME (value));
862   if (!double_range_end)
863     return g_strdup_printf ("end value location for `%s' passed as NULL",
864         G_VALUE_TYPE_NAME (value));
865
866   *double_range_start = value->data[0].v_double;
867   *double_range_end = value->data[1].v_double;
868
869   return NULL;
870 }
871
872 /**
873  * gst_value_set_double_range:
874  * @value: a GValue initialized to GST_TYPE_DOUBLE_RANGE
875  * @start: the start of the range
876  * @end: the end of the range
877  *
878  * Sets @value to the range specified by @start and @end.
879  */
880 void
881 gst_value_set_double_range (GValue * value, gdouble start, gdouble end)
882 {
883   g_return_if_fail (GST_VALUE_HOLDS_DOUBLE_RANGE (value));
884
885   value->data[0].v_double = start;
886   value->data[1].v_double = end;
887 }
888
889 /**
890  * gst_value_get_double_range_min:
891  * @value: a GValue initialized to GST_TYPE_DOUBLE_RANGE
892  *
893  * Gets the minimum of the range specified by @value.
894  *
895  * Returns: the minumum of the range
896  */
897 gdouble
898 gst_value_get_double_range_min (const GValue * value)
899 {
900   g_return_val_if_fail (GST_VALUE_HOLDS_DOUBLE_RANGE (value), 0);
901
902   return value->data[0].v_double;
903 }
904
905 /**
906  * gst_value_get_double_range_max:
907  * @value: a GValue initialized to GST_TYPE_DOUBLE_RANGE
908  *
909  * Gets the maximum of the range specified by @value.
910  *
911  * Returns: the maxumum of the range
912  */
913 gdouble
914 gst_value_get_double_range_max (const GValue * value)
915 {
916   g_return_val_if_fail (GST_VALUE_HOLDS_DOUBLE_RANGE (value), 0);
917
918   return value->data[1].v_double;
919 }
920
921 static void
922 gst_value_transform_double_range_string (const GValue * src_value,
923     GValue * dest_value)
924 {
925   char s1[G_ASCII_DTOSTR_BUF_SIZE], s2[G_ASCII_DTOSTR_BUF_SIZE];
926
927   dest_value->data[0].v_pointer = g_strdup_printf ("[%s,%s]",
928       g_ascii_dtostr (s1, G_ASCII_DTOSTR_BUF_SIZE,
929           src_value->data[0].v_double),
930       g_ascii_dtostr (s2, G_ASCII_DTOSTR_BUF_SIZE,
931           src_value->data[1].v_double));
932 }
933
934 static gint
935 gst_value_compare_double_range (const GValue * value1, const GValue * value2)
936 {
937   if (value2->data[0].v_double == value1->data[0].v_double &&
938       value2->data[0].v_double == value1->data[0].v_double)
939     return GST_VALUE_EQUAL;
940   return GST_VALUE_UNORDERED;
941 }
942
943 static gchar *
944 gst_value_serialize_double_range (const GValue * value)
945 {
946   char d1[G_ASCII_DTOSTR_BUF_SIZE];
947   char d2[G_ASCII_DTOSTR_BUF_SIZE];
948
949   g_ascii_dtostr (d1, G_ASCII_DTOSTR_BUF_SIZE, value->data[0].v_double);
950   g_ascii_dtostr (d2, G_ASCII_DTOSTR_BUF_SIZE, value->data[1].v_double);
951   return g_strdup_printf ("[ %s, %s ]", d1, d2);
952 }
953
954 static gboolean
955 gst_value_deserialize_double_range (GValue * dest, const gchar * s)
956 {
957   g_warning ("unimplemented");
958   return FALSE;
959 }
960
961 /****************
962  * fraction range *
963  ****************/
964
965 static void
966 gst_value_init_fraction_range (GValue * value)
967 {
968   GValue *vals;
969
970   value->data[0].v_pointer = vals = g_new0 (GValue, 2);
971   g_value_init (&vals[0], GST_TYPE_FRACTION);
972   g_value_init (&vals[1], GST_TYPE_FRACTION);
973 }
974
975 static void
976 gst_value_free_fraction_range (GValue * value)
977 {
978   GValue *vals = (GValue *) value->data[0].v_pointer;
979
980   if (vals != NULL) {
981     g_value_unset (&vals[0]);
982     g_value_unset (&vals[1]);
983     g_free (vals);
984     value->data[0].v_pointer = NULL;
985   }
986 }
987
988 static void
989 gst_value_copy_fraction_range (const GValue * src_value, GValue * dest_value)
990 {
991   GValue *vals = (GValue *) dest_value->data[0].v_pointer;
992   GValue *src_vals = (GValue *) src_value->data[0].v_pointer;
993
994   if (vals == NULL) {
995     dest_value->data[0].v_pointer = vals = g_new0 (GValue, 2);
996     g_return_if_fail (vals != NULL);
997     g_value_init (&vals[0], GST_TYPE_FRACTION);
998     g_value_init (&vals[1], GST_TYPE_FRACTION);
999   }
1000
1001   if (src_vals != NULL) {
1002     g_value_copy (&src_vals[0], &vals[0]);
1003     g_value_copy (&src_vals[1], &vals[1]);
1004   }
1005 }
1006
1007 static gchar *
1008 gst_value_collect_fraction_range (GValue * value, guint n_collect_values,
1009     GTypeCValue * collect_values, guint collect_flags)
1010 {
1011   GValue *vals = (GValue *) value->data[0].v_pointer;
1012
1013   if (n_collect_values != 4)
1014     return g_strdup_printf ("not enough value locations for `%s' passed",
1015         G_VALUE_TYPE_NAME (value));
1016   if (vals == NULL) {
1017     value->data[0].v_pointer = vals = g_new0 (GValue, 2);
1018     if (vals == NULL)
1019       return g_strdup_printf ("Could not initialise`%s' during collect",
1020           G_VALUE_TYPE_NAME (value));
1021     g_value_init (&vals[0], GST_TYPE_FRACTION);
1022     g_value_init (&vals[1], GST_TYPE_FRACTION);
1023   }
1024
1025   gst_value_set_fraction (&vals[0], collect_values[0].v_int,
1026       collect_values[1].v_int);
1027   gst_value_set_fraction (&vals[1], collect_values[2].v_int,
1028       collect_values[3].v_int);
1029
1030   return NULL;
1031 }
1032
1033 static gchar *
1034 gst_value_lcopy_fraction_range (const GValue * value, guint n_collect_values,
1035     GTypeCValue * collect_values, guint collect_flags)
1036 {
1037   int i;
1038   int *dest_values[4];
1039   GValue *vals = (GValue *) value->data[0].v_pointer;
1040
1041   if (n_collect_values != 4)
1042     return g_strdup_printf ("not enough value locations for `%s' passed",
1043         G_VALUE_TYPE_NAME (value));
1044
1045   for (i = 0; i < 4; i++) {
1046     if (collect_values[i].v_pointer == NULL) {
1047       return g_strdup_printf ("value location for `%s' passed as NULL",
1048           G_VALUE_TYPE_NAME (value));
1049     }
1050     dest_values[i] = collect_values[i].v_pointer;
1051   }
1052
1053   if (vals == NULL) {
1054     return g_strdup_printf ("Uninitialised `%s' passed",
1055         G_VALUE_TYPE_NAME (value));
1056   }
1057
1058   dest_values[0][0] = gst_value_get_fraction_numerator (&vals[0]);
1059   dest_values[1][0] = gst_value_get_fraction_denominator (&vals[0]);
1060   dest_values[2][0] = gst_value_get_fraction_denominator (&vals[1]);
1061   dest_values[3][0] = gst_value_get_fraction_denominator (&vals[1]);
1062   return NULL;
1063 }
1064
1065 /**
1066  * gst_value_set_fraction_range:
1067  * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE
1068  * @start: the start of the range (a GST_TYPE_FRACTION GValue)
1069  * @end: the end of the range (a GST_TYPE_FRACTION GValue)
1070  *
1071  * Sets @value to the range specified by @start and @end.
1072  */
1073 void
1074 gst_value_set_fraction_range (GValue * value, const GValue * start,
1075     const GValue * end)
1076 {
1077   GValue *vals;
1078
1079   g_return_if_fail (GST_VALUE_HOLDS_FRACTION_RANGE (value));
1080
1081   vals = (GValue *) value->data[0].v_pointer;
1082   if (vals == NULL) {
1083     value->data[0].v_pointer = vals = g_new0 (GValue, 2);
1084     g_value_init (&vals[0], GST_TYPE_FRACTION);
1085     g_value_init (&vals[1], GST_TYPE_FRACTION);
1086   }
1087
1088   g_value_copy (start, &vals[0]);
1089   g_value_copy (end, &vals[1]);
1090 }
1091
1092 /**
1093  * gst_value_set_fraction_range_full:
1094  * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE
1095  * @numerator_start: the numerator start of the range
1096  * @denominator_start: the denominator start of the range
1097  * @numerator_end: the numerator end of the range
1098  * @denominator_end: the denominator end of the range
1099  *
1100  * Sets @value to the range specified by @numerator_start/@denominator_start
1101  * and @numerator_end/@denominator_end.
1102  */
1103 void
1104 gst_value_set_fraction_range_full (GValue * value,
1105     gint numerator_start, gint denominator_start,
1106     gint numerator_end, gint denominator_end)
1107 {
1108   GValue start = { 0 };
1109   GValue end = { 0 };
1110
1111   g_value_init (&start, GST_TYPE_FRACTION);
1112   g_value_init (&end, GST_TYPE_FRACTION);
1113
1114   gst_value_set_fraction (&start, numerator_start, denominator_start);
1115   gst_value_set_fraction (&end, numerator_end, denominator_end);
1116   gst_value_set_fraction_range (value, &start, &end);
1117
1118   g_value_unset (&start);
1119   g_value_unset (&end);
1120 }
1121
1122 /**
1123  * gst_value_get_fraction_range_min:
1124  * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE
1125  *
1126  * Gets the minimum of the range specified by @value.
1127  *
1128  * Returns: the minumum of the range
1129  */
1130 const GValue *
1131 gst_value_get_fraction_range_min (const GValue * value)
1132 {
1133   GValue *vals;
1134
1135   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION_RANGE (value), FALSE);
1136
1137   vals = (GValue *) value->data[0].v_pointer;
1138   if (vals != NULL) {
1139     return &vals[0];
1140   }
1141
1142   return NULL;
1143 }
1144
1145 /**
1146  * gst_value_get_fraction_range_max:
1147  * @value: a GValue initialized to GST_TYPE_FRACTION_RANGE
1148  *
1149  * Gets the maximum of the range specified by @value.
1150  *
1151  * Returns: the maximum of the range
1152  */
1153 const GValue *
1154 gst_value_get_fraction_range_max (const GValue * value)
1155 {
1156   GValue *vals;
1157
1158   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION_RANGE (value), FALSE);
1159
1160   vals = (GValue *) value->data[0].v_pointer;
1161   if (vals != NULL) {
1162     return &vals[1];
1163   }
1164
1165   return NULL;
1166 }
1167
1168 static char *
1169 gst_value_serialize_fraction_range (const GValue * value)
1170 {
1171   GValue *vals = (GValue *) value->data[0].v_pointer;
1172   gchar *retval;
1173
1174   if (vals == NULL) {
1175     retval = g_strdup ("[ 0/1, 0/1 ]");
1176   } else {
1177     gchar *start, *end;
1178
1179     start = gst_value_serialize_fraction (&vals[0]);
1180     end = gst_value_serialize_fraction (&vals[1]);
1181
1182     retval = g_strdup_printf ("[ %s, %s ]", start, end);
1183     g_free (start);
1184     g_free (end);
1185   }
1186
1187   return retval;
1188 }
1189
1190 static void
1191 gst_value_transform_fraction_range_string (const GValue * src_value,
1192     GValue * dest_value)
1193 {
1194   dest_value->data[0].v_pointer =
1195       gst_value_serialize_fraction_range (src_value);
1196 }
1197
1198 static gint
1199 gst_value_compare_fraction_range (const GValue * value1, const GValue * value2)
1200 {
1201   GValue *vals1, *vals2;
1202   GstValueCompareFunc compare;
1203
1204   if (value2->data[0].v_pointer == value1->data[0].v_pointer)
1205     return GST_VALUE_EQUAL;     /* Only possible if both are NULL */
1206
1207   if (value2->data[0].v_pointer == NULL || value1->data[0].v_pointer == NULL)
1208     return GST_VALUE_UNORDERED;
1209
1210   vals1 = (GValue *) value1->data[0].v_pointer;
1211   vals2 = (GValue *) value2->data[0].v_pointer;
1212   if ((compare = gst_value_get_compare_func (&vals1[0]))) {
1213     if (gst_value_compare_with_func (&vals1[0], &vals2[0], compare) ==
1214         GST_VALUE_EQUAL &&
1215         gst_value_compare_with_func (&vals1[1], &vals2[1], compare) ==
1216         GST_VALUE_EQUAL)
1217       return GST_VALUE_EQUAL;
1218   }
1219   return GST_VALUE_UNORDERED;
1220 }
1221
1222 static gboolean
1223 gst_value_deserialize_fraction_range (GValue * dest, const gchar * s)
1224 {
1225   g_warning ("unimplemented");
1226   return FALSE;
1227 }
1228
1229 /***********
1230  * GstCaps *
1231  ***********/
1232
1233 /**
1234  * gst_value_set_caps:
1235  * @value: a GValue initialized to GST_TYPE_CAPS
1236  * @caps: the caps to set the value to
1237  *
1238  * Sets the contents of @value to coorespond to @caps.  The actual
1239  * #GstCaps structure is copied before it is used.
1240  */
1241 void
1242 gst_value_set_caps (GValue * value, const GstCaps * caps)
1243 {
1244   g_return_if_fail (G_VALUE_TYPE (value) == GST_TYPE_CAPS);
1245
1246   g_value_set_boxed (value, caps);
1247 }
1248
1249 /**
1250  * gst_value_get_caps:
1251  * @value: a GValue initialized to GST_TYPE_CAPS
1252  *
1253  * Gets the contents of @value.
1254  *
1255  * Returns: the contents of @value
1256  */
1257 const GstCaps *
1258 gst_value_get_caps (const GValue * value)
1259 {
1260   g_return_val_if_fail (G_VALUE_TYPE (value) == GST_TYPE_CAPS, NULL);
1261
1262   return (GstCaps *) g_value_get_boxed (value);
1263 }
1264
1265 static char *
1266 gst_value_serialize_caps (const GValue * value)
1267 {
1268   GstCaps *caps = g_value_get_boxed (value);
1269
1270   return gst_caps_to_string (caps);
1271 }
1272
1273 static gboolean
1274 gst_value_deserialize_caps (GValue * dest, const gchar * s)
1275 {
1276   GstCaps *caps;
1277
1278   caps = gst_caps_from_string (s);
1279
1280   if (caps) {
1281     g_value_set_boxed (dest, caps);
1282     return TRUE;
1283   }
1284   return FALSE;
1285 }
1286
1287
1288 /*************
1289  * GstBuffer *
1290  *************/
1291
1292 static int
1293 gst_value_compare_buffer (const GValue * value1, const GValue * value2)
1294 {
1295   GstBuffer *buf1 = GST_BUFFER (gst_value_get_mini_object (value1));
1296   GstBuffer *buf2 = GST_BUFFER (gst_value_get_mini_object (value2));
1297
1298   if (GST_BUFFER_SIZE (buf1) != GST_BUFFER_SIZE (buf2))
1299     return GST_VALUE_UNORDERED;
1300   if (GST_BUFFER_SIZE (buf1) == 0)
1301     return GST_VALUE_EQUAL;
1302   g_assert (GST_BUFFER_DATA (buf1));
1303   g_assert (GST_BUFFER_DATA (buf2));
1304   if (memcmp (GST_BUFFER_DATA (buf1), GST_BUFFER_DATA (buf2),
1305           GST_BUFFER_SIZE (buf1)) == 0)
1306     return GST_VALUE_EQUAL;
1307
1308   return GST_VALUE_UNORDERED;
1309 }
1310
1311 static char *
1312 gst_value_serialize_buffer (const GValue * value)
1313 {
1314   guint8 *data;
1315   int i;
1316   int size;
1317   char *string;
1318   GstBuffer *buffer;
1319
1320   buffer = gst_value_get_buffer (value);
1321   if (buffer == NULL)
1322     return NULL;
1323
1324   data = GST_BUFFER_DATA (buffer);
1325   size = GST_BUFFER_SIZE (buffer);
1326
1327   string = g_malloc (size * 2 + 1);
1328   for (i = 0; i < size; i++) {
1329     sprintf (string + i * 2, "%02x", data[i]);
1330   }
1331   string[size * 2] = 0;
1332
1333   return string;
1334 }
1335
1336 static gboolean
1337 gst_value_deserialize_buffer (GValue * dest, const gchar * s)
1338 {
1339   GstBuffer *buffer;
1340   int len;
1341   char ts[3];
1342   guint8 *data;
1343   int i;
1344
1345   len = strlen (s);
1346   if (len & 1)
1347     goto wrong_length;
1348
1349   buffer = gst_buffer_new_and_alloc (len / 2);
1350   data = GST_BUFFER_DATA (buffer);
1351   for (i = 0; i < len / 2; i++) {
1352     if (!isxdigit ((int) s[i * 2]) || !isxdigit ((int) s[i * 2 + 1]))
1353       goto wrong_char;
1354
1355     ts[0] = s[i * 2 + 0];
1356     ts[1] = s[i * 2 + 1];
1357     ts[2] = 0;
1358
1359     data[i] = (guint8) strtoul (ts, NULL, 16);
1360   }
1361
1362   gst_value_take_buffer (dest, buffer);
1363
1364   return TRUE;
1365
1366   /* ERRORS */
1367 wrong_length:
1368   {
1369     return FALSE;
1370   }
1371 wrong_char:
1372   {
1373     gst_buffer_unref (buffer);
1374     return FALSE;
1375   }
1376 }
1377
1378
1379 /***********
1380  * boolean *
1381  ***********/
1382
1383 static int
1384 gst_value_compare_boolean (const GValue * value1, const GValue * value2)
1385 {
1386   if ((value1->data[0].v_int != 0) == (value2->data[0].v_int != 0))
1387     return GST_VALUE_EQUAL;
1388   return GST_VALUE_UNORDERED;
1389 }
1390
1391 static char *
1392 gst_value_serialize_boolean (const GValue * value)
1393 {
1394   if (value->data[0].v_int) {
1395     return g_strdup ("true");
1396   }
1397   return g_strdup ("false");
1398 }
1399
1400 static gboolean
1401 gst_value_deserialize_boolean (GValue * dest, const gchar * s)
1402 {
1403   gboolean ret = FALSE;
1404
1405   if (g_ascii_strcasecmp (s, "true") == 0 ||
1406       g_ascii_strcasecmp (s, "yes") == 0 ||
1407       g_ascii_strcasecmp (s, "t") == 0 || strcmp (s, "1") == 0) {
1408     g_value_set_boolean (dest, TRUE);
1409     ret = TRUE;
1410   } else if (g_ascii_strcasecmp (s, "false") == 0 ||
1411       g_ascii_strcasecmp (s, "no") == 0 ||
1412       g_ascii_strcasecmp (s, "f") == 0 || strcmp (s, "0") == 0) {
1413     g_value_set_boolean (dest, FALSE);
1414     ret = TRUE;
1415   }
1416
1417   return ret;
1418 }
1419
1420 #define CREATE_SERIALIZATION_START(_type,_macro)                        \
1421 static gint                                                             \
1422 gst_value_compare_ ## _type                                             \
1423 (const GValue * value1, const GValue * value2)                          \
1424 {                                                                       \
1425   g ## _type val1 = g_value_get_ ## _type (value1);                     \
1426   g ## _type val2 = g_value_get_ ## _type (value2);                     \
1427   if (val1 > val2)                                                      \
1428     return GST_VALUE_GREATER_THAN;                                      \
1429   if (val1 < val2)                                                      \
1430     return GST_VALUE_LESS_THAN;                                         \
1431   return GST_VALUE_EQUAL;                                               \
1432 }                                                                       \
1433                                                                         \
1434 static char *                                                           \
1435 gst_value_serialize_ ## _type (const GValue * value)                    \
1436 {                                                                       \
1437   GValue val = { 0, };                                                  \
1438   g_value_init (&val, G_TYPE_STRING);                                   \
1439   if (!g_value_transform (value, &val))                                 \
1440     g_assert_not_reached ();                                            \
1441   /* NO_COPY_MADNESS!!! */                                              \
1442   return (char *) g_value_get_string (&val);                            \
1443 }
1444
1445 /* deserialize the given s into to as a gint64.
1446  * check if the result is actually storeable in the given size number of
1447  * bytes.
1448  */
1449 static gboolean
1450 gst_value_deserialize_int_helper (gint64 * to, const gchar * s,
1451     gint64 min, gint64 max, gint size)
1452 {
1453   gboolean ret = FALSE;
1454   char *end;
1455   gint64 mask = -1;
1456
1457   errno = 0;
1458   *to = g_ascii_strtoull (s, &end, 0);
1459   /* a range error is a definitive no-no */
1460   if (errno == ERANGE) {
1461     return FALSE;
1462   }
1463
1464   if (*end == 0) {
1465     ret = TRUE;
1466   } else {
1467     if (g_ascii_strcasecmp (s, "little_endian") == 0) {
1468       *to = G_LITTLE_ENDIAN;
1469       ret = TRUE;
1470     } else if (g_ascii_strcasecmp (s, "big_endian") == 0) {
1471       *to = G_BIG_ENDIAN;
1472       ret = TRUE;
1473     } else if (g_ascii_strcasecmp (s, "byte_order") == 0) {
1474       *to = G_BYTE_ORDER;
1475       ret = TRUE;
1476     } else if (g_ascii_strcasecmp (s, "min") == 0) {
1477       *to = min;
1478       ret = TRUE;
1479     } else if (g_ascii_strcasecmp (s, "max") == 0) {
1480       *to = max;
1481       ret = TRUE;
1482     }
1483   }
1484   if (ret) {
1485     /* by definition, a gint64 fits into a gint64; so ignore those */
1486     if (size != sizeof (mask)) {
1487       if (*to >= 0) {
1488         /* for positive numbers, we create a mask of 1's outside of the range
1489          * and 0's inside the range.  An and will thus keep only 1 bits
1490          * outside of the range */
1491         mask <<= (size * 8);
1492         if ((mask & *to) != 0) {
1493           ret = FALSE;
1494         }
1495       } else {
1496         /* for negative numbers, we do a 2's complement version */
1497         mask <<= ((size * 8) - 1);
1498         if ((mask & *to) != mask) {
1499           ret = FALSE;
1500         }
1501       }
1502     }
1503   }
1504   return ret;
1505 }
1506
1507 #define CREATE_SERIALIZATION(_type,_macro)                              \
1508 CREATE_SERIALIZATION_START(_type,_macro)                                \
1509                                                                         \
1510 static gboolean                                                         \
1511 gst_value_deserialize_ ## _type (GValue * dest, const gchar *s)         \
1512 {                                                                       \
1513   gint64 x;                                                             \
1514                                                                         \
1515   if (gst_value_deserialize_int_helper (&x, s, G_MIN ## _macro,         \
1516       G_MAX ## _macro, sizeof (g ## _type))) {                          \
1517     g_value_set_ ## _type (dest, /*(g ## _type)*/ x);                   \
1518     return TRUE;                                                        \
1519   } else {                                                              \
1520     return FALSE;                                                       \
1521   }                                                                     \
1522 }
1523
1524 #define CREATE_USERIALIZATION(_type,_macro)                             \
1525 CREATE_SERIALIZATION_START(_type,_macro)                                \
1526                                                                         \
1527 static gboolean                                                         \
1528 gst_value_deserialize_ ## _type (GValue * dest, const gchar *s)         \
1529 {                                                                       \
1530   gint64 x;                                                             \
1531   char *end;                                                            \
1532   gboolean ret = FALSE;                                                 \
1533                                                                         \
1534   errno = 0;                                                            \
1535   x = g_ascii_strtoull (s, &end, 0);                                    \
1536   /* a range error is a definitive no-no */                             \
1537   if (errno == ERANGE) {                                                \
1538     return FALSE;                                                       \
1539   }                                                                     \
1540   /* the cast ensures the range check later on makes sense */           \
1541   x = (g ## _type) x;                                                   \
1542   if (*end == 0) {                                                      \
1543     ret = TRUE;                                                         \
1544   } else {                                                              \
1545     if (g_ascii_strcasecmp (s, "little_endian") == 0) {                 \
1546       x = G_LITTLE_ENDIAN;                                              \
1547       ret = TRUE;                                                       \
1548     } else if (g_ascii_strcasecmp (s, "big_endian") == 0) {             \
1549       x = G_BIG_ENDIAN;                                                 \
1550       ret = TRUE;                                                       \
1551     } else if (g_ascii_strcasecmp (s, "byte_order") == 0) {             \
1552       x = G_BYTE_ORDER;                                                 \
1553       ret = TRUE;                                                       \
1554     } else if (g_ascii_strcasecmp (s, "min") == 0) {                    \
1555       x = 0;                                                            \
1556       ret = TRUE;                                                       \
1557     } else if (g_ascii_strcasecmp (s, "max") == 0) {                    \
1558       x = G_MAX ## _macro;                                              \
1559       ret = TRUE;                                                       \
1560     }                                                                   \
1561   }                                                                     \
1562   if (ret) {                                                            \
1563     if (x > G_MAX ## _macro) {                                          \
1564       ret = FALSE;                                                      \
1565     } else {                                                            \
1566       g_value_set_ ## _type (dest, x);                                  \
1567     }                                                                   \
1568   }                                                                     \
1569   return ret;                                                           \
1570 }
1571
1572 #define REGISTER_SERIALIZATION(_gtype, _type)                           \
1573 G_STMT_START {                                                          \
1574   static const GstValueTable gst_value = {                              \
1575     _gtype,                                                             \
1576     gst_value_compare_ ## _type,                                        \
1577     gst_value_serialize_ ## _type,                                      \
1578     gst_value_deserialize_ ## _type,                                    \
1579   };                                                                    \
1580                                                                         \
1581   gst_value_register (&gst_value);                                      \
1582 } G_STMT_END
1583
1584 CREATE_SERIALIZATION (int, INT);
1585 CREATE_SERIALIZATION (int64, INT64);
1586 CREATE_SERIALIZATION (long, LONG);
1587
1588 CREATE_USERIALIZATION (uint, UINT);
1589 CREATE_USERIALIZATION (uint64, UINT64);
1590 CREATE_USERIALIZATION (ulong, ULONG);
1591
1592 /**********
1593  * double *
1594  **********/
1595 static int
1596 gst_value_compare_double (const GValue * value1, const GValue * value2)
1597 {
1598   if (value1->data[0].v_double > value2->data[0].v_double)
1599     return GST_VALUE_GREATER_THAN;
1600   if (value1->data[0].v_double < value2->data[0].v_double)
1601     return GST_VALUE_LESS_THAN;
1602   if (value1->data[0].v_double == value2->data[0].v_double)
1603     return GST_VALUE_EQUAL;
1604   return GST_VALUE_UNORDERED;
1605 }
1606
1607 static char *
1608 gst_value_serialize_double (const GValue * value)
1609 {
1610   char d[G_ASCII_DTOSTR_BUF_SIZE];
1611
1612   g_ascii_dtostr (d, G_ASCII_DTOSTR_BUF_SIZE, value->data[0].v_double);
1613   return g_strdup (d);
1614 }
1615
1616 static gboolean
1617 gst_value_deserialize_double (GValue * dest, const gchar * s)
1618 {
1619   double x;
1620   gboolean ret = FALSE;
1621   char *end;
1622
1623   x = g_ascii_strtod (s, &end);
1624   if (*end == 0) {
1625     ret = TRUE;
1626   } else {
1627     if (g_ascii_strcasecmp (s, "min") == 0) {
1628       x = -G_MAXDOUBLE;
1629       ret = TRUE;
1630     } else if (g_ascii_strcasecmp (s, "max") == 0) {
1631       x = G_MAXDOUBLE;
1632       ret = TRUE;
1633     }
1634   }
1635   if (ret) {
1636     g_value_set_double (dest, x);
1637   }
1638   return ret;
1639 }
1640
1641 /*********
1642  * float *
1643  *********/
1644
1645 static gint
1646 gst_value_compare_float (const GValue * value1, const GValue * value2)
1647 {
1648   if (value1->data[0].v_float > value2->data[0].v_float)
1649     return GST_VALUE_GREATER_THAN;
1650   if (value1->data[0].v_float < value2->data[0].v_float)
1651     return GST_VALUE_LESS_THAN;
1652   if (value1->data[0].v_float == value2->data[0].v_float)
1653     return GST_VALUE_EQUAL;
1654   return GST_VALUE_UNORDERED;
1655 }
1656
1657 static gchar *
1658 gst_value_serialize_float (const GValue * value)
1659 {
1660   gchar d[G_ASCII_DTOSTR_BUF_SIZE];
1661
1662   g_ascii_dtostr (d, G_ASCII_DTOSTR_BUF_SIZE, value->data[0].v_float);
1663   return g_strdup (d);
1664 }
1665
1666 static gboolean
1667 gst_value_deserialize_float (GValue * dest, const gchar * s)
1668 {
1669   double x;
1670   gboolean ret = FALSE;
1671   char *end;
1672
1673   x = g_ascii_strtod (s, &end);
1674   if (*end == 0) {
1675     ret = TRUE;
1676   } else {
1677     if (g_ascii_strcasecmp (s, "min") == 0) {
1678       x = -G_MAXFLOAT;
1679       ret = TRUE;
1680     } else if (g_ascii_strcasecmp (s, "max") == 0) {
1681       x = G_MAXFLOAT;
1682       ret = TRUE;
1683     }
1684   }
1685   if (x > G_MAXFLOAT || x < -G_MAXFLOAT)
1686     ret = FALSE;
1687   if (ret) {
1688     g_value_set_float (dest, (float) x);
1689   }
1690   return ret;
1691 }
1692
1693 /**********
1694  * string *
1695  **********/
1696
1697 static gint
1698 gst_value_compare_string (const GValue * value1, const GValue * value2)
1699 {
1700   int x = strcmp (value1->data[0].v_pointer, value2->data[0].v_pointer);
1701
1702   if (x < 0)
1703     return GST_VALUE_LESS_THAN;
1704   if (x > 0)
1705     return GST_VALUE_GREATER_THAN;
1706   return GST_VALUE_EQUAL;
1707 }
1708
1709 #define GST_ASCII_IS_STRING(c) (g_ascii_isalnum((c)) || ((c) == '_') || \
1710     ((c) == '-') || ((c) == '+') || ((c) == '/') || ((c) == ':') || \
1711     ((c) == '.'))
1712
1713 static gchar *
1714 gst_string_wrap (const gchar * s)
1715 {
1716   const gchar *t;
1717   int len;
1718   gchar *d, *e;
1719   gboolean wrap = FALSE;
1720
1721   len = 0;
1722   t = s;
1723   if (!s)
1724     return NULL;
1725   while (*t) {
1726     if (GST_ASCII_IS_STRING (*t)) {
1727       len++;
1728     } else if (*t < 0x20 || *t >= 0x7f) {
1729       wrap = TRUE;
1730       len += 4;
1731     } else {
1732       wrap = TRUE;
1733       len += 2;
1734     }
1735     t++;
1736   }
1737
1738   if (!wrap)
1739     return g_strdup (s);
1740
1741   e = d = g_malloc (len + 3);
1742
1743   *e++ = '\"';
1744   t = s;
1745   while (*t) {
1746     if (GST_ASCII_IS_STRING (*t)) {
1747       *e++ = *t++;
1748     } else if (*t < 0x20 || *t >= 0x7f) {
1749       *e++ = '\\';
1750       *e++ = '0' + ((*(guchar *) t) >> 6);
1751       *e++ = '0' + (((*t) >> 3) & 0x7);
1752       *e++ = '0' + ((*t++) & 0x7);
1753     } else {
1754       *e++ = '\\';
1755       *e++ = *t++;
1756     }
1757   }
1758   *e++ = '\"';
1759   *e = 0;
1760
1761   return d;
1762 }
1763
1764 /*
1765  * This function takes a string delimited with double quotes (")
1766  * and unescapes any \xxx octal numbers.
1767  *
1768  * If sequences of \y are found where y is not in the range of
1769  * 0->3, y is copied unescaped.
1770  *
1771  * If \xyy is found where x is an octal number but y is not, an
1772  * error is encountered and NULL is returned.
1773  *
1774  * the input string must be \0 terminated.
1775  */
1776 static gchar *
1777 gst_string_unwrap (const gchar * s)
1778 {
1779   gchar *ret;
1780   gchar *read, *write;
1781
1782   /* NULL string returns NULL */
1783   if (s == NULL)
1784     return NULL;
1785
1786   /* strings not starting with " are invalid */
1787   if (*s != '"')
1788     return NULL;
1789
1790   /* make copy of original string to hold the result. This
1791    * string will always be smaller than the original */
1792   ret = g_strdup (s);
1793   read = ret;
1794   write = ret;
1795
1796   /* need to move to the next position as we parsed the " */
1797   read++;
1798
1799   while (*read) {
1800     if (GST_ASCII_IS_STRING (*read)) {
1801       /* normal chars are just copied */
1802       *write++ = *read++;
1803     } else if (*read == '"') {
1804       /* quote marks end of string */
1805       break;
1806     } else if (*read == '\\') {
1807       /* got an escape char, move to next position to read a tripplet
1808        * of octal numbers */
1809       read++;
1810       /* is the next char a possible first octal number? */
1811       if (*read >= '0' && *read <= '3') {
1812         /* parse other 2 numbers, if one of them is not in the range of
1813          * an octal number, we error. We also catch the case where a zero
1814          * byte is found here. */
1815         if (read[1] < '0' || read[1] > '7' || read[2] < '0' || read[2] > '7')
1816           goto beach;
1817
1818         /* now convert the octal number to a byte again. */
1819         *write++ = ((read[0] - '0') << 6) +
1820             ((read[1] - '0') << 3) + (read[2] - '0');
1821
1822         read += 3;
1823       } else {
1824         /* if we run into a \0 here, we definately won't get a quote later */
1825         if (*read == 0)
1826           goto beach;
1827
1828         /* else copy \X sequence */
1829         *write++ = *read++;
1830       }
1831     } else {
1832       /* weird character, error */
1833       goto beach;
1834     }
1835   }
1836   /* if the string is not ending in " and zero terminated, we error */
1837   if (*read != '"' || read[1] != '\0')
1838     goto beach;
1839
1840   /* null terminate result string and return */
1841   *write++ = '\0';
1842   return ret;
1843
1844 beach:
1845   g_free (ret);
1846   return NULL;
1847 }
1848
1849 static gchar *
1850 gst_value_serialize_string (const GValue * value)
1851 {
1852   return gst_string_wrap (value->data[0].v_pointer);
1853 }
1854
1855 static gboolean
1856 gst_value_deserialize_string (GValue * dest, const gchar * s)
1857 {
1858   if (*s != '"') {
1859     if (!g_utf8_validate (s, -1, NULL))
1860       return FALSE;
1861     g_value_set_string (dest, s);
1862     return TRUE;
1863   } else {
1864     gchar *str = gst_string_unwrap (s);
1865
1866     if (!str)
1867       return FALSE;
1868     g_value_take_string (dest, str);
1869   }
1870
1871   return TRUE;
1872 }
1873
1874 /********
1875  * enum *
1876  ********/
1877
1878 static gint
1879 gst_value_compare_enum (const GValue * value1, const GValue * value2)
1880 {
1881   GEnumValue *en1, *en2;
1882   GEnumClass *klass1 = (GEnumClass *) g_type_class_ref (G_VALUE_TYPE (value1));
1883   GEnumClass *klass2 = (GEnumClass *) g_type_class_ref (G_VALUE_TYPE (value2));
1884
1885   g_return_val_if_fail (klass1, GST_VALUE_UNORDERED);
1886   g_return_val_if_fail (klass2, GST_VALUE_UNORDERED);
1887   en1 = g_enum_get_value (klass1, g_value_get_enum (value1));
1888   en2 = g_enum_get_value (klass2, g_value_get_enum (value2));
1889   g_type_class_unref (klass1);
1890   g_type_class_unref (klass2);
1891   g_return_val_if_fail (en1, GST_VALUE_UNORDERED);
1892   g_return_val_if_fail (en2, GST_VALUE_UNORDERED);
1893   if (en1->value < en2->value)
1894     return GST_VALUE_LESS_THAN;
1895   if (en1->value > en2->value)
1896     return GST_VALUE_GREATER_THAN;
1897
1898   return GST_VALUE_EQUAL;
1899 }
1900
1901 static gchar *
1902 gst_value_serialize_enum (const GValue * value)
1903 {
1904   GEnumValue *en;
1905   GEnumClass *klass = (GEnumClass *) g_type_class_ref (G_VALUE_TYPE (value));
1906
1907   g_return_val_if_fail (klass, NULL);
1908   en = g_enum_get_value (klass, g_value_get_enum (value));
1909   g_type_class_unref (klass);
1910   g_return_val_if_fail (en, NULL);
1911   return g_strdup (en->value_name);
1912 }
1913
1914 static gboolean
1915 gst_value_deserialize_enum (GValue * dest, const gchar * s)
1916 {
1917   GEnumValue *en;
1918   gchar *endptr = NULL;
1919   GEnumClass *klass = (GEnumClass *) g_type_class_ref (G_VALUE_TYPE (dest));
1920
1921   g_return_val_if_fail (klass, FALSE);
1922   if (!(en = g_enum_get_value_by_name (klass, s))) {
1923     if (!(en = g_enum_get_value_by_nick (klass, s))) {
1924       gint i = strtol (s, &endptr, 0);
1925
1926       if (endptr && *endptr == '\0') {
1927         en = g_enum_get_value (klass, i);
1928       }
1929     }
1930   }
1931   g_type_class_unref (klass);
1932   g_return_val_if_fail (en, FALSE);
1933   g_value_set_enum (dest, en->value);
1934   return TRUE;
1935 }
1936
1937 /********
1938  * flags *
1939  ********/
1940
1941 /* we just compare the value here */
1942 static gint
1943 gst_value_compare_flags (const GValue * value1, const GValue * value2)
1944 {
1945   guint fl1, fl2;
1946   GFlagsClass *klass1 =
1947       (GFlagsClass *) g_type_class_ref (G_VALUE_TYPE (value1));
1948   GFlagsClass *klass2 =
1949       (GFlagsClass *) g_type_class_ref (G_VALUE_TYPE (value2));
1950
1951   g_return_val_if_fail (klass1, GST_VALUE_UNORDERED);
1952   g_return_val_if_fail (klass2, GST_VALUE_UNORDERED);
1953   fl1 = g_value_get_flags (value1);
1954   fl2 = g_value_get_flags (value2);
1955   g_type_class_unref (klass1);
1956   g_type_class_unref (klass2);
1957   if (fl1 < fl2)
1958     return GST_VALUE_LESS_THAN;
1959   if (fl1 > fl2)
1960     return GST_VALUE_GREATER_THAN;
1961
1962   return GST_VALUE_EQUAL;
1963 }
1964
1965 /* the different flags are serialized separated with a + */
1966 static gchar *
1967 gst_value_serialize_flags (const GValue * value)
1968 {
1969   guint flags;
1970   GFlagsValue *fl;
1971   GFlagsClass *klass = (GFlagsClass *) g_type_class_ref (G_VALUE_TYPE (value));
1972   gchar *result, *tmp;
1973   gboolean first = TRUE;
1974
1975   g_return_val_if_fail (klass, NULL);
1976
1977   flags = g_value_get_flags (value);
1978
1979   /* if no flags are set, try to serialize to the _NONE string */
1980   if (!flags) {
1981     fl = g_flags_get_first_value (klass, flags);
1982     return g_strdup (fl->value_name);
1983   }
1984
1985   /* some flags are set, so serialize one by one */
1986   result = g_strdup ("");
1987   while (flags) {
1988     fl = g_flags_get_first_value (klass, flags);
1989     if (fl != NULL) {
1990       tmp = g_strconcat (result, (first ? "" : "+"), fl->value_name, NULL);
1991       g_free (result);
1992       result = tmp;
1993       first = FALSE;
1994
1995       /* clear flag */
1996       flags &= ~fl->value;
1997     }
1998   }
1999   g_type_class_unref (klass);
2000
2001   return result;
2002 }
2003
2004 static gboolean
2005 gst_value_deserialize_flags (GValue * dest, const gchar * s)
2006 {
2007   GFlagsValue *fl;
2008   gchar *endptr = NULL;
2009   GFlagsClass *klass = (GFlagsClass *) g_type_class_ref (G_VALUE_TYPE (dest));
2010   gchar **split;
2011   guint flags;
2012   gint i;
2013
2014   g_return_val_if_fail (klass, FALSE);
2015
2016   /* split into parts delimited with + */
2017   split = g_strsplit (s, "+", 0);
2018
2019   flags = 0;
2020   i = 0;
2021   /* loop over each part */
2022   while (split[i]) {
2023     if (!(fl = g_flags_get_value_by_name (klass, split[i]))) {
2024       if (!(fl = g_flags_get_value_by_nick (klass, split[i]))) {
2025         gint val = strtol (split[i], &endptr, 0);
2026
2027         /* just or numeric value */
2028         if (endptr && *endptr == '\0') {
2029           flags |= val;
2030         }
2031       }
2032     }
2033     if (fl) {
2034       flags |= fl->value;
2035     }
2036     i++;
2037   }
2038   g_strfreev (split);
2039   g_type_class_unref (klass);
2040   g_value_set_flags (dest, flags);
2041
2042   return TRUE;
2043 }
2044
2045 /*********
2046  * union *
2047  *********/
2048
2049 static gboolean
2050 gst_value_union_int_int_range (GValue * dest, const GValue * src1,
2051     const GValue * src2)
2052 {
2053   if (src2->data[0].v_int <= src1->data[0].v_int &&
2054       src2->data[1].v_int >= src1->data[0].v_int) {
2055     gst_value_init_and_copy (dest, src2);
2056     return TRUE;
2057   }
2058   return FALSE;
2059 }
2060
2061 static gboolean
2062 gst_value_union_int_range_int_range (GValue * dest, const GValue * src1,
2063     const GValue * src2)
2064 {
2065   gint min;
2066   gint max;
2067
2068   min = MAX (src1->data[0].v_int, src2->data[0].v_int);
2069   max = MIN (src1->data[1].v_int, src2->data[1].v_int);
2070
2071   if (min <= max) {
2072     g_value_init (dest, GST_TYPE_INT_RANGE);
2073     gst_value_set_int_range (dest,
2074         MIN (src1->data[0].v_int, src2->data[0].v_int),
2075         MAX (src1->data[1].v_int, src2->data[1].v_int));
2076     return TRUE;
2077   }
2078
2079   return FALSE;
2080 }
2081
2082 /****************
2083  * intersection *
2084  ****************/
2085
2086 static gboolean
2087 gst_value_intersect_int_int_range (GValue * dest, const GValue * src1,
2088     const GValue * src2)
2089 {
2090   if (src2->data[0].v_int <= src1->data[0].v_int &&
2091       src2->data[1].v_int >= src1->data[0].v_int) {
2092     gst_value_init_and_copy (dest, src1);
2093     return TRUE;
2094   }
2095
2096   return FALSE;
2097 }
2098
2099 static gboolean
2100 gst_value_intersect_int_range_int_range (GValue * dest, const GValue * src1,
2101     const GValue * src2)
2102 {
2103   gint min;
2104   gint max;
2105
2106   min = MAX (src1->data[0].v_int, src2->data[0].v_int);
2107   max = MIN (src1->data[1].v_int, src2->data[1].v_int);
2108
2109   if (min < max) {
2110     g_value_init (dest, GST_TYPE_INT_RANGE);
2111     gst_value_set_int_range (dest, min, max);
2112     return TRUE;
2113   }
2114   if (min == max) {
2115     g_value_init (dest, G_TYPE_INT);
2116     g_value_set_int (dest, min);
2117     return TRUE;
2118   }
2119
2120   return FALSE;
2121 }
2122
2123 static gboolean
2124 gst_value_intersect_double_double_range (GValue * dest, const GValue * src1,
2125     const GValue * src2)
2126 {
2127   if (src2->data[0].v_double <= src1->data[0].v_double &&
2128       src2->data[1].v_double >= src1->data[0].v_double) {
2129     gst_value_init_and_copy (dest, src1);
2130     return TRUE;
2131   }
2132
2133   return FALSE;
2134 }
2135
2136 static gboolean
2137 gst_value_intersect_double_range_double_range (GValue * dest,
2138     const GValue * src1, const GValue * src2)
2139 {
2140   gdouble min;
2141   gdouble max;
2142
2143   min = MAX (src1->data[0].v_double, src2->data[0].v_double);
2144   max = MIN (src1->data[1].v_double, src2->data[1].v_double);
2145
2146   if (min < max) {
2147     g_value_init (dest, GST_TYPE_DOUBLE_RANGE);
2148     gst_value_set_double_range (dest, min, max);
2149     return TRUE;
2150   }
2151   if (min == max) {
2152     g_value_init (dest, G_TYPE_DOUBLE);
2153     g_value_set_int (dest, (int) min);
2154     return TRUE;
2155   }
2156
2157   return FALSE;
2158 }
2159
2160 static gboolean
2161 gst_value_intersect_list (GValue * dest, const GValue * value1,
2162     const GValue * value2)
2163 {
2164   guint i, size;
2165   GValue intersection = { 0, };
2166   gboolean ret = FALSE;
2167
2168   size = gst_value_list_get_size (value1);
2169   for (i = 0; i < size; i++) {
2170     const GValue *cur = gst_value_list_get_value (value1, i);
2171
2172     if (gst_value_intersect (&intersection, cur, value2)) {
2173       /* append value */
2174       if (!ret) {
2175         gst_value_init_and_copy (dest, &intersection);
2176         ret = TRUE;
2177       } else if (GST_VALUE_HOLDS_LIST (dest)) {
2178         gst_value_list_append_value (dest, &intersection);
2179       } else {
2180         GValue temp = { 0, };
2181
2182         gst_value_init_and_copy (&temp, dest);
2183         g_value_unset (dest);
2184         gst_value_list_concat (dest, &temp, &intersection);
2185         g_value_unset (&temp);
2186       }
2187       g_value_unset (&intersection);
2188     }
2189   }
2190
2191   return ret;
2192 }
2193
2194 static gboolean
2195 gst_value_intersect_array (GValue * dest, const GValue * src1,
2196     const GValue * src2)
2197 {
2198   guint size;
2199   guint n;
2200   GValue val = { 0 };
2201
2202   /* only works on similar-sized arrays */
2203   size = gst_value_array_get_size (src1);
2204   if (size != gst_value_array_get_size (src2))
2205     return FALSE;
2206   g_value_init (dest, GST_TYPE_ARRAY);
2207
2208   for (n = 0; n < size; n++) {
2209     if (!gst_value_intersect (&val, gst_value_array_get_value (src1, n),
2210             gst_value_array_get_value (src2, n))) {
2211       g_value_unset (dest);
2212       return FALSE;
2213     }
2214     gst_value_array_append_value (dest, &val);
2215     g_value_unset (&val);
2216   }
2217
2218   return TRUE;
2219 }
2220
2221 static gboolean
2222 gst_value_intersect_fraction_fraction_range (GValue * dest, const GValue * src1,
2223     const GValue * src2)
2224 {
2225   int res1, res2;
2226   GValue *vals;
2227   GstValueCompareFunc compare;
2228
2229   vals = src2->data[0].v_pointer;
2230
2231   if (vals == NULL)
2232     return FALSE;
2233
2234   if ((compare = gst_value_get_compare_func (src1))) {
2235     res1 = gst_value_compare_with_func (&vals[0], src1, compare);
2236     res2 = gst_value_compare_with_func (&vals[1], src1, compare);
2237
2238     if ((res1 == GST_VALUE_EQUAL || res1 == GST_VALUE_LESS_THAN) &&
2239         (res2 == GST_VALUE_EQUAL || res2 == GST_VALUE_GREATER_THAN)) {
2240       gst_value_init_and_copy (dest, src1);
2241       return TRUE;
2242     }
2243   }
2244
2245   return FALSE;
2246 }
2247
2248 static gboolean
2249     gst_value_intersect_fraction_range_fraction_range
2250     (GValue * dest, const GValue * src1, const GValue * src2)
2251 {
2252   GValue *min;
2253   GValue *max;
2254   int res;
2255   GValue *vals1, *vals2;
2256   GstValueCompareFunc compare;
2257
2258   vals1 = src1->data[0].v_pointer;
2259   vals2 = src2->data[0].v_pointer;
2260   g_return_val_if_fail (vals1 != NULL && vals2 != NULL, FALSE);
2261
2262   if ((compare = gst_value_get_compare_func (&vals1[0]))) {
2263     /* min = MAX (src1.start, src2.start) */
2264     res = gst_value_compare_with_func (&vals1[0], &vals2[0], compare);
2265     g_return_val_if_fail (res != GST_VALUE_UNORDERED, FALSE);
2266     if (res == GST_VALUE_LESS_THAN)
2267       min = &vals2[0];          /* Take the max of the 2 */
2268     else
2269       min = &vals1[0];
2270
2271     /* max = MIN (src1.end, src2.end) */
2272     res = gst_value_compare_with_func (&vals1[1], &vals2[1], compare);
2273     g_return_val_if_fail (res != GST_VALUE_UNORDERED, FALSE);
2274     if (res == GST_VALUE_GREATER_THAN)
2275       max = &vals2[1];          /* Take the min of the 2 */
2276     else
2277       max = &vals1[1];
2278
2279     res = gst_value_compare_with_func (min, max, compare);
2280     g_return_val_if_fail (res != GST_VALUE_UNORDERED, FALSE);
2281     if (res == GST_VALUE_LESS_THAN) {
2282       g_value_init (dest, GST_TYPE_FRACTION_RANGE);
2283       vals1 = dest->data[0].v_pointer;
2284       g_value_copy (min, &vals1[0]);
2285       g_value_copy (max, &vals1[1]);
2286       return TRUE;
2287     }
2288     if (res == GST_VALUE_EQUAL) {
2289       gst_value_init_and_copy (dest, min);
2290       return TRUE;
2291     }
2292   }
2293
2294   return FALSE;
2295 }
2296
2297 /***************
2298  * subtraction *
2299  ***************/
2300
2301 static gboolean
2302 gst_value_subtract_int_int_range (GValue * dest, const GValue * minuend,
2303     const GValue * subtrahend)
2304 {
2305   int min = gst_value_get_int_range_min (subtrahend);
2306   int max = gst_value_get_int_range_max (subtrahend);
2307   int val = g_value_get_int (minuend);
2308
2309   /* subtracting a range from an int only works if the int is not in the
2310    * range */
2311   if (val < min || val > max) {
2312     /* and the result is the int */
2313     gst_value_init_and_copy (dest, minuend);
2314     return TRUE;
2315   }
2316   return FALSE;
2317 }
2318
2319 /* creates a new int range based on input values.
2320  */
2321 static gboolean
2322 gst_value_create_new_range (GValue * dest, gint min1, gint max1, gint min2,
2323     gint max2)
2324 {
2325   GValue v1 = { 0, };
2326   GValue v2 = { 0, };
2327   GValue *pv1, *pv2;            /* yeah, hungarian! */
2328
2329   if (min1 <= max1 && min2 <= max2) {
2330     pv1 = &v1;
2331     pv2 = &v2;
2332   } else if (min1 <= max1) {
2333     pv1 = dest;
2334     pv2 = NULL;
2335   } else if (min2 <= max2) {
2336     pv1 = NULL;
2337     pv2 = dest;
2338   } else {
2339     return FALSE;
2340   }
2341
2342   if (min1 < max1) {
2343     g_value_init (pv1, GST_TYPE_INT_RANGE);
2344     gst_value_set_int_range (pv1, min1, max1);
2345   } else if (min1 == max1) {
2346     g_value_init (pv1, G_TYPE_INT);
2347     g_value_set_int (pv1, min1);
2348   }
2349   if (min2 < max2) {
2350     g_value_init (pv2, GST_TYPE_INT_RANGE);
2351     gst_value_set_int_range (pv2, min2, max2);
2352   } else if (min2 == max2) {
2353     g_value_init (pv2, G_TYPE_INT);
2354     g_value_set_int (pv2, min2);
2355   }
2356
2357   if (min1 <= max1 && min2 <= max2) {
2358     gst_value_list_concat (dest, pv1, pv2);
2359     g_value_unset (pv1);
2360     g_value_unset (pv2);
2361   }
2362   return TRUE;
2363 }
2364
2365 static gboolean
2366 gst_value_subtract_int_range_int (GValue * dest, const GValue * minuend,
2367     const GValue * subtrahend)
2368 {
2369   gint min = gst_value_get_int_range_min (minuend);
2370   gint max = gst_value_get_int_range_max (minuend);
2371   gint val = g_value_get_int (subtrahend);
2372
2373   g_return_val_if_fail (min < max, FALSE);
2374
2375   /* value is outside of the range, return range unchanged */
2376   if (val < min || val > max) {
2377     gst_value_init_and_copy (dest, minuend);
2378     return TRUE;
2379   } else {
2380     /* max must be MAXINT too as val <= max */
2381     if (val == G_MAXINT) {
2382       max--;
2383       val--;
2384     }
2385     /* min must be MININT too as val >= max */
2386     if (val == G_MININT) {
2387       min++;
2388       val++;
2389     }
2390     gst_value_create_new_range (dest, min, val - 1, val + 1, max);
2391   }
2392   return TRUE;
2393 }
2394
2395 static gboolean
2396 gst_value_subtract_int_range_int_range (GValue * dest, const GValue * minuend,
2397     const GValue * subtrahend)
2398 {
2399   gint min1 = gst_value_get_int_range_min (minuend);
2400   gint max1 = gst_value_get_int_range_max (minuend);
2401   gint min2 = gst_value_get_int_range_min (subtrahend);
2402   gint max2 = gst_value_get_int_range_max (subtrahend);
2403
2404   if (max2 == G_MAXINT && min2 == G_MININT) {
2405     return FALSE;
2406   } else if (max2 == G_MAXINT) {
2407     return gst_value_create_new_range (dest, min1, MIN (min2 - 1, max1), 1, 0);
2408   } else if (min2 == G_MININT) {
2409     return gst_value_create_new_range (dest, MAX (max2 + 1, min1), max1, 1, 0);
2410   } else {
2411     return gst_value_create_new_range (dest, min1, MIN (min2 - 1, max1),
2412         MAX (max2 + 1, min1), max1);
2413   }
2414 }
2415
2416 static gboolean
2417 gst_value_subtract_double_double_range (GValue * dest, const GValue * minuend,
2418     const GValue * subtrahend)
2419 {
2420   gdouble min = gst_value_get_double_range_min (subtrahend);
2421   gdouble max = gst_value_get_double_range_max (subtrahend);
2422   gdouble val = g_value_get_double (minuend);
2423
2424   if (val < min || val > max) {
2425     gst_value_init_and_copy (dest, minuend);
2426     return TRUE;
2427   }
2428   return FALSE;
2429 }
2430
2431 static gboolean
2432 gst_value_subtract_double_range_double (GValue * dest, const GValue * minuend,
2433     const GValue * subtrahend)
2434 {
2435   /* since we don't have open ranges, we cannot create a hole in
2436    * a double range. We return the original range */
2437   gst_value_init_and_copy (dest, minuend);
2438   return TRUE;
2439 }
2440
2441 static gboolean
2442 gst_value_subtract_double_range_double_range (GValue * dest,
2443     const GValue * minuend, const GValue * subtrahend)
2444 {
2445   /* since we don't have open ranges, we have to approximate */
2446   /* done like with ints */
2447   gdouble min1 = gst_value_get_double_range_min (minuend);
2448   gdouble max2 = gst_value_get_double_range_max (minuend);
2449   gdouble max1 = MIN (gst_value_get_double_range_min (subtrahend), max2);
2450   gdouble min2 = MAX (gst_value_get_double_range_max (subtrahend), min1);
2451   GValue v1 = { 0, };
2452   GValue v2 = { 0, };
2453   GValue *pv1, *pv2;            /* yeah, hungarian! */
2454
2455   if (min1 < max1 && min2 < max2) {
2456     pv1 = &v1;
2457     pv2 = &v2;
2458   } else if (min1 < max1) {
2459     pv1 = dest;
2460     pv2 = NULL;
2461   } else if (min2 < max2) {
2462     pv1 = NULL;
2463     pv2 = dest;
2464   } else {
2465     return FALSE;
2466   }
2467
2468   if (min1 < max1) {
2469     g_value_init (pv1, GST_TYPE_DOUBLE_RANGE);
2470     gst_value_set_double_range (pv1, min1, max1);
2471   }
2472   if (min2 < max2) {
2473     g_value_init (pv2, GST_TYPE_DOUBLE_RANGE);
2474     gst_value_set_double_range (pv2, min2, max2);
2475   }
2476
2477   if (min1 < max1 && min2 < max2) {
2478     gst_value_list_concat (dest, pv1, pv2);
2479     g_value_unset (pv1);
2480     g_value_unset (pv2);
2481   }
2482   return TRUE;
2483 }
2484
2485 static gboolean
2486 gst_value_subtract_from_list (GValue * dest, const GValue * minuend,
2487     const GValue * subtrahend)
2488 {
2489   guint i, size;
2490   GValue subtraction = { 0, };
2491   gboolean ret = FALSE;
2492
2493   size = gst_value_list_get_size (minuend);
2494   for (i = 0; i < size; i++) {
2495     const GValue *cur = gst_value_list_get_value (minuend, i);
2496
2497     if (gst_value_subtract (&subtraction, cur, subtrahend)) {
2498       if (!ret) {
2499         gst_value_init_and_copy (dest, &subtraction);
2500         ret = TRUE;
2501       } else if (GST_VALUE_HOLDS_LIST (dest)
2502           && GST_VALUE_HOLDS_LIST (&subtraction)) {
2503         /* unroll */
2504         GValue unroll = { 0, };
2505
2506         gst_value_init_and_copy (&unroll, dest);
2507         g_value_unset (dest);
2508         gst_value_list_concat (dest, &unroll, &subtraction);
2509       } else if (GST_VALUE_HOLDS_LIST (dest)) {
2510         gst_value_list_append_value (dest, &subtraction);
2511       } else {
2512         GValue temp = { 0, };
2513
2514         gst_value_init_and_copy (&temp, dest);
2515         g_value_unset (dest);
2516         gst_value_list_concat (dest, &temp, &subtraction);
2517         g_value_unset (&temp);
2518       }
2519       g_value_unset (&subtraction);
2520     }
2521   }
2522   return ret;
2523 }
2524
2525 static gboolean
2526 gst_value_subtract_list (GValue * dest, const GValue * minuend,
2527     const GValue * subtrahend)
2528 {
2529   guint i, size;
2530   GValue data[2] = { {0,}, {0,} };
2531   GValue *subtraction = &data[0], *result = &data[1];
2532
2533   gst_value_init_and_copy (result, minuend);
2534   size = gst_value_list_get_size (subtrahend);
2535   for (i = 0; i < size; i++) {
2536     const GValue *cur = gst_value_list_get_value (subtrahend, i);
2537
2538     if (gst_value_subtract (subtraction, result, cur)) {
2539       GValue *temp = result;
2540
2541       result = subtraction;
2542       subtraction = temp;
2543       g_value_unset (subtraction);
2544     } else {
2545       g_value_unset (result);
2546       return FALSE;
2547     }
2548   }
2549   gst_value_init_and_copy (dest, result);
2550   g_value_unset (result);
2551   return TRUE;
2552 }
2553
2554 static gboolean
2555 gst_value_subtract_fraction_fraction_range (GValue * dest,
2556     const GValue * minuend, const GValue * subtrahend)
2557 {
2558   const GValue *min = gst_value_get_fraction_range_min (subtrahend);
2559   const GValue *max = gst_value_get_fraction_range_max (subtrahend);
2560   GstValueCompareFunc compare;
2561
2562   if ((compare = gst_value_get_compare_func (minuend))) {
2563     /* subtracting a range from an fraction only works if the fraction
2564      * is not in the range */
2565     if (gst_value_compare_with_func (minuend, min, compare) ==
2566         GST_VALUE_LESS_THAN ||
2567         gst_value_compare_with_func (minuend, max, compare) ==
2568         GST_VALUE_GREATER_THAN) {
2569       /* and the result is the value */
2570       gst_value_init_and_copy (dest, minuend);
2571       return TRUE;
2572     }
2573   }
2574   return FALSE;
2575 }
2576
2577 static gboolean
2578 gst_value_subtract_fraction_range_fraction (GValue * dest,
2579     const GValue * minuend, const GValue * subtrahend)
2580 {
2581   /* since we don't have open ranges, we cannot create a hole in
2582    * a range. We return the original range */
2583   gst_value_init_and_copy (dest, minuend);
2584   return TRUE;
2585 }
2586
2587 static gboolean
2588 gst_value_subtract_fraction_range_fraction_range (GValue * dest,
2589     const GValue * minuend, const GValue * subtrahend)
2590 {
2591   /* since we don't have open ranges, we have to approximate */
2592   /* done like with ints and doubles. Creates a list of 2 fraction ranges */
2593   const GValue *min1 = gst_value_get_fraction_range_min (minuend);
2594   const GValue *max2 = gst_value_get_fraction_range_max (minuend);
2595   const GValue *max1 = gst_value_get_fraction_range_min (subtrahend);
2596   const GValue *min2 = gst_value_get_fraction_range_max (subtrahend);
2597   int cmp1, cmp2;
2598   GValue v1 = { 0, };
2599   GValue v2 = { 0, };
2600   GValue *pv1, *pv2;            /* yeah, hungarian! */
2601   GstValueCompareFunc compare;
2602
2603   g_return_val_if_fail (min1 != NULL && max1 != NULL, FALSE);
2604   g_return_val_if_fail (min2 != NULL && max2 != NULL, FALSE);
2605
2606   compare = gst_value_get_compare_func (min1);
2607   g_return_val_if_fail (compare, FALSE);
2608
2609   cmp1 = gst_value_compare_with_func (max2, max1, compare);
2610   g_return_val_if_fail (cmp1 != GST_VALUE_UNORDERED, FALSE);
2611   if (cmp1 == GST_VALUE_LESS_THAN)
2612     max1 = max2;
2613   cmp1 = gst_value_compare_with_func (min1, min2, compare);
2614   g_return_val_if_fail (cmp1 != GST_VALUE_UNORDERED, FALSE);
2615   if (cmp1 == GST_VALUE_GREATER_THAN)
2616     min2 = min1;
2617
2618   cmp1 = gst_value_compare_with_func (min1, max1, compare);
2619   cmp2 = gst_value_compare_with_func (min2, max2, compare);
2620
2621   if (cmp1 == GST_VALUE_LESS_THAN && cmp2 == GST_VALUE_LESS_THAN) {
2622     pv1 = &v1;
2623     pv2 = &v2;
2624   } else if (cmp1 == GST_VALUE_LESS_THAN) {
2625     pv1 = dest;
2626     pv2 = NULL;
2627   } else if (cmp2 == GST_VALUE_LESS_THAN) {
2628     pv1 = NULL;
2629     pv2 = dest;
2630   } else {
2631     return FALSE;
2632   }
2633
2634   if (cmp1 == GST_VALUE_LESS_THAN) {
2635     g_value_init (pv1, GST_TYPE_FRACTION_RANGE);
2636     gst_value_set_fraction_range (pv1, min1, max1);
2637   }
2638   if (cmp2 == GST_VALUE_LESS_THAN) {
2639     g_value_init (pv2, GST_TYPE_FRACTION_RANGE);
2640     gst_value_set_fraction_range (pv2, min2, max2);
2641   }
2642
2643   if (cmp1 == GST_VALUE_LESS_THAN && cmp2 == GST_VALUE_LESS_THAN) {
2644     gst_value_list_concat (dest, pv1, pv2);
2645     g_value_unset (pv1);
2646     g_value_unset (pv2);
2647   }
2648   return TRUE;
2649 }
2650
2651
2652 /**************
2653  * comparison *
2654  **************/
2655
2656 /**
2657  * gst_value_can_compare:
2658  * @value1: a value to compare
2659  * @value2: another value to compare
2660  *
2661  * Determines if @value1 and @value2 can be compared.
2662  *
2663  * Returns: TRUE if the values can be compared
2664  */
2665 gboolean
2666 gst_value_can_compare (const GValue * value1, const GValue * value2)
2667 {
2668   GstValueTable *table;
2669   guint i;
2670
2671   if (G_VALUE_TYPE (value1) != G_VALUE_TYPE (value2))
2672     return FALSE;
2673
2674   for (i = 0; i < gst_value_table->len; i++) {
2675     table = &g_array_index (gst_value_table, GstValueTable, i);
2676     if (g_type_is_a (G_VALUE_TYPE (value1), table->type) && table->compare)
2677       return TRUE;
2678   }
2679
2680   return FALSE;
2681 }
2682
2683 /**
2684  * gst_value_get_compare_func:
2685  * @value1: a value to get the compare function for
2686  *
2687  * Determines the compare function to be used with values of the same type as
2688  * @value1. The function can be given to gst_value_compare_with_func().
2689  *
2690  * Returns: A #GstValueCompareFunc value
2691  */
2692 GstValueCompareFunc
2693 gst_value_get_compare_func (const GValue * value1)
2694 {
2695   GstValueTable *table, *best = NULL;
2696   guint i;
2697
2698   for (i = 0; i < gst_value_table->len; i++) {
2699     table = &g_array_index (gst_value_table, GstValueTable, i);
2700     if (table->type == G_VALUE_TYPE (value1) && table->compare != NULL) {
2701       best = table;
2702       break;
2703     }
2704     if (g_type_is_a (G_VALUE_TYPE (value1), table->type)) {
2705       if (!best || g_type_is_a (table->type, best->type))
2706         best = table;
2707     }
2708   }
2709   if (best) {
2710     return best->compare;
2711   }
2712   return NULL;
2713 }
2714
2715 /**
2716  * gst_value_compare:
2717  * @value1: a value to compare
2718  * @value2: another value to compare
2719  *
2720  * Compares @value1 and @value2.  If @value1 and @value2 cannot be
2721  * compared, the function returns GST_VALUE_UNORDERED.  Otherwise,
2722  * if @value1 is greater than @value2, GST_VALUE_GREATER is returned.
2723  * If @value1 is less than @value2, GST_VALUE_LESSER is returned.
2724  * If the values are equal, GST_VALUE_EQUAL is returned.
2725  *
2726  * Returns: A #GstValueCompareType value
2727  */
2728 gint
2729 gst_value_compare (const GValue * value1, const GValue * value2)
2730 {
2731   GstValueCompareFunc compare;
2732
2733   if (G_VALUE_TYPE (value1) != G_VALUE_TYPE (value2))
2734     return GST_VALUE_UNORDERED;
2735
2736   compare = gst_value_get_compare_func (value1);
2737   if (compare) {
2738     return compare (value1, value2);
2739   }
2740
2741   g_critical ("unable to compare values of type %s\n",
2742       g_type_name (G_VALUE_TYPE (value1)));
2743   return GST_VALUE_UNORDERED;
2744 }
2745
2746 /**
2747  * gst_value_compare_with_func:
2748  * @value1: a value to compare
2749  * @value2: another value to compare
2750  * @compare: compare function
2751  *
2752  * Compares @value1 and @value2 using the @compare function. Works like
2753  * gst_value_compare() but allows to save time determining the compare function
2754  * a multiple times. 
2755  *
2756  * Returns: A #GstValueCompareType value
2757  */
2758 gint
2759 gst_value_compare_with_func (const GValue * value1, const GValue * value2,
2760     GstValueCompareFunc compare)
2761 {
2762   g_assert (compare);
2763
2764   if (G_VALUE_TYPE (value1) != G_VALUE_TYPE (value2))
2765     return GST_VALUE_UNORDERED;
2766
2767   return compare (value1, value2);
2768 }
2769
2770 /* union */
2771
2772 /**
2773  * gst_value_can_union:
2774  * @value1: a value to union
2775  * @value2: another value to union
2776  *
2777  * Determines if @value1 and @value2 can be non-trivially unioned.
2778  * Any two values can be trivially unioned by adding both of them
2779  * to a GstValueList.  However, certain types have the possibility
2780  * to be unioned in a simpler way.  For example, an integer range
2781  * and an integer can be unioned if the integer is a subset of the
2782  * integer range.  If there is the possibility that two values can
2783  * be unioned, this function returns TRUE.
2784  *
2785  * Returns: TRUE if there is a function allowing the two values to
2786  * be unioned.
2787  */
2788 gboolean
2789 gst_value_can_union (const GValue * value1, const GValue * value2)
2790 {
2791   GstValueUnionInfo *union_info;
2792   guint i;
2793
2794   for (i = 0; i < gst_value_union_funcs->len; i++) {
2795     union_info = &g_array_index (gst_value_union_funcs, GstValueUnionInfo, i);
2796     if (union_info->type1 == G_VALUE_TYPE (value1) &&
2797         union_info->type2 == G_VALUE_TYPE (value2))
2798       return TRUE;
2799     if (union_info->type1 == G_VALUE_TYPE (value2) &&
2800         union_info->type2 == G_VALUE_TYPE (value1))
2801       return TRUE;
2802   }
2803
2804   return FALSE;
2805 }
2806
2807 /**
2808  * gst_value_union:
2809  * @dest: the destination value
2810  * @value1: a value to union
2811  * @value2: another value to union
2812  *
2813  * Creates a GValue cooresponding to the union of @value1 and @value2.
2814  *
2815  * Returns: always returns %TRUE
2816  */
2817 /* FIXME: change return type to 'void'? */
2818 gboolean
2819 gst_value_union (GValue * dest, const GValue * value1, const GValue * value2)
2820 {
2821   GstValueUnionInfo *union_info;
2822   guint i;
2823
2824   for (i = 0; i < gst_value_union_funcs->len; i++) {
2825     union_info = &g_array_index (gst_value_union_funcs, GstValueUnionInfo, i);
2826     if (union_info->type1 == G_VALUE_TYPE (value1) &&
2827         union_info->type2 == G_VALUE_TYPE (value2)) {
2828       if (union_info->func (dest, value1, value2)) {
2829         return TRUE;
2830       }
2831     }
2832     if (union_info->type1 == G_VALUE_TYPE (value2) &&
2833         union_info->type2 == G_VALUE_TYPE (value1)) {
2834       if (union_info->func (dest, value2, value1)) {
2835         return TRUE;
2836       }
2837     }
2838   }
2839
2840   gst_value_list_concat (dest, value1, value2);
2841   return TRUE;
2842 }
2843
2844 /**
2845  * gst_value_register_union_func:
2846  * @type1: a type to union
2847  * @type2: another type to union
2848  * @func: a function that implments creating a union between the two types
2849  *
2850  * Registers a union function that can create a union between GValues
2851  * of the type @type1 and @type2.
2852  *
2853  * Union functions should be registered at startup before any pipelines are
2854  * started, as gst_value_register_union_func() is not thread-safe and cannot
2855  * be used at the same time as gst_value_union() or gst_value_can_union().
2856  */
2857 void
2858 gst_value_register_union_func (GType type1, GType type2, GstValueUnionFunc func)
2859 {
2860   GstValueUnionInfo union_info;
2861
2862   union_info.type1 = type1;
2863   union_info.type2 = type2;
2864   union_info.func = func;
2865
2866   g_array_append_val (gst_value_union_funcs, union_info);
2867 }
2868
2869 /* intersection */
2870
2871 /**
2872  * gst_value_can_intersect:
2873  * @value1: a value to intersect
2874  * @value2: another value to intersect
2875  *
2876  * Determines if intersecting two values will produce a valid result.
2877  * Two values will produce a valid intersection if they have the same
2878  * type, or if there is a method (registered by
2879  * gst_value_register_intersection_func()) to calculate the intersection.
2880  *
2881  * Returns: TRUE if the values can intersect
2882  */
2883 gboolean
2884 gst_value_can_intersect (const GValue * value1, const GValue * value2)
2885 {
2886   GstValueIntersectInfo *intersect_info;
2887   guint i;
2888
2889   /* special cases */
2890   if (GST_VALUE_HOLDS_LIST (value1) || GST_VALUE_HOLDS_LIST (value2))
2891     return TRUE;
2892
2893   for (i = 0; i < gst_value_intersect_funcs->len; i++) {
2894     intersect_info = &g_array_index (gst_value_intersect_funcs,
2895         GstValueIntersectInfo, i);
2896     if (intersect_info->type1 == G_VALUE_TYPE (value1) &&
2897         intersect_info->type2 == G_VALUE_TYPE (value2))
2898       if (intersect_info->type2 == G_VALUE_TYPE (value1) &&
2899           intersect_info->type1 == G_VALUE_TYPE (value2))
2900         return TRUE;
2901   }
2902
2903   return gst_value_can_compare (value1, value2);
2904 }
2905
2906 /**
2907  * gst_value_intersect:
2908  * @dest: a uninitialized #GValue that will hold the calculated
2909  * intersection value
2910  * @value1: a value to intersect
2911  * @value2: another value to intersect
2912  *
2913  * Calculates the intersection of two values.  If the values have
2914  * a non-empty intersection, the value representing the intersection
2915  * is placed in @dest.  If the intersection is non-empty, @dest is
2916  * not modified.
2917  *
2918  * Returns: TRUE if the intersection is non-empty
2919  */
2920 gboolean
2921 gst_value_intersect (GValue * dest, const GValue * value1,
2922     const GValue * value2)
2923 {
2924   GstValueIntersectInfo *intersect_info;
2925   guint i;
2926   gboolean ret = FALSE;
2927
2928   /* special cases first */
2929   if (GST_VALUE_HOLDS_LIST (value1))
2930     return gst_value_intersect_list (dest, value1, value2);
2931   if (GST_VALUE_HOLDS_LIST (value2))
2932     return gst_value_intersect_list (dest, value2, value1);
2933
2934   for (i = 0; i < gst_value_intersect_funcs->len; i++) {
2935     intersect_info = &g_array_index (gst_value_intersect_funcs,
2936         GstValueIntersectInfo, i);
2937     if (intersect_info->type1 == G_VALUE_TYPE (value1) &&
2938         intersect_info->type2 == G_VALUE_TYPE (value2)) {
2939       ret = intersect_info->func (dest, value1, value2);
2940       return ret;
2941     }
2942     if (intersect_info->type1 == G_VALUE_TYPE (value2) &&
2943         intersect_info->type2 == G_VALUE_TYPE (value1)) {
2944       ret = intersect_info->func (dest, value2, value1);
2945       return ret;
2946     }
2947   }
2948
2949   if (gst_value_compare (value1, value2) == GST_VALUE_EQUAL) {
2950     gst_value_init_and_copy (dest, value1);
2951     ret = TRUE;
2952   }
2953
2954   return ret;
2955 }
2956
2957 /**
2958  * gst_value_register_intersect_func:
2959  * @type1: the first type to intersect
2960  * @type2: the second type to intersect
2961  * @func: the intersection function
2962  *
2963  * Registers a function that is called to calculate the intersection
2964  * of the values having the types @type1 and @type2.
2965  *
2966  * Intersect functions should be registered at startup before any pipelines are
2967  * started, as gst_value_register_intersect_func() is not thread-safe and
2968  * cannot be used at the same time as gst_value_intersect() or
2969  * gst_value_can_intersect().
2970  */
2971 void
2972 gst_value_register_intersect_func (GType type1, GType type2,
2973     GstValueIntersectFunc func)
2974 {
2975   GstValueIntersectInfo intersect_info;
2976
2977   intersect_info.type1 = type1;
2978   intersect_info.type2 = type2;
2979   intersect_info.func = func;
2980
2981   g_array_append_val (gst_value_intersect_funcs, intersect_info);
2982 }
2983
2984
2985 /* subtraction */
2986
2987 /**
2988  * gst_value_subtract:
2989  * @dest: the destination value for the result if the subtraction is not empty
2990  * @minuend: the value to subtract from
2991  * @subtrahend: the value to subtract
2992  *
2993  * Subtracts @subtrahend from @minuend and stores the result in @dest.
2994  * Note that this means subtraction as in sets, not as in mathematics.
2995  *
2996  * Returns: %TRUE if the subtraction is not empty
2997  */
2998 gboolean
2999 gst_value_subtract (GValue * dest, const GValue * minuend,
3000     const GValue * subtrahend)
3001 {
3002   GstValueSubtractInfo *info;
3003   guint i;
3004
3005   /* special cases first */
3006   if (GST_VALUE_HOLDS_LIST (minuend))
3007     return gst_value_subtract_from_list (dest, minuend, subtrahend);
3008   if (GST_VALUE_HOLDS_LIST (subtrahend))
3009     return gst_value_subtract_list (dest, minuend, subtrahend);
3010
3011   for (i = 0; i < gst_value_subtract_funcs->len; i++) {
3012     info = &g_array_index (gst_value_subtract_funcs, GstValueSubtractInfo, i);
3013     if (info->minuend == G_VALUE_TYPE (minuend) &&
3014         info->subtrahend == G_VALUE_TYPE (subtrahend)) {
3015       return info->func (dest, minuend, subtrahend);
3016     }
3017   }
3018
3019   if (gst_value_compare (minuend, subtrahend) != GST_VALUE_EQUAL) {
3020     gst_value_init_and_copy (dest, minuend);
3021     return TRUE;
3022   }
3023
3024   return FALSE;
3025 }
3026
3027 #if 0
3028 gboolean
3029 gst_value_subtract (GValue * dest, const GValue * minuend,
3030     const GValue * subtrahend)
3031 {
3032   gboolean ret = gst_value_subtract2 (dest, minuend, subtrahend);
3033
3034   g_printerr ("\"%s\"  -  \"%s\"  =  \"%s\"\n", gst_value_serialize (minuend),
3035       gst_value_serialize (subtrahend),
3036       ret ? gst_value_serialize (dest) : "---");
3037   return ret;
3038 }
3039 #endif
3040
3041 /**
3042  * gst_value_can_subtract:
3043  * @minuend: the value to subtract from
3044  * @subtrahend: the value to subtract
3045  *
3046  * Checks if it's possible to subtract @subtrahend from @minuend.
3047  *
3048  * Returns: TRUE if a subtraction is possible
3049  */
3050 gboolean
3051 gst_value_can_subtract (const GValue * minuend, const GValue * subtrahend)
3052 {
3053   GstValueSubtractInfo *info;
3054   guint i;
3055
3056   /* special cases */
3057   if (GST_VALUE_HOLDS_LIST (minuend) || GST_VALUE_HOLDS_LIST (subtrahend))
3058     return TRUE;
3059
3060   for (i = 0; i < gst_value_subtract_funcs->len; i++) {
3061     info = &g_array_index (gst_value_subtract_funcs, GstValueSubtractInfo, i);
3062     if (info->minuend == G_VALUE_TYPE (minuend) &&
3063         info->subtrahend == G_VALUE_TYPE (subtrahend))
3064       return TRUE;
3065   }
3066
3067   return gst_value_can_compare (minuend, subtrahend);
3068 }
3069
3070 /**
3071  * gst_value_register_subtract_func:
3072  * @minuend_type: type of the minuend
3073  * @subtrahend_type: type of the subtrahend
3074  * @func: function to use
3075  *
3076  * Registers @func as a function capable of subtracting the values of
3077  * @subtrahend_type from values of @minuend_type.
3078  *
3079  * Subtract functions should be registered at startup before any pipelines are
3080  * started, as gst_value_register_subtract_func() is not thread-safe and
3081  * cannot be used at the same time as gst_value_subtract().
3082  */
3083 void
3084 gst_value_register_subtract_func (GType minuend_type, GType subtrahend_type,
3085     GstValueSubtractFunc func)
3086 {
3087   GstValueSubtractInfo info;
3088
3089   /* one type must be unfixed, other subtractions can be done as comparisons */
3090   g_return_if_fail (!gst_type_is_fixed (minuend_type)
3091       || !gst_type_is_fixed (subtrahend_type));
3092
3093   info.minuend = minuend_type;
3094   info.subtrahend = subtrahend_type;
3095   info.func = func;
3096
3097   g_array_append_val (gst_value_subtract_funcs, info);
3098 }
3099
3100 /**
3101  * gst_value_register:
3102  * @table: structure containing functions to register
3103  *
3104  * Registers functions to perform calculations on #GValues of a given
3105  * type.
3106  */
3107 /**
3108  * GstValueTable:
3109  * @type: GType that the functions operate on.
3110  * @compare: A function that compares two values of this type.
3111  * @serialize: A function that transforms a value of this type to a
3112  * string.  Strings created by this function must be unique and should
3113  * be human readable.
3114  * @deserialize: A function that transforms a string to a value of
3115  * this type.  This function must transform strings created by the
3116  * serialize function back to the original value.  This function may
3117  * optionally transform other strings into values.
3118  */
3119 void
3120 gst_value_register (const GstValueTable * table)
3121 {
3122   g_array_append_val (gst_value_table, *table);
3123 }
3124
3125 /**
3126  * gst_value_init_and_copy:
3127  * @dest: the target value
3128  * @src: the source value
3129  *
3130  * Initialises the target value to be of the same type as source and then copies
3131  * the contents from source to target.
3132  */
3133 void
3134 gst_value_init_and_copy (GValue * dest, const GValue * src)
3135 {
3136   g_value_init (dest, G_VALUE_TYPE (src));
3137   g_value_copy (src, dest);
3138 }
3139
3140 /**
3141  * gst_value_serialize:
3142  * @value: a #GValue to serialize
3143  *
3144  * tries to transform the given @value into a string representation that allows
3145  * getting back this string later on using gst_value_deserialize().
3146  *
3147  * Returns: the serialization for @value or NULL if none exists
3148  */
3149 gchar *
3150 gst_value_serialize (const GValue * value)
3151 {
3152   guint i;
3153   GValue s_val = { 0 };
3154   GstValueTable *table, *best = NULL;
3155   char *s;
3156
3157   g_return_val_if_fail (G_IS_VALUE (value), NULL);
3158
3159   for (i = 0; i < gst_value_table->len; i++) {
3160     table = &g_array_index (gst_value_table, GstValueTable, i);
3161     if (table->serialize == NULL)
3162       continue;
3163     if (table->type == G_VALUE_TYPE (value)) {
3164       best = table;
3165       break;
3166     }
3167     if (g_type_is_a (G_VALUE_TYPE (value), table->type)) {
3168       if (!best || g_type_is_a (table->type, best->type))
3169         best = table;
3170     }
3171   }
3172   if (best)
3173     return best->serialize (value);
3174
3175   g_value_init (&s_val, G_TYPE_STRING);
3176   if (g_value_transform (value, &s_val)) {
3177     s = gst_string_wrap (g_value_get_string (&s_val));
3178   } else {
3179     s = NULL;
3180   }
3181   g_value_unset (&s_val);
3182
3183   return s;
3184 }
3185
3186 /**
3187  * gst_value_deserialize:
3188  * @dest: #GValue to fill with contents of deserialization
3189  * @src: string to deserialize
3190  *
3191  * Tries to deserialize a string into the type specified by the given GValue.
3192  * If the operation succeeds, TRUE is returned, FALSE otherwise.
3193  *
3194  * Returns: TRUE on success
3195  */
3196 gboolean
3197 gst_value_deserialize (GValue * dest, const gchar * src)
3198 {
3199   GstValueTable *table, *best = NULL;
3200   guint i;
3201
3202   g_return_val_if_fail (src != NULL, FALSE);
3203   g_return_val_if_fail (G_IS_VALUE (dest), FALSE);
3204
3205   for (i = 0; i < gst_value_table->len; i++) {
3206     table = &g_array_index (gst_value_table, GstValueTable, i);
3207     if (table->serialize == NULL)
3208       continue;
3209
3210     if (table->type == G_VALUE_TYPE (dest)) {
3211       best = table;
3212       break;
3213     }
3214
3215     if (g_type_is_a (G_VALUE_TYPE (dest), table->type)) {
3216       if (!best || g_type_is_a (table->type, best->type))
3217         best = table;
3218     }
3219   }
3220   if (best) {
3221     return best->deserialize (dest, src);
3222   }
3223
3224   return FALSE;
3225 }
3226
3227 /**
3228  * gst_value_is_fixed:
3229  * @value: the #GValue to check
3230  *
3231  * Tests if the given GValue, if available in a GstStructure (or any other
3232  * container) contains a "fixed" (which means: one value) or an "unfixed"
3233  * (which means: multiple possible values, such as data lists or data
3234  * ranges) value.
3235  *
3236  * Returns: true if the value is "fixed".
3237  */
3238
3239 gboolean
3240 gst_value_is_fixed (const GValue * value)
3241 {
3242   GType type = G_VALUE_TYPE (value);
3243
3244   if (type == GST_TYPE_ARRAY) {
3245     gboolean fixed = TRUE;
3246     gint size, n;
3247     const GValue *kid;
3248
3249     /* check recursively */
3250     size = gst_value_array_get_size (value);
3251     for (n = 0; n < size; n++) {
3252       kid = gst_value_array_get_value (value, n);
3253       fixed &= gst_value_is_fixed (kid);
3254     }
3255
3256     return fixed;
3257   }
3258
3259   return gst_type_is_fixed (type);
3260 }
3261
3262 /************
3263  * fraction *
3264  ************/
3265
3266 /* helper functions */
3267
3268 /* Finds the greatest common divisor.
3269  * Returns 1 if none other found.
3270  * This is Euclid's algorithm. */
3271 static gint
3272 gst_greatest_common_divisor (gint a, gint b)
3273 {
3274   while (b != 0) {
3275     int temp = a;
3276
3277     a = b;
3278     b = temp % b;
3279   }
3280
3281   return ABS (a);
3282 }
3283
3284 static void
3285 gst_value_init_fraction (GValue * value)
3286 {
3287   value->data[0].v_int = 0;
3288   value->data[1].v_int = 1;
3289 }
3290
3291 static void
3292 gst_value_copy_fraction (const GValue * src_value, GValue * dest_value)
3293 {
3294   dest_value->data[0].v_int = src_value->data[0].v_int;
3295   dest_value->data[1].v_int = src_value->data[1].v_int;
3296 }
3297
3298 static gchar *
3299 gst_value_collect_fraction (GValue * value, guint n_collect_values,
3300     GTypeCValue * collect_values, guint collect_flags)
3301 {
3302   gst_value_set_fraction (value,
3303       collect_values[0].v_int, collect_values[1].v_int);
3304
3305   return NULL;
3306 }
3307
3308 static gchar *
3309 gst_value_lcopy_fraction (const GValue * value, guint n_collect_values,
3310     GTypeCValue * collect_values, guint collect_flags)
3311 {
3312   gint *numerator = collect_values[0].v_pointer;
3313   gint *denominator = collect_values[1].v_pointer;
3314
3315   if (!numerator)
3316     return g_strdup_printf ("numerator for `%s' passed as NULL",
3317         G_VALUE_TYPE_NAME (value));
3318   if (!denominator)
3319     return g_strdup_printf ("denominator for `%s' passed as NULL",
3320         G_VALUE_TYPE_NAME (value));
3321
3322   *numerator = value->data[0].v_int;
3323   *denominator = value->data[1].v_int;
3324
3325   return NULL;
3326 }
3327
3328 /**
3329  * gst_value_set_fraction:
3330  * @value: a GValue initialized to #GST_TYPE_FRACTION
3331  * @numerator: the numerator of the fraction
3332  * @denominator: the denominator of the fraction
3333  *
3334  * Sets @value to the fraction specified by @numerator over @denominator.
3335  * The fraction gets reduced to the smallest numerator and denominator,
3336  * and if necessary the sign is moved to the numerator.
3337  */
3338 void
3339 gst_value_set_fraction (GValue * value, gint numerator, gint denominator)
3340 {
3341   gint gcd = 0;
3342
3343   g_return_if_fail (GST_VALUE_HOLDS_FRACTION (value));
3344   g_return_if_fail (denominator != 0);
3345   g_return_if_fail (denominator >= -G_MAXINT);
3346   g_return_if_fail (numerator >= -G_MAXINT);
3347
3348   /* normalize sign */
3349   if (denominator < 0) {
3350     numerator = -numerator;
3351     denominator = -denominator;
3352   }
3353
3354   /* check for reduction */
3355   gcd = gst_greatest_common_divisor (numerator, denominator);
3356   if (gcd) {
3357     numerator /= gcd;
3358     denominator /= gcd;
3359   }
3360
3361   g_assert (denominator > 0);
3362
3363   value->data[0].v_int = numerator;
3364   value->data[1].v_int = denominator;
3365 }
3366
3367 /**
3368  * gst_value_get_fraction_numerator:
3369  * @value: a GValue initialized to #GST_TYPE_FRACTION
3370  *
3371  * Gets the numerator of the fraction specified by @value.
3372  *
3373  * Returns: the numerator of the fraction.
3374  */
3375 gint
3376 gst_value_get_fraction_numerator (const GValue * value)
3377 {
3378   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (value), 0);
3379
3380   return value->data[0].v_int;
3381 }
3382
3383 /**
3384  * gst_value_get_fraction_denominator:
3385  * @value: a GValue initialized to #GST_TYPE_FRACTION
3386  *
3387  * Gets the denominator of the fraction specified by @value.
3388  *
3389  * Returns: the denominator of the fraction.
3390  */
3391 gint
3392 gst_value_get_fraction_denominator (const GValue * value)
3393 {
3394   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (value), 1);
3395
3396   return value->data[1].v_int;
3397 }
3398
3399 /**
3400  * gst_value_fraction_multiply:
3401  * @product: a GValue initialized to #GST_TYPE_FRACTION
3402  * @factor1: a GValue initialized to #GST_TYPE_FRACTION
3403  * @factor2: a GValue initialized to #GST_TYPE_FRACTION
3404  *
3405  * Multiplies the two GValues containing a GstFraction and sets @product
3406  * to the product of the two fractions.
3407  *
3408  * Returns: FALSE in case of an error (like integer overflow), TRUE otherwise.
3409  */
3410 gboolean
3411 gst_value_fraction_multiply (GValue * product, const GValue * factor1,
3412     const GValue * factor2)
3413 {
3414   gint gcd, n1, n2, d1, d2;
3415
3416   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (factor1), FALSE);
3417   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (factor2), FALSE);
3418
3419   n1 = factor1->data[0].v_int;
3420   n2 = factor2->data[0].v_int;
3421   d1 = factor1->data[1].v_int;
3422   d2 = factor2->data[1].v_int;
3423
3424   gcd = gst_greatest_common_divisor (n1, d2);
3425   n1 /= gcd;
3426   d2 /= gcd;
3427   gcd = gst_greatest_common_divisor (n2, d1);
3428   n2 /= gcd;
3429   d1 /= gcd;
3430
3431   g_return_val_if_fail (n1 == 0 || G_MAXINT / ABS (n1) >= ABS (n2), FALSE);
3432   g_return_val_if_fail (G_MAXINT / ABS (d1) >= ABS (d2), FALSE);
3433
3434   gst_value_set_fraction (product, n1 * n2, d1 * d2);
3435
3436   return TRUE;
3437 }
3438
3439 /**
3440  * gst_value_fraction_subtract:
3441  * @dest: a GValue initialized to #GST_TYPE_FRACTION
3442  * @minuend: a GValue initialized to #GST_TYPE_FRACTION
3443  * @subtrahend: a GValue initialized to #GST_TYPE_FRACTION
3444  *
3445  * Subtracts the @subtrahend from the @minuend and sets @dest to the result.
3446  *
3447  * Returns: FALSE in case of an error (like integer overflow), TRUE otherwise.
3448  */
3449 gboolean
3450 gst_value_fraction_subtract (GValue * dest,
3451     const GValue * minuend, const GValue * subtrahend)
3452 {
3453   gint n1, n2, d1, d2;
3454
3455   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (minuend), FALSE);
3456   g_return_val_if_fail (GST_VALUE_HOLDS_FRACTION (subtrahend), FALSE);
3457
3458   n1 = minuend->data[0].v_int;
3459   n2 = subtrahend->data[0].v_int;
3460   d1 = minuend->data[1].v_int;
3461   d2 = subtrahend->data[1].v_int;
3462
3463   if (n1 == 0) {
3464     gst_value_set_fraction (dest, -n2, d2);
3465     return TRUE;
3466   }
3467   if (n2 == 0) {
3468     gst_value_set_fraction (dest, n1, d1);
3469     return TRUE;
3470   }
3471
3472   g_return_val_if_fail (n1 == 0 || G_MAXINT / ABS (n1) >= ABS (d2), FALSE);
3473   g_return_val_if_fail (G_MAXINT / ABS (d1) >= ABS (n2), FALSE);
3474   g_return_val_if_fail (G_MAXINT / ABS (d1) >= ABS (d2), FALSE);
3475
3476   gst_value_set_fraction (dest, (n1 * d2) - (n2 * d1), d1 * d2);
3477
3478   return TRUE;
3479 }
3480
3481 static gchar *
3482 gst_value_serialize_fraction (const GValue * value)
3483 {
3484   gint32 numerator = value->data[0].v_int;
3485   gint32 denominator = value->data[1].v_int;
3486   gboolean positive = TRUE;
3487
3488   /* get the sign and make components absolute */
3489   if (numerator < 0) {
3490     numerator = -numerator;
3491     positive = !positive;
3492   }
3493   if (denominator < 0) {
3494     denominator = -denominator;
3495     positive = !positive;
3496   }
3497
3498   return g_strdup_printf ("%s%d/%d",
3499       positive ? "" : "-", numerator, denominator);
3500 }
3501
3502 static gboolean
3503 gst_value_deserialize_fraction (GValue * dest, const gchar * s)
3504 {
3505   gint num, den;
3506
3507   if (G_UNLIKELY (s == NULL))
3508     return FALSE;
3509
3510   if (G_UNLIKELY (dest == NULL || !GST_VALUE_HOLDS_FRACTION (dest)))
3511     return FALSE;
3512
3513   if (sscanf (s, "%d/%d", &num, &den) == 2) {
3514     gst_value_set_fraction (dest, num, den);
3515     return TRUE;
3516   }
3517   if (sscanf (s, "%d", &num) == 1) {
3518     gst_value_set_fraction (dest, num, 1);
3519     return TRUE;
3520   }
3521   if (g_ascii_strcasecmp (s, "min") == 0) {
3522     gst_value_set_fraction (dest, -G_MAXINT, 1);
3523     return TRUE;
3524   } else if (g_ascii_strcasecmp (s, "max") == 0) {
3525     gst_value_set_fraction (dest, G_MAXINT, 1);
3526     return TRUE;
3527   }
3528
3529   return FALSE;
3530 }
3531
3532 static void
3533 gst_value_transform_fraction_string (const GValue * src_value,
3534     GValue * dest_value)
3535 {
3536   dest_value->data[0].v_pointer = gst_value_serialize_fraction (src_value);
3537 }
3538
3539 static void
3540 gst_value_transform_string_fraction (const GValue * src_value,
3541     GValue * dest_value)
3542 {
3543   if (!gst_value_deserialize_fraction (dest_value,
3544           src_value->data[0].v_pointer))
3545     /* If the deserialize fails, ensure we leave the fraction in a
3546      * valid, if incorrect, state */
3547     gst_value_set_fraction (dest_value, 0, 1);
3548 }
3549
3550 #define MAX_TERMS       30
3551 #define MIN_DIVISOR     1.0e-10
3552 #define MAX_ERROR       1.0e-20
3553
3554 /* use continued fractions to transform a double into a fraction,
3555  * see http://mathforum.org/dr.math/faq/faq.fractions.html#decfrac.
3556  * This algorithm takes care of overflows.
3557  */
3558 static void
3559 gst_value_transform_double_fraction (const GValue * src_value,
3560     GValue * dest_value)
3561 {
3562   gdouble V, F;                 /* double being converted */
3563   gint N, D;                    /* will contain the result */
3564   gint A;                       /* current term in continued fraction */
3565   gint64 N1, D1;                /* numerator, denominator of last approx */
3566   gint64 N2, D2;                /* numerator, denominator of previous approx */
3567   gint i;
3568   gboolean negative = FALSE;
3569
3570   /* initialize fraction being converted */
3571   F = src_value->data[0].v_double;
3572   if (F < 0.0) {
3573     F = -F;
3574     negative = TRUE;
3575   }
3576
3577   V = F;
3578   /* initialize fractions with 1/0, 0/1 */
3579   N1 = 1;
3580   D1 = 0;
3581   N2 = 0;
3582   D2 = 1;
3583   N = 1;
3584   D = 1;
3585
3586   for (i = 0; i < MAX_TERMS; i++) {
3587     /* get next term */
3588     A = (gint) F;               /* no floor() needed, F is always >= 0 */
3589     /* get new divisor */
3590     F = F - A;
3591
3592     /* calculate new fraction in temp */
3593     N2 = N1 * A + N2;
3594     D2 = D1 * A + D2;
3595
3596     /* guard against overflow */
3597     if (N2 > G_MAXINT || D2 > G_MAXINT) {
3598       break;
3599     }
3600
3601     N = N2;
3602     D = D2;
3603
3604     /* save last two fractions */
3605     N2 = N1;
3606     D2 = D1;
3607     N1 = N;
3608     D1 = D;
3609
3610     /* quit if dividing by zero or close enough to target */
3611     if (F < MIN_DIVISOR || fabs (V - ((gdouble) N) / D) < MAX_ERROR) {
3612       break;
3613     }
3614
3615     /* Take reciprocal */
3616     F = 1 / F;
3617   }
3618   /* fix for overflow */
3619   if (D == 0) {
3620     N = G_MAXINT;
3621     D = 1;
3622   }
3623   /* fix for negative */
3624   if (negative)
3625     N = -N;
3626
3627   /* will also simplify */
3628   gst_value_set_fraction (dest_value, N, D);
3629 }
3630
3631 static void
3632 gst_value_transform_fraction_double (const GValue * src_value,
3633     GValue * dest_value)
3634 {
3635   dest_value->data[0].v_double = ((double) src_value->data[0].v_int) /
3636       ((double) src_value->data[1].v_int);
3637 }
3638
3639 static gint
3640 gst_value_compare_fraction (const GValue * value1, const GValue * value2)
3641 {
3642   gint n1, n2;
3643   gint d1, d2;
3644
3645   gint64 new_num_1;
3646   gint64 new_num_2;
3647
3648   n1 = value1->data[0].v_int;
3649   n2 = value2->data[0].v_int;
3650   d1 = value1->data[1].v_int;
3651   d2 = value2->data[1].v_int;
3652
3653   /* fractions are reduced when set, so we can quickly see if they're equal */
3654   if (n1 == n2 && d1 == d2)
3655     return GST_VALUE_EQUAL;
3656
3657   /* extend to 64 bits */
3658   new_num_1 = ((gint64) n1) * d2;
3659   new_num_2 = ((gint64) n2) * d1;
3660   if (new_num_1 < new_num_2)
3661     return GST_VALUE_LESS_THAN;
3662   if (new_num_1 > new_num_2)
3663     return GST_VALUE_GREATER_THAN;
3664
3665   /* new_num_1 == new_num_2 implies that both denominators must have 
3666    * been 0, beause otherwise simplification would have caught the
3667    * equivalence */
3668   g_assert_not_reached ();
3669   return GST_VALUE_UNORDERED;
3670 }
3671
3672 /*********
3673  * GDate *
3674  *********/
3675
3676 /**
3677  * gst_value_set_date:
3678  * @value: a GValue initialized to GST_TYPE_DATE
3679  * @date: the date to set the value to
3680  *
3681  * Sets the contents of @value to coorespond to @date.  The actual
3682  * #GDate structure is copied before it is used.
3683  */
3684 void
3685 gst_value_set_date (GValue * value, const GDate * date)
3686 {
3687   g_return_if_fail (G_VALUE_TYPE (value) == GST_TYPE_DATE);
3688
3689   g_value_set_boxed (value, date);
3690 }
3691
3692 /**
3693  * gst_value_get_date:
3694  * @value: a GValue initialized to GST_TYPE_DATE
3695  *
3696  * Gets the contents of @value.
3697  *
3698  * Returns: the contents of @value
3699  */
3700 const GDate *
3701 gst_value_get_date (const GValue * value)
3702 {
3703   g_return_val_if_fail (G_VALUE_TYPE (value) == GST_TYPE_DATE, NULL);
3704
3705   return (const GDate *) g_value_get_boxed (value);
3706 }
3707
3708 static gpointer
3709 gst_date_copy (gpointer boxed)
3710 {
3711   const GDate *date = (const GDate *) boxed;
3712
3713   return g_date_new_julian (g_date_get_julian (date));
3714 }
3715
3716 static gint
3717 gst_value_compare_date (const GValue * value1, const GValue * value2)
3718 {
3719   const GDate *date1 = (const GDate *) g_value_get_boxed (value1);
3720   const GDate *date2 = (const GDate *) g_value_get_boxed (value2);
3721   guint32 j1, j2;
3722
3723   if (date1 == date2)
3724     return GST_VALUE_EQUAL;
3725
3726   if ((date1 == NULL || !g_date_valid (date1))
3727       && (date2 != NULL && g_date_valid (date2))) {
3728     return GST_VALUE_LESS_THAN;
3729   }
3730
3731   if ((date2 == NULL || !g_date_valid (date2))
3732       && (date1 != NULL && g_date_valid (date1))) {
3733     return GST_VALUE_GREATER_THAN;
3734   }
3735
3736   if (date1 == NULL || date2 == NULL || !g_date_valid (date1)
3737       || !g_date_valid (date2)) {
3738     return GST_VALUE_UNORDERED;
3739   }
3740
3741   j1 = g_date_get_julian (date1);
3742   j2 = g_date_get_julian (date2);
3743
3744   if (j1 == j2)
3745     return GST_VALUE_EQUAL;
3746   else if (j1 < j2)
3747     return GST_VALUE_LESS_THAN;
3748   else
3749     return GST_VALUE_GREATER_THAN;
3750 }
3751
3752 static gchar *
3753 gst_value_serialize_date (const GValue * val)
3754 {
3755   const GDate *date = (const GDate *) g_value_get_boxed (val);
3756
3757   if (date == NULL || !g_date_valid (date))
3758     return g_strdup ("9999-99-99");
3759
3760   return g_strdup_printf ("%04u-%02u-%02u", g_date_get_year (date),
3761       g_date_get_month (date), g_date_get_day (date));
3762 }
3763
3764 static gboolean
3765 gst_value_deserialize_date (GValue * dest, const char *s)
3766 {
3767   guint year, month, day;
3768
3769   if (!s || sscanf (s, "%04u-%02u-%02u", &year, &month, &day) != 3)
3770     return FALSE;
3771
3772   if (!g_date_valid_dmy (day, month, year))
3773     return FALSE;
3774
3775   g_value_take_boxed (dest, g_date_new_dmy (day, month, year));
3776   return TRUE;
3777 }
3778
3779 static void
3780 gst_value_transform_date_string (const GValue * src_value, GValue * dest_value)
3781 {
3782   dest_value->data[0].v_pointer = gst_value_serialize_date (src_value);
3783 }
3784
3785 static void
3786 gst_value_transform_string_date (const GValue * src_value, GValue * dest_value)
3787 {
3788   gst_value_deserialize_date (dest_value, src_value->data[0].v_pointer);
3789 }
3790
3791 static GTypeInfo _info = {
3792   0,
3793   NULL,
3794   NULL,
3795   NULL,
3796   NULL,
3797   NULL,
3798   0,
3799   0,
3800   NULL,
3801   NULL,
3802 };
3803
3804 static GTypeFundamentalInfo _finfo = {
3805   0
3806 };
3807
3808 #define FUNC_VALUE_GET_TYPE(type, name)                         \
3809 GType gst_ ## type ## _get_type (void)                          \
3810 {                                                               \
3811   static GType gst_ ## type ## _type = 0;                       \
3812                                                                 \
3813   if (G_UNLIKELY (gst_ ## type ## _type == 0)) {                \
3814     _info.value_table = & _gst_ ## type ## _value_table;        \
3815     gst_ ## type ## _type = g_type_register_fundamental (       \
3816         g_type_fundamental_next (),                             \
3817         name, &_info, &_finfo, 0);                              \
3818   }                                                             \
3819                                                                 \
3820   return gst_ ## type ## _type;                                 \
3821 }
3822
3823 static const GTypeValueTable _gst_fourcc_value_table = {
3824   gst_value_init_fourcc,
3825   NULL,
3826   gst_value_copy_fourcc,
3827   NULL,
3828   "i",
3829   gst_value_collect_fourcc,
3830   "p",
3831   gst_value_lcopy_fourcc
3832 };
3833
3834 FUNC_VALUE_GET_TYPE (fourcc, "GstFourcc");
3835
3836 static const GTypeValueTable _gst_int_range_value_table = {
3837   gst_value_init_int_range,
3838   NULL,
3839   gst_value_copy_int_range,
3840   NULL,
3841   "ii",
3842   gst_value_collect_int_range,
3843   "pp",
3844   gst_value_lcopy_int_range
3845 };
3846
3847 FUNC_VALUE_GET_TYPE (int_range, "GstIntRange");
3848
3849 static const GTypeValueTable _gst_double_range_value_table = {
3850   gst_value_init_double_range,
3851   NULL,
3852   gst_value_copy_double_range,
3853   NULL,
3854   "dd",
3855   gst_value_collect_double_range,
3856   "pp",
3857   gst_value_lcopy_double_range
3858 };
3859
3860 FUNC_VALUE_GET_TYPE (double_range, "GstDoubleRange");
3861
3862 static const GTypeValueTable _gst_fraction_range_value_table = {
3863   gst_value_init_fraction_range,
3864   gst_value_free_fraction_range,
3865   gst_value_copy_fraction_range,
3866   NULL,
3867   "iiii",
3868   gst_value_collect_fraction_range,
3869   "pppp",
3870   gst_value_lcopy_fraction_range
3871 };
3872
3873 FUNC_VALUE_GET_TYPE (fraction_range, "GstFractionRange");
3874
3875 static const GTypeValueTable _gst_value_list_value_table = {
3876   gst_value_init_list_or_array,
3877   gst_value_free_list_or_array,
3878   gst_value_copy_list_or_array,
3879   gst_value_list_or_array_peek_pointer,
3880   "p",
3881   gst_value_collect_list_or_array,
3882   "p",
3883   gst_value_lcopy_list_or_array
3884 };
3885
3886 FUNC_VALUE_GET_TYPE (value_list, "GstValueList");
3887
3888 static const GTypeValueTable _gst_value_array_value_table = {
3889   gst_value_init_list_or_array,
3890   gst_value_free_list_or_array,
3891   gst_value_copy_list_or_array,
3892   gst_value_list_or_array_peek_pointer,
3893   "p",
3894   gst_value_collect_list_or_array,
3895   "p",
3896   gst_value_lcopy_list_or_array
3897 };
3898
3899 FUNC_VALUE_GET_TYPE (value_array, "GstValueArray");
3900
3901 static const GTypeValueTable _gst_fraction_value_table = {
3902   gst_value_init_fraction,
3903   NULL,
3904   gst_value_copy_fraction,
3905   NULL,
3906   "ii",
3907   gst_value_collect_fraction,
3908   "pp",
3909   gst_value_lcopy_fraction
3910 };
3911
3912 FUNC_VALUE_GET_TYPE (fraction, "GstFraction");
3913
3914
3915 GType
3916 gst_date_get_type (void)
3917 {
3918   static GType gst_date_type = 0;
3919
3920   if (G_UNLIKELY (gst_date_type == 0)) {
3921     /* Not using G_TYPE_DATE here on purpose, even if we could
3922      * if GLIB_CHECK_VERSION(2,8,0) was true: we don't want the
3923      * serialised strings to have different type strings depending
3924      * on what version is used, so FIXME when we
3925      * require GLib-2.8 */
3926     gst_date_type = g_boxed_type_register_static ("GstDate",
3927         (GBoxedCopyFunc) gst_date_copy, (GBoxedFreeFunc) g_date_free);
3928   }
3929
3930   return gst_date_type;
3931 }
3932
3933 void
3934 _gst_value_initialize (void)
3935 {
3936   //const GTypeFundamentalInfo finfo = { G_TYPE_FLAG_DERIVABLE, };
3937
3938   gst_value_table = g_array_new (FALSE, FALSE, sizeof (GstValueTable));
3939   gst_value_union_funcs = g_array_new (FALSE, FALSE,
3940       sizeof (GstValueUnionInfo));
3941   gst_value_intersect_funcs = g_array_new (FALSE, FALSE,
3942       sizeof (GstValueIntersectInfo));
3943   gst_value_subtract_funcs = g_array_new (FALSE, FALSE,
3944       sizeof (GstValueSubtractInfo));
3945
3946   {
3947     static GstValueTable gst_value = {
3948       0,
3949       gst_value_compare_fourcc,
3950       gst_value_serialize_fourcc,
3951       gst_value_deserialize_fourcc,
3952     };
3953
3954     gst_value.type = gst_fourcc_get_type ();
3955     gst_value_register (&gst_value);
3956   }
3957
3958   {
3959     static GstValueTable gst_value = {
3960       0,
3961       gst_value_compare_int_range,
3962       gst_value_serialize_int_range,
3963       gst_value_deserialize_int_range,
3964     };
3965
3966     gst_value.type = gst_int_range_get_type ();
3967     gst_value_register (&gst_value);
3968   }
3969
3970   {
3971     static GstValueTable gst_value = {
3972       0,
3973       gst_value_compare_double_range,
3974       gst_value_serialize_double_range,
3975       gst_value_deserialize_double_range,
3976     };
3977
3978     gst_value.type = gst_double_range_get_type ();
3979     gst_value_register (&gst_value);
3980   }
3981
3982   {
3983     static GstValueTable gst_value = {
3984       0,
3985       gst_value_compare_fraction_range,
3986       gst_value_serialize_fraction_range,
3987       gst_value_deserialize_fraction_range,
3988     };
3989
3990     gst_value.type = gst_fraction_range_get_type ();
3991     gst_value_register (&gst_value);
3992   }
3993
3994   {
3995     static GstValueTable gst_value = {
3996       0,
3997       gst_value_compare_list,
3998       gst_value_serialize_list,
3999       gst_value_deserialize_list,
4000     };
4001
4002     gst_value.type = gst_value_list_get_type ();
4003     gst_value_register (&gst_value);
4004   }
4005
4006   {
4007     static GstValueTable gst_value = {
4008       0,
4009       gst_value_compare_array,
4010       gst_value_serialize_array,
4011       gst_value_deserialize_array,
4012     };
4013
4014     gst_value.type = gst_value_array_get_type ();;
4015     gst_value_register (&gst_value);
4016   }
4017
4018   {
4019 #if 0
4020     static const GTypeValueTable value_table = {
4021       gst_value_init_buffer,
4022       NULL,
4023       gst_value_copy_buffer,
4024       NULL,
4025       "i",
4026       NULL,                     /*gst_value_collect_buffer, */
4027       "p",
4028       NULL                      /*gst_value_lcopy_buffer */
4029     };
4030 #endif
4031     static GstValueTable gst_value = {
4032       0,
4033       gst_value_compare_buffer,
4034       gst_value_serialize_buffer,
4035       gst_value_deserialize_buffer,
4036     };
4037
4038     gst_value.type = GST_TYPE_BUFFER;
4039     gst_value_register (&gst_value);
4040   }
4041   {
4042     static GstValueTable gst_value = {
4043       0,
4044       gst_value_compare_fraction,
4045       gst_value_serialize_fraction,
4046       gst_value_deserialize_fraction,
4047     };
4048
4049     gst_value.type = gst_fraction_get_type ();
4050     gst_value_register (&gst_value);
4051   }
4052   {
4053     static GstValueTable gst_value = {
4054       0,
4055       NULL,
4056       gst_value_serialize_caps,
4057       gst_value_deserialize_caps,
4058     };
4059
4060     gst_value.type = GST_TYPE_CAPS;
4061     gst_value_register (&gst_value);
4062   }
4063   {
4064     static GstValueTable gst_value = {
4065       0,
4066       gst_value_compare_date,
4067       gst_value_serialize_date,
4068       gst_value_deserialize_date,
4069     };
4070
4071     gst_value.type = gst_date_get_type ();
4072     gst_value_register (&gst_value);
4073   }
4074
4075   REGISTER_SERIALIZATION (G_TYPE_DOUBLE, double);
4076   REGISTER_SERIALIZATION (G_TYPE_FLOAT, float);
4077
4078   REGISTER_SERIALIZATION (G_TYPE_STRING, string);
4079   REGISTER_SERIALIZATION (G_TYPE_BOOLEAN, boolean);
4080   REGISTER_SERIALIZATION (G_TYPE_ENUM, enum);
4081
4082   REGISTER_SERIALIZATION (G_TYPE_FLAGS, flags);
4083
4084   REGISTER_SERIALIZATION (G_TYPE_INT, int);
4085
4086   REGISTER_SERIALIZATION (G_TYPE_INT64, int64);
4087   REGISTER_SERIALIZATION (G_TYPE_LONG, long);
4088
4089   REGISTER_SERIALIZATION (G_TYPE_UINT, uint);
4090   REGISTER_SERIALIZATION (G_TYPE_UINT64, uint64);
4091   REGISTER_SERIALIZATION (G_TYPE_ULONG, ulong);
4092
4093   g_value_register_transform_func (GST_TYPE_FOURCC, G_TYPE_STRING,
4094       gst_value_transform_fourcc_string);
4095   g_value_register_transform_func (GST_TYPE_INT_RANGE, G_TYPE_STRING,
4096       gst_value_transform_int_range_string);
4097   g_value_register_transform_func (GST_TYPE_DOUBLE_RANGE, G_TYPE_STRING,
4098       gst_value_transform_double_range_string);
4099   g_value_register_transform_func (GST_TYPE_FRACTION_RANGE, G_TYPE_STRING,
4100       gst_value_transform_fraction_range_string);
4101   g_value_register_transform_func (GST_TYPE_LIST, G_TYPE_STRING,
4102       gst_value_transform_list_string);
4103   g_value_register_transform_func (GST_TYPE_ARRAY, G_TYPE_STRING,
4104       gst_value_transform_array_string);
4105   g_value_register_transform_func (GST_TYPE_FRACTION, G_TYPE_STRING,
4106       gst_value_transform_fraction_string);
4107   g_value_register_transform_func (G_TYPE_STRING, GST_TYPE_FRACTION,
4108       gst_value_transform_string_fraction);
4109   g_value_register_transform_func (GST_TYPE_FRACTION, G_TYPE_DOUBLE,
4110       gst_value_transform_fraction_double);
4111   g_value_register_transform_func (G_TYPE_DOUBLE, GST_TYPE_FRACTION,
4112       gst_value_transform_double_fraction);
4113   g_value_register_transform_func (GST_TYPE_DATE, G_TYPE_STRING,
4114       gst_value_transform_date_string);
4115   g_value_register_transform_func (G_TYPE_STRING, GST_TYPE_DATE,
4116       gst_value_transform_string_date);
4117
4118   gst_value_register_intersect_func (G_TYPE_INT, GST_TYPE_INT_RANGE,
4119       gst_value_intersect_int_int_range);
4120   gst_value_register_intersect_func (GST_TYPE_INT_RANGE, GST_TYPE_INT_RANGE,
4121       gst_value_intersect_int_range_int_range);
4122   gst_value_register_intersect_func (G_TYPE_DOUBLE, GST_TYPE_DOUBLE_RANGE,
4123       gst_value_intersect_double_double_range);
4124   gst_value_register_intersect_func (GST_TYPE_DOUBLE_RANGE,
4125       GST_TYPE_DOUBLE_RANGE, gst_value_intersect_double_range_double_range);
4126   gst_value_register_intersect_func (GST_TYPE_ARRAY,
4127       GST_TYPE_ARRAY, gst_value_intersect_array);
4128   gst_value_register_intersect_func (GST_TYPE_FRACTION, GST_TYPE_FRACTION_RANGE,
4129       gst_value_intersect_fraction_fraction_range);
4130   gst_value_register_intersect_func (GST_TYPE_FRACTION_RANGE,
4131       GST_TYPE_FRACTION_RANGE,
4132       gst_value_intersect_fraction_range_fraction_range);
4133
4134   gst_value_register_subtract_func (G_TYPE_INT, GST_TYPE_INT_RANGE,
4135       gst_value_subtract_int_int_range);
4136   gst_value_register_subtract_func (GST_TYPE_INT_RANGE, G_TYPE_INT,
4137       gst_value_subtract_int_range_int);
4138   gst_value_register_subtract_func (GST_TYPE_INT_RANGE, GST_TYPE_INT_RANGE,
4139       gst_value_subtract_int_range_int_range);
4140   gst_value_register_subtract_func (G_TYPE_DOUBLE, GST_TYPE_DOUBLE_RANGE,
4141       gst_value_subtract_double_double_range);
4142   gst_value_register_subtract_func (GST_TYPE_DOUBLE_RANGE, G_TYPE_DOUBLE,
4143       gst_value_subtract_double_range_double);
4144   gst_value_register_subtract_func (GST_TYPE_DOUBLE_RANGE,
4145       GST_TYPE_DOUBLE_RANGE, gst_value_subtract_double_range_double_range);
4146
4147   gst_value_register_subtract_func (GST_TYPE_FRACTION, GST_TYPE_FRACTION_RANGE,
4148       gst_value_subtract_fraction_fraction_range);
4149   gst_value_register_subtract_func (GST_TYPE_FRACTION_RANGE, GST_TYPE_FRACTION,
4150       gst_value_subtract_fraction_range_fraction);
4151   gst_value_register_subtract_func (GST_TYPE_FRACTION_RANGE,
4152       GST_TYPE_FRACTION_RANGE,
4153       gst_value_subtract_fraction_range_fraction_range);
4154
4155   /* see bug #317246, #64994, #65041 */
4156   {
4157     volatile GType date_type = G_TYPE_DATE;
4158
4159     g_type_name (date_type);
4160   }
4161
4162   gst_value_register_union_func (G_TYPE_INT, GST_TYPE_INT_RANGE,
4163       gst_value_union_int_int_range);
4164   gst_value_register_union_func (GST_TYPE_INT_RANGE, GST_TYPE_INT_RANGE,
4165       gst_value_union_int_range_int_range);
4166
4167 #if 0
4168   /* Implement these if needed */
4169   gst_value_register_union_func (GST_TYPE_FRACTION, GST_TYPE_FRACTION_RANGE,
4170       gst_value_union_fraction_fraction_range);
4171   gst_value_register_union_func (GST_TYPE_FRACTION_RANGE,
4172       GST_TYPE_FRACTION_RANGE, gst_value_union_fraction_range_fraction_range);
4173 #endif
4174 }