gststructure.c 97.1 KB
Newer Older
David Schleef's avatar
David Schleef committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* GStreamer
 * Copyright (C) 2003 David A. Schleef <ds@schleef.org>
 *
 * gststructure.c: lists of { GQuark, GValue } tuples
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
18
19
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
David Schleef's avatar
David Schleef committed
20
 */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
21

Stefan Kost's avatar
Stefan Kost committed
22
23
/**
 * SECTION:gststructure
24
 * @title: GstStructure
Stefan Kost's avatar
Stefan Kost committed
25
 * @short_description: Generic structure containing fields of names and values
26
 * @see_also: #GstCaps, #GstMessage, #GstEvent, #GstQuery
Stefan Kost's avatar
Stefan Kost committed
27
 *
28
29
 * A #GstStructure is a collection of key/value pairs. The keys are expressed as
 * GQuarks and the values can be of any GType.
30
 *
31
 * In addition to the key/value pairs, a #GstStructure also has a name. The name
32
33
 * starts with a letter and can be filled by letters, numbers and any of
 * "/-_.:".
34
 *
35
36
 * #GstStructure is used by various GStreamer subsystems to store information in
 * a flexible and extensible way. A #GstStructure does not have a refcount
Wim Taymans's avatar
Wim Taymans committed
37
38
39
40
 * because it usually is part of a higher level object such as #GstCaps,
 * #GstMessage, #GstEvent, #GstQuery. It provides a means to enforce mutability
 * using the refcount of the parent with the gst_structure_set_parent_refcount()
 * method.
41
 *
Wim Taymans's avatar
Wim Taymans committed
42
 * A #GstStructure can be created with gst_structure_new_empty() or
43
44
 * gst_structure_new(), which both take a name and an optional set of key/value
 * pairs along with the types of the values.
45
 *
46
47
 * Field values can be changed with gst_structure_set_value() or
 * gst_structure_set().
48
 *
49
50
 * Field values can be retrieved with gst_structure_get_value() or the more
 * convenient gst_structure_get_*() functions.
51
 *
52
53
 * Fields can be removed with gst_structure_remove_field() or
 * gst_structure_remove_fields().
54
 *
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
 * Strings in structures must be ASCII or UTF-8 encoded. Other encodings are not
 * allowed. Strings may be %NULL however.
 *
 * ## The serialization format
 *
 * GstStructure serialization format serialize the GstStructure name,
 * keys/GType/values in a comma separated list with the structure name as first
 * field without value followed by separated key/value pairs in the form
 * `key=value`, for example:
 *
 * ```
 * a-structure, key=value
 * ````
 *
 * The values type will be inferred if not explicitly specified with the
 * `(GTypeName)value` syntax, for example the following struct will have one
 * field called 'is-string' which has the string 'true' as a value:
 *
 * ```
 * a-struct, field-is-string=(string)true, field-is-boolean=true
 * ```
 *
 * *Note*: without specifying `(string), `field-is-string` type would have been
 * inferred as boolean.
 *
 * *Note*: we specified `(string)` as a type even if `gchararray` is the actual
 * GType name as for convenience some well known types have been aliased or
 * abbreviated.
 *
 * To avoid specifying the type, you can give some hints to the "type system".
 * For example to specify a value as a double, you should add a decimal (ie. `1`
 * is an `int` while `1.0` is a `double`).
 *
 * *Note*: when a structure is serialized with #gst_structure_to_string, all
 * values are explicitly typed.
 *
 * Some types have special delimiters:
 *
 * - [GstValueArray](GST_TYPE_ARRAY) are inside curly brackets (`{` and `}`).
 *   For example `a-structure, array={1, 2, 3}`
 * - Ranges are inside brackets (`[` and `]`). For example `a-structure,
 *   range=[1, 6, 2]` 1 being the min value, 6 the maximum and 2 the step. To
 *   specify a #GST_TYPE_INT64_RANGE you need to explicitly specify it like:
 *   `a-structure, a-int64-range=(gint64) [1, 5]`
 * - [GstValueList](GST_TYPE_LIST) are inside "less and greater than" (`<` and
 *   `>`). For example `a-structure, list=<1, 2, 3>
 *
 * Structures are delimited either by a null character `\0` or a semicolumn `;`
 * the latter allowing to store multiple structures in the same string (see
 * #GstCaps).
 *
 * Quotes are used as "default" delimiters and can be used around any types that
 * don't use other delimiters (for example `a-struct, i=(int)"1"`). They are use
 * to allow adding spaces or special characters (such as delimiters,
 * semicolumns, etc..) inside strings and you can use backslashes `\` to escape
 * characters inside them, for example:
 *
 * ```
 * a-struct, special="\"{[(;)]}\" can be used inside quotes"
 * ```
 *
 * They also allow for nested structure, such as:
 *
 * ```
 * a-struct, nested=(GstStructure)"nested-struct, nested=true"
 * ```
 *
 * > *Note*: Be aware that the current #GstCaps / #GstStructure serialization
 * > into string has limited support for nested #GstCaps / #GstStructure fields.
 * > It can only support one level of nesting. Using more levels will lead to
 * > unexpected behavior when using serialization features, such as
 * > gst_caps_to_string() or gst_value_serialize() and their counterparts.
Stefan Kost's avatar
Stefan Kost committed
127
 */
David Schleef's avatar
David Schleef committed
128
129
130
131
132

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

133
134
135
136
/* FIXME 2.0: suppress warnings for deprecated API such as GValueArray
 * with newer GLib versions (>= 2.31.0) */
#define GLIB_DISABLE_DEPRECATION_WARNINGS

David Schleef's avatar
David Schleef committed
137
138
#include <string.h>

