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