139
#include "gst_private.h"
140
#include "gstquark.h"
David Schleef's avatar
David Schleef committed
141
142
143
#include <gst/gst.h>
#include <gobject/gvaluecollector.h>

Wim Taymans's avatar
Wim Taymans committed
144
145
146
GST_DEBUG_CATEGORY_STATIC (gst_structure_debug);
#define GST_CAT_DEFAULT gst_structure_debug

David Schleef's avatar
David Schleef committed
147
typedef struct _GstStructureField GstStructureField;
148

149
150
struct _GstStructureField
{
David Schleef's avatar
David Schleef committed
151
152
153
154
  GQuark name;
  GValue value;
};

Wim Taymans's avatar
Wim Taymans committed
155
156
157
158
159
160
161
typedef struct
{
  GstStructure s;

  /* owned by parent structure, NULL if no parent */
  gint *parent_refcount;

162
163
164
165
166
167
168
169
  guint fields_len;             /* Number of valid items in fields */
  guint fields_alloc;           /* Allocated items in fields */

  /* Fields are allocated if GST_STRUCTURE_IS_USING_DYNAMIC_ARRAY(),
   *  else it's a pointer to the arr field. */
  GstStructureField *fields;

  GstStructureField arr[1];
Wim Taymans's avatar
Wim Taymans committed
170
171
172
} GstStructureImpl;

#define GST_STRUCTURE_REFCOUNT(s) (((GstStructureImpl*)(s))->parent_refcount)
173
174
175
176
#define GST_STRUCTURE_LEN(s) (((GstStructureImpl*)(s))->fields_len)

#define GST_STRUCTURE_IS_USING_DYNAMIC_ARRAY(s) \
  (((GstStructureImpl*)(s))->fields != &((GstStructureImpl*)(s))->arr[0])
Wim Taymans's avatar
Wim Taymans committed
177

David Schleef's avatar
David Schleef committed
178
#define GST_STRUCTURE_FIELD(structure, index) \
179
  (&((GstStructureImpl*)(structure))->fields[(index)])
David Schleef's avatar
David Schleef committed
180

181
#define IS_MUTABLE(structure) \
Wim Taymans's avatar
Wim Taymans committed
182
183
    (!GST_STRUCTURE_REFCOUNT(structure) || \
     g_atomic_int_get (GST_STRUCTURE_REFCOUNT(structure)) == 1)
184

185
186
187
#define IS_TAGLIST(structure) \
    (structure->name == GST_QUARK (TAGLIST))

188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/* Replacement for g_array_append_val */
static void
_structure_append_val (GstStructure * s, GstStructureField * val)
{
  GstStructureImpl *impl = (GstStructureImpl *) s;

  /* resize if needed */
  if (G_UNLIKELY (impl->fields_len == impl->fields_alloc)) {
    guint want_alloc;

    if (G_UNLIKELY (impl->fields_alloc > (G_MAXUINT / 2)))
      g_error ("Growing structure would result in overflow");

    want_alloc =
        MAX (GST_ROUND_UP_8 (impl->fields_len + 1), impl->fields_alloc * 2);
    if (GST_STRUCTURE_IS_USING_DYNAMIC_ARRAY (s)) {
      impl->fields = g_renew (GstStructureField, impl->fields, want_alloc);
    } else {
      impl->fields = g_new0 (GstStructureField, want_alloc);
      memcpy (impl->fields, &impl->arr[0],
          impl->fields_len * sizeof (GstStructureField));
      GST_CAT_LOG (GST_CAT_PERFORMANCE, "Exceeding pre-allocated array");
    }
    impl->fields_alloc = want_alloc;
  }

  /* Finally set value */
  impl->fields[impl->fields_len++] = *val;
}

/* Replacement for g_array_remove_index */
static inline void
_structure_remove_index (GstStructure * s, guint idx)
{
  GstStructureImpl *impl = (GstStructureImpl *) s;

  /* We never "reduce" the memory footprint. */
  if (idx >= impl->fields_len)
    return;

  /* Shift everything if it's not the last item */
  if (idx != impl->fields_len)
    memmove (&impl->fields[idx],
        &impl->fields[idx + 1],
        (impl->fields_len - idx - 1) * sizeof (GstStructureField));
  impl->fields_len--;
}

236
237
238
239
240
241
242
243
244
245
static void gst_structure_set_field (GstStructure * structure,
    GstStructureField * field);
static GstStructureField *gst_structure_get_field (const GstStructure *
    structure, const gchar * fieldname);
static GstStructureField *gst_structure_id_get_field (const GstStructure *
    structure, GQuark field);
static void gst_structure_transform_to_string (const GValue * src_value,
    GValue * dest_value);
static GstStructure *gst_structure_copy_conditional (const GstStructure *
    structure);
David Schleef's avatar
David Schleef committed
246

Wim Taymans's avatar
Wim Taymans committed
247
GType _gst_structure_type = 0;
248

Johan Dahlin's avatar
Johan Dahlin committed
249
250
251
252

G_DEFINE_BOXED_TYPE (GstStructure, gst_structure,
    gst_structure_copy_conditional, gst_structure_free);

Wim Taymans's avatar
Wim Taymans committed
253
void
254
_priv_gst_structure_initialize (void)
Wim Taymans's avatar
Wim Taymans committed
255
{
Johan Dahlin's avatar
Johan Dahlin committed
256
  _gst_structure_type = gst_structure_get_type ();
257

Wim Taymans's avatar
Wim Taymans committed
258
259
  g_value_register_transform_func (_gst_structure_type, G_TYPE_STRING,
      gst_structure_transform_to_string);
Wim Taymans's avatar
Wim Taymans committed
260
261
262

  GST_DEBUG_CATEGORY_INIT (gst_structure_debug, "structure", 0,
      "GstStructure debug");
David Schleef's avatar
David Schleef committed
263
264
}

265
static GstStructure *
Wim Taymans's avatar
Wim Taymans committed
266
gst_structure_new_id_empty_with_size (GQuark quark, guint prealloc)
267
{
268
  guint n_alloc;
Wim Taymans's avatar
Wim Taymans committed
269
  GstStructureImpl *structure;
270

271
272
273
274
275
276
277
278
  if (prealloc == 0)
    prealloc = 1;

  n_alloc = GST_ROUND_UP_8 (prealloc);
  structure =
      g_malloc0 (sizeof (GstStructureImpl) + (n_alloc -
          1) * sizeof (GstStructureField));

Wim Taymans's avatar
Wim Taymans committed
279
280
281
  ((GstStructure *) structure)->type = _gst_structure_type;
  ((GstStructure *) structure)->name = quark;
  GST_STRUCTURE_REFCOUNT (structure) = NULL;
282
283
284
285

  structure->fields_len = 0;
  structure->fields_alloc = n_alloc;
  structure->fields = &structure->arr[0];
286

Wim Taymans's avatar
Wim Taymans committed
287
288
  GST_TRACE ("created structure %p", structure);

Wim Taymans's avatar
Wim Taymans committed
289
  return GST_STRUCTURE_CAST (structure);
290
291
}

Benjamin Otte's avatar
Benjamin Otte committed
292
/**
Wim Taymans's avatar
Wim Taymans committed
293
 * gst_structure_new_id_empty:
294
 * @quark: name of new structure
Benjamin Otte's avatar
Benjamin Otte committed
295
 *
296
 * Creates a new, empty #GstStructure with the given name as a GQuark.
Benjamin Otte's avatar
Benjamin Otte committed
297
 *
298
299
300
 * Free-function: gst_structure_free
 *
 * Returns: (transfer full): a new, empty #GstStructure
Benjamin Otte's avatar
Benjamin Otte committed
301
 */
302
GstStructure *
Wim Taymans's avatar
Wim Taymans committed
303
gst_structure_new_id_empty (GQuark quark)
Benjamin Otte's avatar
Benjamin Otte committed
304
{
305
  g_return_val_if_fail (quark != 0, NULL);
Benjamin Otte's avatar
Benjamin Otte committed
306

Wim Taymans's avatar
Wim Taymans committed
307
  return gst_structure_new_id_empty_with_size (quark, 0);
Benjamin Otte's avatar
Benjamin Otte committed
308
309
}

310
#ifndef G_DISABLE_CHECKS
311
312
313
314
315
316
317
static gboolean
gst_structure_validate_name (const gchar * name)
{
  const gchar *s;

  g_return_val_if_fail (name != NULL, FALSE);

Wim Taymans's avatar
Wim Taymans committed
318
  if (G_UNLIKELY (!g_ascii_isalpha (*name))) {
319
320
321
322
323
324
325
    GST_WARNING ("Invalid character '%c' at offset 0 in structure name: %s",
        *name, name);
    return FALSE;
  }

  /* FIXME: test name string more */
  s = &name[1];
326
  while (*s && (g_ascii_isalnum (*s) || strchr ("/-_.:+", *s) != NULL))
327
    s++;
328
  if (G_UNLIKELY (*s != '\0')) {
329
330
    GST_WARNING ("Invalid character '%c' at offset %" G_GUINTPTR_FORMAT " in"
        " structure name: %s", *s, ((guintptr) s - (guintptr) name), name);
331
332
333
    return FALSE;
  }

334
335
336
337
338
339
340
341
  if (strncmp (name, "video/x-raw-", 12) == 0) {
    g_warning ("0.10-style raw video caps are being created. Should be "
        "video/x-raw,format=(string).. now.");
  } else if (strncmp (name, "audio/x-raw-", 12) == 0) {
    g_warning ("0.10-style raw audio caps are being created. Should be "
        "audio/x-raw,format=(string).. now.");
  }

342
343
  return TRUE;
}
344
#endif
345

David Schleef's avatar
David Schleef committed
346
/**
Wim Taymans's avatar
Wim Taymans committed
347
 * gst_structure_new_empty:
David Schleef's avatar
David Schleef committed
348
349
 * @name: name of new structure
 *
350
351
352
 * Creates a new, empty #GstStructure with the given @name.
 *
 * See gst_structure_set_name() for constraints on the @name parameter.
David Schleef's avatar
David Schleef committed
353
 *
354
355
356
 * Free-function: gst_structure_free
 *
 * Returns: (transfer full): a new, empty #GstStructure
David Schleef's avatar
David Schleef committed
357
 */
358
GstStructure *
Wim Taymans's avatar
Wim Taymans committed
359
gst_structure_new_empty (const gchar * name)
David Schleef's avatar
David Schleef committed
360
{
361
  g_return_val_if_fail (gst_structure_validate_name (name), NULL);
David Schleef's avatar
David Schleef committed
362

Wim Taymans's avatar
Wim Taymans committed
363
  return gst_structure_new_id_empty_with_size (g_quark_from_string (name), 0);
David Schleef's avatar
David Schleef committed
364
365
366
367
368
369
370
371
372
373
374
}

/**
 * gst_structure_new:
 * @name: name of new structure
 * @firstfield: name of first field to set
 * @...: additional arguments
 *
 * Creates a new #GstStructure with the given name.  Parses the
 * list of variable arguments and sets fields to the values listed.
 * Variable arguments should be passed as field name, field type,
375
 * and value.  Last variable argument should be %NULL.
David Schleef's avatar
David Schleef committed
376
 *
377
378
379
 * Free-function: gst_structure_free
 *
 * Returns: (transfer full): a new #GstStructure
David Schleef's avatar
David Schleef committed
380
 */
381
382
GstStructure *
gst_structure_new (const gchar * name, const gchar * firstfield, ...)
David Schleef's avatar
David Schleef committed
383
384
385
386
{
  GstStructure *structure;
  va_list varargs;

387
388
389
  va_start (varargs, firstfield);
  structure = gst_structure_new_valist (name, firstfield, varargs);
  va_end (varargs);
David Schleef's avatar
David Schleef committed
390
391
392
393
394
395
396
397

  return structure;
}

/**
 * gst_structure_new_valist:
 * @name: name of new structure
 * @firstfield: name of first field to set
398
 * @varargs: variable argument list
David Schleef's avatar
David Schleef committed
399
 *
400
 * Creates a new #GstStructure with the given @name.  Structure fields
David Schleef's avatar
David Schleef committed
401
 * are set according to the varargs in a manner similar to
402
403
404
 * gst_structure_new().
 *
 * See gst_structure_set_name() for constraints on the @name parameter.
David Schleef's avatar
David Schleef committed
405
 *
406
407
408
 * Free-function: gst_structure_free
 *
 * Returns: (transfer full): a new #GstStructure
David Schleef's avatar
David Schleef committed
409
 */
410
GstStructure *
411
412
gst_structure_new_valist (const gchar * name,
    const gchar * firstfield, va_list varargs)
David Schleef's avatar
David Schleef committed
413
414
{
  GstStructure *structure;
415
416
417
418
  va_list copy;
  guint len = 0;
  const gchar *field_copy = firstfield;
  GType type_copy;
David Schleef's avatar
David Schleef committed
419

420
421
422
423
424
425
426
427
428
429
430
431
432
433
  g_return_val_if_fail (gst_structure_validate_name (name), NULL);

  /* Calculate size of varargs */
  va_copy (copy, varargs);
  while (field_copy) {
    type_copy = va_arg (copy, GType);
    G_VALUE_COLLECT_SKIP (type_copy, copy);
    field_copy = va_arg (copy, gchar *);
    len++;
  }
  va_end (copy);

  structure =
      gst_structure_new_id_empty_with_size (g_quark_from_string (name), len);
434
435
436

  if (structure)
    gst_structure_set_valist (structure, firstfield, varargs);
David Schleef's avatar
David Schleef committed
437
438
439
440

  return structure;
}

441
442
443
/**
 * gst_structure_set_parent_refcount:
 * @structure: a #GstStructure
444
 * @refcount: (in): a pointer to the parent's refcount
445
446
447
 *
 * Sets the parent_refcount field of #GstStructure. This field is used to
 * determine whether a structure is mutable or not. This function should only be
448
 * called by code implementing parent objects of #GstStructure, as described in
449
 * the MT Refcounting section of the design documents.
Wim Taymans's avatar
Wim Taymans committed
450
451
 *
 * Returns: %TRUE if the parent refcount could be set.
452
 */
Wim Taymans's avatar
Wim Taymans committed
453
gboolean
454
gst_structure_set_parent_refcount (GstStructure * structure, gint * refcount)
455
{
Wim Taymans's avatar
Wim Taymans committed
456
  g_return_val_if_fail (structure != NULL, FALSE);
457
458
459

  /* if we have a parent_refcount already, we can only clear
   * if with a NULL refcount */
Wim Taymans's avatar
Wim Taymans committed
460
461
462
463
464
465
466
467
468
469
470
  if (GST_STRUCTURE_REFCOUNT (structure)) {
    if (refcount != NULL) {
      g_return_val_if_fail (refcount == NULL, FALSE);
      return FALSE;
    }
  } else {
    if (refcount == NULL) {
      g_return_val_if_fail (refcount != NULL, FALSE);
      return FALSE;
    }
  }
471

Wim Taymans's avatar
Wim Taymans committed
472
473
474
  GST_STRUCTURE_REFCOUNT (structure) = refcount;

  return TRUE;
475
476
}

David Schleef's avatar
David Schleef committed
477
/**
478
 * gst_structure_copy:
David Schleef's avatar
David Schleef committed
479
480
481
482
 * @structure: a #GstStructure to duplicate
 *
 * Duplicates a #GstStructure and all its fields and values.
 *
483
484
 * Free-function: gst_structure_free
 *
485
 * Returns: (transfer full): a new #GstStructure.
David Schleef's avatar
David Schleef committed
486
 */
487
GstStructure *
488
gst_structure_copy (const GstStructure * structure)
David Schleef's avatar
David Schleef committed
489
490
491
{
  GstStructure *new_structure;
  GstStructureField *field;
492
  guint i, len;
David Schleef's avatar
David Schleef committed
493

494
  g_return_val_if_fail (structure != NULL, NULL);
David Schleef's avatar
David Schleef committed
495

496
  len = GST_STRUCTURE_LEN (structure);
Wim Taymans's avatar
Wim Taymans committed
497
  new_structure = gst_structure_new_id_empty_with_size (structure->name, len);
498

499
  for (i = 0; i < len; i++) {
David Schleef's avatar
David Schleef committed
500
501
    GstStructureField new_field = { 0 };

502
    field = GST_STRUCTURE_FIELD (structure, i);
David Schleef's avatar
David Schleef committed
503
504

    new_field.name = field->name;
505
    gst_value_init_and_copy (&new_field.value, &field->value);
506
    _structure_append_val (new_structure, &new_field);
David Schleef's avatar
David Schleef committed
507
  }
Wim Taymans's avatar
Wim Taymans committed
508
509
  GST_CAT_TRACE (GST_CAT_PERFORMANCE, "doing copy %p -> %p",
      structure, new_structure);
David Schleef's avatar
David Schleef committed
510

511
  return new_structure;
David Schleef's avatar
David Schleef committed
512
513
514
}

/**
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
515
 * gst_structure_free:
516
 * @structure: (in) (transfer full): the #GstStructure to free
David Schleef's avatar
David Schleef committed
517
 *
518
 * Frees a #GstStructure and all its fields and values. The structure must not
519
 * have a parent when this function is called.
David Schleef's avatar
David Schleef committed
520
 */
521
void
522
gst_structure_free (GstStructure * structure)
David Schleef's avatar
David Schleef committed
523
524
{
  GstStructureField *field;
525
  guint i, len;
David Schleef's avatar
David Schleef committed
526

527
  g_return_if_fail (structure != NULL);
Wim Taymans's avatar
Wim Taymans committed
528
  g_return_if_fail (GST_STRUCTURE_REFCOUNT (structure) == NULL);
David Schleef's avatar
David Schleef committed
529

530
  len = GST_STRUCTURE_LEN (structure);
531
  for (i = 0; i < len; i++) {
532
    field = GST_STRUCTURE_FIELD (structure, i);
David Schleef's avatar
David Schleef committed
533

534
535
    if (G_IS_VALUE (&field->value)) {
      g_value_unset (&field->value);
David Schleef's avatar
David Schleef committed
536
537
    }
  }
538
539
540
  if (GST_STRUCTURE_IS_USING_DYNAMIC_ARRAY (structure))
    g_free (((GstStructureImpl *) structure)->fields);

David Schleef's avatar
David Schleef committed
541
#ifdef USE_POISONING
542
  memset (structure, 0xff, sizeof (GstStructure));
David Schleef's avatar
David Schleef committed
543
#endif
Wim Taymans's avatar
Wim Taymans committed
544
545
  GST_TRACE ("free structure %p", structure);

546
  g_free (structure);
David Schleef's avatar
David Schleef committed
547
548
}

549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
/**
 * gst_clear_structure: (skip)
 * @structure_ptr: a pointer to a #GstStructure reference
 *
 * Clears a reference to a #GstStructure.
 *
 * @structure_ptr must not be %NULL.
 *
 * If the reference is %NULL then this function does nothing.
 * Otherwise, the structure is free'd using gst_structure_free() and the
 * pointer is set to %NULL.
 *
 * A macro is also included that allows this function to be used without
 * pointer casts.
 *
 * Since: 1.16
 **/
#undef gst_clear_structure
void
568
gst_clear_structure (GstStructure ** structure_ptr)
569
570
571
572
{
  g_clear_pointer (structure_ptr, gst_structure_free);
}

573
574
575
576
577
578
/**
 * gst_structure_take:
 * @oldstr_ptr: (inout) (transfer full) (nullable): pointer to a place of
 *     a #GstStructure to take
 * @newstr: (transfer full) (allow-none): a new #GstStructure
 *
579
 * Atomically modifies a pointer to point to a new structure.
580
581
582
583
584
 * The #GstStructure @oldstr_ptr is pointing to is freed and
 * @newstr is taken ownership over.
 *
 * Either @newstr and the value pointed to by @oldstr_ptr may be %NULL.
 *
585
586
587
 * It is a programming error if both @newstr and the value pointed to by
 * @oldstr_ptr refer to the same, non-%NULL structure.
 *
588
589
590
591
592
593
594
595
596
597
598
 * Returns: %TRUE if @newstr was different from @oldstr_ptr
 *
 * Since: 1.18
 */
gboolean
gst_structure_take (GstStructure ** oldstr_ptr, GstStructure * newstr)
{
  GstStructure *oldstr;

  g_return_val_if_fail (oldstr_ptr != NULL, FALSE);

599
  do {
600
    oldstr = g_atomic_pointer_get ((gpointer *) oldstr_ptr);
601
602
603
604
605
    if (G_UNLIKELY (oldstr == newstr)) {
      g_return_val_if_fail (newstr == NULL, FALSE);
      return FALSE;
    }
  } while (G_UNLIKELY (!g_atomic_pointer_compare_and_exchange ((gpointer *)
Jordan Petridіs's avatar
Jordan Petridіs committed
606
              oldstr_ptr, (gpointer) oldstr, newstr)));
607
608
609
610

  if (oldstr)
    gst_structure_free (oldstr);

611
  return TRUE;
612
613
}

David Schleef's avatar
David Schleef committed
614
615
616
617
/**
 * gst_structure_get_name:
 * @structure: a #GstStructure
 *
618
 * Get the name of @structure as a string.
David Schleef's avatar
David Schleef committed
619
620
621
 *
 * Returns: the name of the structure.
 */
622
const gchar *
623
gst_structure_get_name (const GstStructure * structure)
David Schleef's avatar
David Schleef committed
624
{
625
  g_return_val_if_fail (structure != NULL, NULL);
David Schleef's avatar
David Schleef committed
626

627
  return g_quark_to_string (structure->name);
David Schleef's avatar
David Schleef committed
628
629
}

630
/**
631
632
633
634
 * gst_structure_has_name:
 * @structure: a #GstStructure
 * @name: structure name to check for
 *
Wim Taymans's avatar
Wim Taymans committed
635
636
 * Checks if the structure has the given name
 *
637
 * Returns: %TRUE if @name matches the name of the structure.
638
639
640
641
642
643
644
645
646
 */
gboolean
gst_structure_has_name (const GstStructure * structure, const gchar * name)
{
  const gchar *structure_name;

  g_return_val_if_fail (structure != NULL, FALSE);
  g_return_val_if_fail (name != NULL, FALSE);

647
648
649
  /* getting the string is cheap and comparing short strings is too
   * should be faster than getting the quark for name and comparing the quarks
   */
650
651
652
653
654
655
656
  structure_name = g_quark_to_string (structure->name);

  return (structure_name && strcmp (structure_name, name) == 0);
}

/**
 * gst_structure_get_name_id:
657
658
 * @structure: a #GstStructure
 *
659
 * Get the name of @structure as a GQuark.
660
661
662
663
664
665
666
667
668
669
670
 *
 * Returns: the quark representing the name of the structure.
 */
GQuark
gst_structure_get_name_id (const GstStructure * structure)
{
  g_return_val_if_fail (structure != NULL, 0);

  return structure->name;
}

David Schleef's avatar
David Schleef committed
671
672
673
674
675
/**
 * gst_structure_set_name:
 * @structure: a #GstStructure
 * @name: the new name of the structure
 *
676
 * Sets the name of the structure to the given @name.  The string
677
678
 * provided is copied before being used. It must not be empty, start with a
 * letter and can be followed by letters, numbers and any of "/-_.:".
David Schleef's avatar
David Schleef committed
679
 */
680
void
681
gst_structure_set_name (GstStructure * structure, const gchar * name)
David Schleef's avatar
David Schleef committed
682
{
683
  g_return_if_fail (structure != NULL);
684
  g_return_if_fail (IS_MUTABLE (structure));
685
  g_return_if_fail (gst_structure_validate_name (name));
David Schleef's avatar
David Schleef committed
686

687
  structure->name = g_quark_from_string (name);
David Schleef's avatar
David Schleef committed
688
689
}

690
691
692
693
694
695
696
697
698
699
700
701
static inline void
gst_structure_id_set_value_internal (GstStructure * structure, GQuark field,
    const GValue * value)
{
  GstStructureField gsfield = { 0, {0,} };

  gsfield.name = field;
  gst_value_init_and_copy (&gsfield.value, value);

  gst_structure_set_field (structure, &gsfield);
}

David Schleef's avatar
David Schleef committed
702
703
704
/**
 * gst_structure_id_set_value:
 * @structure: a #GstStructure
705
 * @field: a #GQuark representing a field
David Schleef's avatar
David Schleef committed
706
707
 * @value: the new value of the field
 *
708
 * Sets the field with the given GQuark @field to @value.  If the field
David Schleef's avatar
David Schleef committed
709
 * does not exist, it is created.  If the field exists, the previous
710
 * value is replaced and freed.
David Schleef's avatar
David Schleef committed
711
 */
712
void
713
714
gst_structure_id_set_value (GstStructure * structure,
    GQuark field, const GValue * value)
David Schleef's avatar
David Schleef committed
715
716
{

717
718
  g_return_if_fail (structure != NULL);
  g_return_if_fail (G_IS_VALUE (value));
719
  g_return_if_fail (IS_MUTABLE (structure));
David Schleef's avatar
David Schleef committed
720

721
  gst_structure_id_set_value_internal (structure, field, value);
David Schleef's avatar
David Schleef committed
722
723
724
725
726
}

/**
 * gst_structure_set_value:
 * @structure: a #GstStructure
727
 * @fieldname: the name of the field to set
David Schleef's avatar
David Schleef committed
728
729
 * @value: the new value of the field
 *
730
 * Sets the field with the given name @field to @value.  If the field
David Schleef's avatar
David Schleef committed
731
 * does not exist, it is created.  If the field exists, the previous
732
 * value is replaced and freed.
David Schleef's avatar
David Schleef committed
733
 */
734
void
735
736
gst_structure_set_value (GstStructure * structure,
    const gchar * fieldname, const GValue * value)
David Schleef's avatar
David Schleef committed
737
{
738
739
740
  g_return_if_fail (structure != NULL);
  g_return_if_fail (fieldname != NULL);
  g_return_if_fail (G_IS_VALUE (value));
741
  g_return_if_fail (IS_MUTABLE (structure));
David Schleef's avatar
David Schleef committed
742

743
744
  gst_structure_id_set_value_internal (structure,
      g_quark_from_string (fieldname), value);
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
}

static inline void
gst_structure_id_take_value_internal (GstStructure * structure, GQuark field,
    GValue * value)
{
  GstStructureField gsfield = { 0, {0,} };

  gsfield.name = field;
  gsfield.value = *value;

  gst_structure_set_field (structure, &gsfield);

  /* we took ownership */
#ifdef USE_POISONING
  memset (value, 0, sizeof (GValue));
#else
  value->g_type = G_TYPE_INVALID;
#endif
}

/**
 * gst_structure_id_take_value:
 * @structure: a #GstStructure
 * @field: a #GQuark representing a field
 * @value: (transfer full): the new value of the field
 *
 * Sets the field with the given GQuark @field to @value.  If the field
 * does not exist, it is created.  If the field exists, the previous
 * value is replaced and freed.
 */
void
gst_structure_id_take_value (GstStructure * structure, GQuark field,
    GValue * value)
{
  g_return_if_fail (structure != NULL);
  g_return_if_fail (G_IS_VALUE (value));
  g_return_if_fail (IS_MUTABLE (structure));

  gst_structure_id_take_value_internal (structure, field, value);
}

/**
 * gst_structure_take_value:
 * @structure: a #GstStructure
 * @fieldname: the name of the field to set
 * @value: (transfer full): the new value of the field
 *
 * Sets the field with the given name @field to @value.  If the field
 * does not exist, it is created.  If the field exists, the previous
 * value is replaced and freed. The function will take ownership of @value.
 */
void
gst_structure_take_value (GstStructure * structure, const gchar * fieldname,
    GValue * value)
{
  g_return_if_fail (structure != NULL);
  g_return_if_fail (fieldname != NULL);
  g_return_if_fail (G_IS_VALUE (value));
  g_return_if_fail (IS_MUTABLE (structure));

  gst_structure_id_take_value_internal (structure,
      g_quark_from_string (fieldname), value);
David Schleef's avatar
David Schleef committed
808
809
}

810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
static void
gst_structure_set_valist_internal (GstStructure * structure,
    const gchar * fieldname, va_list varargs)
{
  gchar *err = NULL;
  GType type;

  while (fieldname) {
    GstStructureField field = { 0 };

    field.name = g_quark_from_string (fieldname);

    type = va_arg (varargs, GType);

    G_VALUE_COLLECT_INIT (&field.value, type, varargs, 0, &err);
    if (G_UNLIKELY (err)) {
      g_critical ("%s", err);
827
      g_free (err);
828
829
830
831
832
833
834
835
      return;
    }
    gst_structure_set_field (structure, &field);

    fieldname = va_arg (varargs, gchar *);
  }
}

David Schleef's avatar
David Schleef committed
836
837
838
/**
 * gst_structure_set:
 * @structure: a #GstStructure
839
 * @fieldname: the name of the field to set
David Schleef's avatar
David Schleef committed
840
841
 * @...: variable arguments
 *
842
843
 * Parses the variable arguments and sets fields accordingly. Fields that
 * weren't already part of the structure are added as needed.
David Schleef's avatar
David Schleef committed
844
 * Variable arguments should be in the form field name, field type
845
 * (as a GType), value(s).  The last variable argument should be %NULL.
David Schleef's avatar
David Schleef committed
846
 */
847
void
848
gst_structure_set (GstStructure * structure, const gchar * field, ...)
David Schleef's avatar
David Schleef committed
849
850
851
{
  va_list varargs;

852
  g_return_if_fail (structure != NULL);
853
  g_return_if_fail (IS_MUTABLE (structure) || field == NULL);
David Schleef's avatar
David Schleef committed
854

855
  va_start (varargs, field);
856
  gst_structure_set_valist_internal (structure, field, varargs);
857
  va_end (varargs);
David Schleef's avatar
David Schleef committed
858
859
860
}

/**
David Schleef's avatar
David Schleef committed
861
 * gst_structure_set_valist:
David Schleef's avatar
David Schleef committed
862
 * @structure: a #GstStructure
863
 * @fieldname: the name of the field to set
David Schleef's avatar
David Schleef committed
864
865
 * @varargs: variable arguments
 *
866
 * va_list form of gst_structure_set().
David Schleef's avatar
David Schleef committed
867
 */
868
void
869
870
gst_structure_set_valist (GstStructure * structure,
    const gchar * fieldname, va_list varargs)
David Schleef's avatar
David Schleef committed
871
{
872
  g_return_if_fail (structure != NULL);
873
  g_return_if_fail (IS_MUTABLE (structure));
David Schleef's avatar
David Schleef committed
874

875
876
877
878
879
880
881
882
883
884
  gst_structure_set_valist_internal (structure, fieldname, varargs);
}

static void
gst_structure_id_set_valist_internal (GstStructure * structure,
    GQuark fieldname, va_list varargs)
{
  gchar *err = NULL;
  GType type;

885
  while (fieldname) {
David Schleef's avatar
David Schleef committed
886
887
    GstStructureField field = { 0 };

888
    field.name = fieldname;
889
    type = va_arg (varargs, GType);
890

891
    G_VALUE_COLLECT_INIT (&field.value, type, varargs, 0, &err);
892
    if (G_UNLIKELY (err)) {
893
      g_critical ("%s", err);
894
      g_free (err);
895
      return;
David Schleef's avatar
David Schleef committed
896
    }
897
    gst_structure_set_field (structure, &field);
David Schleef's avatar
David Schleef committed
898

899
    fieldname = va_arg (varargs, GQuark);
David Schleef's avatar
David Schleef committed
900
901
902
  }
}

903
904
905
906
907
908
909
910
911
912
/**
 * gst_structure_id_set:
 * @structure: a #GstStructure
 * @fieldname: the GQuark for the name of the field to set
 * @...: variable arguments
 *
 * Identical to gst_structure_set, except that field names are
 * passed using the GQuark for the field name. This allows more efficient
 * setting of the structure if the caller already knows the associated
 * quark values.
913
 * The last variable argument must be %NULL.
914
915
916
917
918
919
920
921
922
 */
void
gst_structure_id_set (GstStructure * structure, GQuark field, ...)
{
  va_list varargs;

  g_return_if_fail (structure != NULL);

  va_start (varargs, field);
923
  gst_structure_id_set_valist_internal (structure, field, varargs);
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
  va_end (varargs);
}

/**
 * gst_structure_id_set_valist:
 * @structure: a #GstStructure
 * @fieldname: the name of the field to set
 * @varargs: variable arguments
 *
 * va_list form of gst_structure_id_set().
 */
void
gst_structure_id_set_valist (GstStructure * structure,
    GQuark fieldname, va_list varargs)
{
  g_return_if_fail (structure != NULL);
  g_return_if_fail (IS_MUTABLE (structure));

942
  gst_structure_id_set_valist_internal (structure, fieldname, varargs);
943
944
}

945
/**
Wim Taymans's avatar
Wim Taymans committed
946
 * gst_structure_new_id:
947
948
949
950
951
952
953
 * @name_quark: name of new structure
 * @field_quark: the GQuark for the name of the field to set
 * @...: variable arguments
 *
 * Creates a new #GstStructure with the given name as a GQuark, followed by
 * fieldname quark, GType, argument(s) "triplets" in the same format as
 * gst_structure_id_set(). Basically a convenience wrapper around
Wim Taymans's avatar
Wim Taymans committed
954
 * gst_structure_new_id_empty() and gst_structure_id_set().
955
 *
956
 * The last variable argument must be %NULL (or 0).
957
 *
958
959
960
 * Free-function: gst_structure_free
 *
 * Returns: (transfer full): a new #GstStructure
961
962
 */
GstStructure *
Wim Taymans's avatar
Wim Taymans committed
963
gst_structure_new_id (GQuark name_quark, GQuark field_quark, ...)
964
965
966
{
  GstStructure *s;
  va_list varargs;
967
968
969
970
  va_list copy;
  guint len = 0;
  GQuark quark_copy = field_quark;
  GType type_copy;
971
972
973
974
975

  g_return_val_if_fail (name_quark != 0, NULL);
  g_return_val_if_fail (field_quark != 0, NULL);

  va_start (varargs, field_quark);
976
977
978
979
980
981
982
983
984
985
986
987
988

  /* Calculate size of varargs */
  va_copy (copy, varargs);
  while (quark_copy) {
    type_copy = va_arg (copy, GType);
    G_VALUE_COLLECT_SKIP (type_copy, copy);
    quark_copy = va_arg (copy, GQuark);
    len++;
  }
  va_end (copy);

  s = gst_structure_new_id_empty_with_size (name_quark, len);

989
  gst_structure_id_set_valist_internal (s, field_quark, varargs);
990
991
992
993
994
  va_end (varargs);

  return s;
}

995
996
997
998
999
1000
#if GST_VERSION_NANO == 1
#define GIT_G_WARNING g_warning
#else
#define GIT_G_WARNING GST_WARNING
#endif

1001
1002
1003
/* If the structure currently contains a field with the same name, it is
 * replaced with the provided field. Otherwise, the field is added to the
 * structure. The field's value is not deeply copied.
David Schleef's avatar
David Schleef committed
1004
 */
1005
static void
1006
gst_structure_set_field (GstStructure * structure, GstStructureField * field)
David Schleef's avatar
David Schleef committed
1007
1008
{
  GstStructureField *f;
1009
1010
1011
  GType field_value_type;
  guint i, len;

1012
  len = GST_STRUCTURE_LEN (structure);
David Schleef's avatar
David Schleef committed
1013

1014
1015
  field_value_type = G_VALUE_TYPE (&field->value);
  if (field_value_type == G_TYPE_STRING) {
1016
1017
1018
1019
1020
    const gchar *s;

    s = g_value_get_string (&field->value);
    /* only check for NULL strings in taglists, as they are allowed in message
     * structs, e.g. error message debug strings */
1021
1022
    if (G_UNLIKELY (IS_TAGLIST (structure) && (s == NULL || *s == '\0'))) {
      if (s == NULL) {
1023
        GIT_G_WARNING ("Trying to set NULL string on field '%s' on taglist. "
1024
1025
1026
1027
1028
            "Please file a bug.", g_quark_to_string (field->name));
        g_value_unset (&field->value);
        return;
      } else {
        /* empty strings never make sense */
1029
        GIT_G_WARNING ("Trying to set empty string on taglist field '%s'. "
1030
1031
1032
1033
            "Please file a bug.", g_quark_to_string (field->name));
        g_value_unset (&field->value);
        return;
      }
1034
1035
1036
1037
1038
    } else if (G_UNLIKELY (s != NULL && !g_utf8_validate (s, -1, NULL))) {
      g_warning ("Trying to set string on %s field '%s', but string is not "
          "valid UTF-8. Please file a bug.",
          IS_TAGLIST (structure) ? "taglist" : "structure",
          g_quark_to_string (field->name));
1039
      g_value_unset (&field->value);
1040
1041
      return;
    }
1042
  } else if (G_UNLIKELY (field_value_type == G_TYPE_DATE)) {
1043
1044
    const GDate *d;

1045
    d = g_value_get_boxed (&field->value);
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
    /* only check for NULL GDates in taglists, as they might make sense
     * in other, generic structs */
    if (G_UNLIKELY ((IS_TAGLIST (structure) && d == NULL))) {
      GIT_G_WARNING ("Trying to set NULL GDate on field '%s' on taglist. "
          "Please file a bug.", g_quark_to_string (field->name));
      g_value_unset (&field->value);
      return;
    } else if (G_UNLIKELY (d != NULL && !g_date_valid (d))) {
      g_warning
          ("Trying to set invalid GDate on %s field '%s'. Please file a bug.",
          IS_TAGLIST (structure) ? "taglist" : "structure",
          g_quark_to_string (field->name));
      g_value_unset (&field->value);
      return;
    }
1061
1062
  }

1063
  for (i = 0; i < len; i++) {
1064
    f = GST_STRUCTURE_FIELD (structure, i);
David Schleef's avatar
David Schleef committed
1065

1066
    if (G_UNLIKELY (f->name == field->name)) {
1067
1068
      g_value_unset (&f->value);
      memcpy (f, field, sizeof (GstStructureField));
David Schleef's avatar
David Schleef committed
1069
1070
1071
1072
      return;
    }
  }

1073
  _structure_append_val (structure, field);
David Schleef's avatar
David Schleef committed
1074
1075
}

1076
/* If there is no field with the given ID, NULL is returned.
David Schleef's avatar
David Schleef committed
1077
 */
1078
static GstStructureField *
1079
gst_structure_id_get_field (const GstStructure * structure, GQuark field_id)
David Schleef's avatar
David Schleef committed
1080
1081
{
  GstStructureField *field;
1082
  guint i, len;
David Schleef's avatar
David Schleef committed
1083

1084
  len = GST_STRUCTURE_LEN (structure);
David Schleef's avatar
David Schleef committed
1085

1086
  for (i = 0; i < len; i++) {
1087
    field = GST_STRUCTURE_FIELD (structure, i);
David Schleef's avatar
David Schleef committed
1088

1089
    if (G_UNLIKELY (field->name == field_id