Coverage Report

Created: 2026-05-16 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gstreamer/subprojects/glib-2.86.3/glib/deprecated/grel.c
Line
Count
Source
1
/* GLIB - Library of useful routines for C programming
2
 * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library; if not, write to the Free
18
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
 */
20
21
/*
22
 * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
23
 * file for a list of people on the GLib Team.  See the ChangeLog
24
 * files for a list of changes.  These files are distributed with
25
 * GLib at ftp://ftp.gtk.org/pub/gtk/. 
26
 */
27
28
/* 
29
 * MT safe
30
 */
31
32
#include "config.h"
33
34
/* we know we are deprecated here, no need for warnings */
35
#ifndef GLIB_DISABLE_DEPRECATION_WARNINGS
36
#define GLIB_DISABLE_DEPRECATION_WARNINGS
37
#endif
38
39
#include "grel.h"
40
41
#include <glib/gmessages.h>
42
#include <glib/gtestutils.h>
43
#include <glib/gstring.h>
44
#include <glib/gslice.h>
45
#include <glib/ghash.h>
46
47
#include <stdarg.h>
48
#include <string.h>
49
50
/**
51
 * GRelation:
52
 *
53
 * A `GRelation` is a table of data which can be indexed on any number
54
 * of fields, rather like simple database tables. A `GRelation` contains
55
 * a number of records, called tuples. Each record contains a number of
56
 * fields. Records are not ordered, so it is not possible to find the
57
 * record at a particular index.
58
 *
59
 * Note that `GRelation` tables are currently limited to 2 fields.
60
 *
61
 * To create a `GRelation`, use [func@GLib.Relation.new].
62
 *
63
 * To specify which fields should be indexed, use [method@GLib.Relation.index].
64
 * Note that this must be called before any tuples are added to the
65
 * `GRelation`.
66
 *
67
 * To add records to a `GRelation` use [method@GLib.Relation.insert].
68
 *
69
 * To determine if a given record appears in a `GRelation`, use
70
 * [method@GLib.Relation.exists]. Note that fields are compared directly, so
71
 * pointers must point to the exact same position (i.e. different
72
 * copies of the same string will not match.)
73
 *
74
 * To count the number of records which have a particular value in a
75
 * given field, use [method@GLib.Relation.count].
76
 *
77
 * To get all the records which have a particular value in a given
78
 * field, use [method@GLib.Relation.select]. To access fields of the resulting
79
 * records, use [method@GLib.Tuples.index]. To free the resulting records use
80
 * [method@GLib.Tuples.destroy].
81
 *
82
 * To delete all records which have a particular value in a given
83
 * field, use [method@GLib.Relation.delete].
84
 *
85
 * To destroy the `GRelation`, use [method@GLib.Relation.destroy].
86
 *
87
 * To help debug `GRelation` objects, use [method@GLib.Relation.print].
88
 *
89
 * `GRelation` has been marked as deprecated, since this API has never
90
 * been fully implemented, is not very actively maintained and rarely
91
 * used.
92
 *
93
 * Deprecated: 2.26: Rarely used API
94
 **/
95
96
typedef struct _GRealTuples        GRealTuples;
97
98
struct _GRelation
99
{
100
  size_t fields;
101
  size_t current_field;
102
  
103
  GHashTable   *all_tuples;
104
  GHashTable  **hashed_tuple_tables;
105
  
106
  size_t count;
107
};
108
109
static size_t relation_count_internal (GRelation     *relation,
110
                                       gconstpointer  key,
111
                                       size_t         field);
112
113
/**
114
 * GTuples:
115
 * @len: the number of records that matched.
116
 *
117
 * The #GTuples struct is used to return records (or tuples) from the
118
 * #GRelation by g_relation_select(). It only contains one public
119
 * member - the number of records that matched. To access the matched
120
 * records, you must use g_tuples_index().
121
 *
122
 * Deprecated: 2.26: Rarely used API
123
 **/
124
struct _GRealTuples
125
{
126
  guint     len;
127
  size_t    width;
128
  gpointer *data;
129
};
130
131
static gboolean
132
tuple_equal_2 (gconstpointer v_a,
133
         gconstpointer v_b)
134
0
{
135
0
  gpointer* a = (gpointer*) v_a;
136
0
  gpointer* b = (gpointer*) v_b;
137
  
138
0
  return a[0] == b[0] && a[1] == b[1];
139
0
}
140
141
static guint
142
tuple_hash_2 (gconstpointer v_a)
143
0
{
144
#if GLIB_SIZEOF_VOID_P > GLIB_SIZEOF_LONG
145
  /* In practise this snippet has been written for 64-bit Windows
146
   * where ints are 32 bits, pointers 64 bits. More exotic platforms
147
   * need more tweaks.
148
   */
149
  guint* a = (guint*) v_a;
150
151
  return (a[0] ^ a[1] ^ a[2] ^ a[3]);
152
#else
153
0
  gpointer* a = (gpointer*) v_a;
154
  
155
0
  return (gulong)a[0] ^ (gulong)a[1];
156
0
#endif
157
0
}
158
159
static GHashFunc
160
tuple_hash (gint fields)
161
0
{
162
0
  switch (fields)
163
0
    {
164
0
    case 2:
165
0
      return tuple_hash_2;
166
0
    default:
167
0
      g_error ("no tuple hash for %d", fields);
168
0
    }
169
  
170
0
  return NULL;
171
0
}
172
173
static GEqualFunc
174
tuple_equal (gint fields)
175
0
{
176
0
  switch (fields)
177
0
    {
178
0
    case 2:
179
0
      return tuple_equal_2;
180
0
    default:
181
0
      g_error ("no tuple equal for %d", fields);
182
0
    }
183
  
184
0
  return NULL;
185
0
}
186
187
/**
188
 * g_relation_new:
189
 * @fields: the number of fields.
190
 *
191
 * Creates a new #GRelation with the given number of fields. Note that
192
 * currently the number of fields must be 2.
193
 *
194
 * Returns: a new #GRelation.
195
 *
196
 * Deprecated: 2.26: Rarely used API
197
 **/
198
GRelation*
199
g_relation_new (gint fields)
200
0
{
201
0
  GRelation* rel = g_new0 (GRelation, 1);
202
0
  size_t unsigned_fields;
203
  
204
0
  g_return_val_if_fail (fields == 2, NULL);
205
206
0
  unsigned_fields = (size_t) fields;
207
208
0
  rel->fields = unsigned_fields;
209
0
  rel->all_tuples = g_hash_table_new (tuple_hash (unsigned_fields), tuple_equal (unsigned_fields));
210
0
  rel->hashed_tuple_tables = g_new0 (GHashTable*, unsigned_fields);
211
  
212
0
  return rel;
213
0
}
214
215
static void
216
relation_delete_value_tuple (gpointer tuple_key,
217
                             gpointer tuple_value,
218
                             gpointer user_data)
219
0
{
220
0
  GRelation *relation = user_data;
221
0
  gpointer *tuple = tuple_value;
222
0
  g_slice_free1 (relation->fields * sizeof (gpointer), tuple);
223
0
}
224
225
static void
226
g_relation_free_array (gpointer key, gpointer value, gpointer user_data)
227
0
{
228
0
  g_hash_table_destroy ((GHashTable*) value);
229
0
}
230
231
/**
232
 * g_relation_destroy:
233
 * @relation: a #GRelation.
234
 *
235
 * Destroys the #GRelation, freeing all memory allocated. However, it
236
 * does not free memory allocated for the tuple data, so you should
237
 * free that first if appropriate.
238
 *
239
 * Deprecated: 2.26: Rarely used API
240
 **/
241
void
242
g_relation_destroy (GRelation *relation)
243
0
{
244
0
  if (relation)
245
0
    {
246
0
      for (size_t i = 0; i < relation->fields; i += 1)
247
0
  {
248
0
    if (relation->hashed_tuple_tables[i])
249
0
      {
250
0
        g_hash_table_foreach (relation->hashed_tuple_tables[i], g_relation_free_array, NULL);
251
0
        g_hash_table_destroy (relation->hashed_tuple_tables[i]);
252
0
      }
253
0
  }
254
255
0
      g_hash_table_foreach (relation->all_tuples, relation_delete_value_tuple, relation);
256
0
      g_hash_table_destroy (relation->all_tuples);
257
      
258
0
      g_free (relation->hashed_tuple_tables);
259
0
      g_free (relation);
260
0
    }
261
0
}
262
263
/**
264
 * g_relation_index:
265
 * @relation: a #GRelation.
266
 * @field: the field to index, counting from 0.
267
 * @hash_func: a function to produce a hash value from the field data.
268
 * @key_equal_func: a function to compare two values of the given field.
269
 *
270
 * Creates an index on the given field. Note that this must be called
271
 * before any records are added to the #GRelation.
272
 *
273
 * Deprecated: 2.26: Rarely used API
274
 **/
275
void
276
g_relation_index (GRelation   *relation,
277
      gint         field,
278
      GHashFunc    hash_func,
279
      GEqualFunc   key_equal_func)
280
0
{
281
0
  g_return_if_fail (relation != NULL);
282
  
283
0
  g_return_if_fail (relation->count == 0 && relation->hashed_tuple_tables[field] == NULL);
284
  
285
0
  relation->hashed_tuple_tables[field] = g_hash_table_new (hash_func, key_equal_func);
286
0
}
287
288
/**
289
 * g_relation_insert:
290
 * @relation: a #GRelation.
291
 * @...: the fields of the record to add. These must match the
292
 *       number of fields in the #GRelation, and of type #gpointer
293
 *       or #gconstpointer.
294
 *
295
 * Inserts a record into a #GRelation.
296
 *
297
 * Deprecated: 2.26: Rarely used API
298
 **/
299
void
300
g_relation_insert (GRelation   *relation,
301
       ...)
302
0
{
303
0
  gpointer* tuple = g_slice_alloc (relation->fields * sizeof (gpointer));
304
0
  va_list args;
305
  
306
0
  va_start (args, relation);
307
  
308
0
  for (size_t i = 0; i < relation->fields; i += 1)
309
0
    tuple[i] = va_arg (args, gpointer);
310
  
311
0
  va_end (args);
312
  
313
0
  g_hash_table_insert (relation->all_tuples, tuple, tuple);
314
  
315
0
  relation->count += 1;
316
  
317
0
  for (size_t i = 0; i < relation->fields; i += 1)
318
0
    {
319
0
      GHashTable *table;
320
0
      gpointer    key;
321
0
      GHashTable *per_key_table;
322
      
323
0
      table = relation->hashed_tuple_tables[i];
324
      
325
0
      if (table == NULL)
326
0
  continue;
327
      
328
0
      key = tuple[i];
329
0
      per_key_table = g_hash_table_lookup (table, key);
330
      
331
0
      if (per_key_table == NULL)
332
0
  {
333
0
    per_key_table = g_hash_table_new (tuple_hash (relation->fields), tuple_equal (relation->fields));
334
0
    g_hash_table_insert (table, key, per_key_table);
335
0
  }
336
      
337
0
      g_hash_table_insert (per_key_table, tuple, tuple);
338
0
    }
339
0
}
340
341
static void
342
g_relation_delete_tuple (gpointer tuple_key,
343
       gpointer tuple_value,
344
       gpointer user_data)
345
0
{
346
0
  gpointer      *tuple = (gpointer*) tuple_value;
347
0
  GRelation     *relation = (GRelation *) user_data;
348
  
349
0
  g_assert (tuple_key == tuple_value);
350
  
351
0
  for (size_t j = 0; j < relation->fields; j += 1)
352
0
    {
353
0
      GHashTable *one_table = relation->hashed_tuple_tables[j];
354
0
      gpointer    one_key;
355
0
      GHashTable *per_key_table;
356
      
357
0
      if (one_table == NULL)
358
0
  continue;
359
      
360
0
      if (j == relation->current_field)
361
  /* can't delete from the table we're foreaching in */
362
0
  continue;
363
      
364
0
      one_key = tuple[j];
365
      
366
0
      per_key_table = g_hash_table_lookup (one_table, one_key);
367
      
368
0
      g_hash_table_remove (per_key_table, tuple);
369
0
    }
370
  
371
0
  if (g_hash_table_remove (relation->all_tuples, tuple))
372
0
    g_slice_free1 (relation->fields * sizeof (gpointer), tuple);
373
  
374
0
  relation->count -= 1;
375
0
}
376
377
/**
378
 * g_relation_delete:
379
 * @relation: a #GRelation.
380
 * @key: the value to compare with.
381
 * @field: the field of each record to match.
382
 *
383
 * Deletes any records from a #GRelation that have the given key value
384
 * in the given field.
385
 *
386
 * Returns: the number of records deleted.
387
 *
388
 * Deprecated: 2.26: Rarely used API
389
 **/
390
gint
391
g_relation_delete  (GRelation     *relation,
392
        gconstpointer  key,
393
        gint           field)
394
0
{
395
0
  GHashTable *table; 
396
0
  GHashTable *key_table;
397
0
  size_t count;
398
0
  size_t unsigned_field;
399
  
400
0
  g_return_val_if_fail (relation != NULL, 0);
401
0
  g_return_val_if_fail (field >= 0 && (size_t) field < relation->fields, 0);
402
403
0
  unsigned_field = (size_t) field;
404
0
  table = relation->hashed_tuple_tables[unsigned_field];
405
0
  count = relation->count;
406
407
0
  g_return_val_if_fail (table != NULL, 0);
408
  
409
0
  key_table = g_hash_table_lookup (table, key);
410
  
411
0
  if (!key_table)
412
0
    return 0;
413
  
414
0
  relation->current_field = unsigned_field;
415
  
416
0
  g_hash_table_foreach (key_table, g_relation_delete_tuple, relation);
417
  
418
0
  g_hash_table_remove (table, key);
419
  
420
0
  g_hash_table_destroy (key_table);
421
  
422
  /* @@@ FIXME: Remove empty hash tables. */
423
  
424
0
  return count - relation->count;
425
0
}
426
427
static void
428
g_relation_select_tuple (gpointer tuple_key,
429
       gpointer tuple_value,
430
       gpointer user_data)
431
0
{
432
0
  gpointer    *tuple = (gpointer*) tuple_value;
433
0
  GRealTuples *tuples = (GRealTuples*) user_data;
434
0
  size_t stride = sizeof (gpointer) * tuples->width;
435
  
436
0
  g_assert (tuple_key == tuple_value);
437
  
438
0
  memcpy (tuples->data + (tuples->len * tuples->width),
439
0
    tuple,
440
0
    stride);
441
  
442
0
  tuples->len += 1;
443
0
}
444
445
/**
446
 * g_relation_select:
447
 * @relation: a #GRelation.
448
 * @key: the value to compare with.
449
 * @field: the field of each record to match.
450
 *
451
 * Returns all of the tuples which have the given key in the given
452
 * field. Use g_tuples_index() to access the returned records. The
453
 * returned records should be freed with g_tuples_destroy().
454
 *
455
 * Returns: the records (tuples) that matched.
456
 *
457
 * Deprecated: 2.26: Rarely used API
458
 **/
459
GTuples*
460
g_relation_select (GRelation     *relation,
461
       gconstpointer  key,
462
       gint           field)
463
0
{
464
0
  GHashTable  *table;
465
0
  GHashTable  *key_table;
466
0
  GRealTuples *tuples; 
467
0
  size_t count;
468
0
  size_t unsigned_field;
469
  
470
0
  g_return_val_if_fail (relation != NULL, NULL);
471
0
  g_return_val_if_fail (field >= 0 && (size_t) field < relation->fields, NULL);
472
473
0
  unsigned_field = (size_t) field;
474
0
  table = relation->hashed_tuple_tables[unsigned_field];
475
476
0
  g_return_val_if_fail (table != NULL, NULL);
477
  
478
0
  tuples = g_new0 (GRealTuples, 1);
479
0
  key_table = g_hash_table_lookup (table, key);
480
  
481
0
  if (!key_table)
482
0
    return (GTuples*)tuples;
483
  
484
0
  count = relation_count_internal (relation, key, unsigned_field);
485
  
486
0
  tuples->data = g_malloc (sizeof (gpointer) * relation->fields * count);
487
0
  tuples->width = relation->fields;
488
  
489
0
  g_hash_table_foreach (key_table, g_relation_select_tuple, tuples);
490
  
491
0
  g_assert (count == (size_t) tuples->len);
492
  
493
0
  return (GTuples*)tuples;
494
0
}
495
496
static size_t
497
relation_count_internal (GRelation     *relation,
498
                         gconstpointer  key,
499
                         size_t         field)
500
0
{
501
0
  GHashTable *table;
502
0
  GHashTable *key_table;
503
504
0
  g_return_val_if_fail (relation != NULL, 0);
505
0
  g_return_val_if_fail (field < relation->fields, 0);
506
507
0
  table = relation->hashed_tuple_tables[field];
508
509
0
  g_return_val_if_fail (table != NULL, 0);
510
511
0
  key_table = g_hash_table_lookup (table, key);
512
513
0
  if (!key_table)
514
0
    return 0;
515
516
0
  return g_hash_table_size (key_table);
517
0
}
518
519
/**
520
 * g_relation_count:
521
 * @relation: a #GRelation.
522
 * @key: the value to compare with.
523
 * @field: the field of each record to match.
524
 *
525
 * Returns the number of tuples in a #GRelation that have the given
526
 * value in the given field.
527
 *
528
 * Returns: the number of matches.
529
 *
530
 * Deprecated: 2.26: Rarely used API
531
 **/
532
gint
533
g_relation_count (GRelation     *relation,
534
      gconstpointer  key,
535
      gint           field)
536
0
{
537
0
  unsigned int n_matches;
538
539
0
  g_return_val_if_fail (field >= 0 && (size_t) field < relation->fields, 0);
540
541
  /* Do the best we can with the limited return type */
542
0
  n_matches = relation_count_internal (relation, key, (size_t) field);
543
0
  g_return_val_if_fail (n_matches <= G_MAXINT, G_MAXINT);
544
0
  return (gint) n_matches;
545
0
}
546
547
/**
548
 * g_relation_exists:
549
 * @relation: a #GRelation.
550
 * @...: the fields of the record to compare. The number must match
551
 *       the number of fields in the #GRelation.
552
 *
553
 * Returns %TRUE if a record with the given values exists in a
554
 * #GRelation. Note that the values are compared directly, so that, for
555
 * example, two copies of the same string will not match.
556
 *
557
 * Returns: %TRUE if a record matches.
558
 *
559
 * Deprecated: 2.26: Rarely used API
560
 **/
561
gboolean
562
g_relation_exists (GRelation   *relation, ...)
563
0
{
564
0
  gpointer *tuple = g_slice_alloc (relation->fields * sizeof (gpointer));
565
0
  va_list args;
566
0
  gboolean result;
567
  
568
0
  va_start(args, relation);
569
  
570
0
  for (size_t i = 0; i < relation->fields; i += 1)
571
0
    tuple[i] = va_arg(args, gpointer);
572
  
573
0
  va_end(args);
574
  
575
0
  result = g_hash_table_lookup (relation->all_tuples, tuple) != NULL;
576
  
577
0
  g_slice_free1 (relation->fields * sizeof (gpointer), tuple);
578
  
579
0
  return result;
580
0
}
581
582
/**
583
 * g_tuples_destroy:
584
 * @tuples: the tuple data to free.
585
 *
586
 * Frees the records which were returned by g_relation_select(). This
587
 * should always be called after g_relation_select() when you are
588
 * finished with the records. The records are not removed from the
589
 * #GRelation.
590
 *
591
 * Deprecated: 2.26: Rarely used API
592
 **/
593
void
594
g_tuples_destroy (GTuples *tuples0)
595
0
{
596
0
  GRealTuples *tuples = (GRealTuples*) tuples0;
597
  
598
0
  if (tuples)
599
0
    {
600
0
      g_free (tuples->data);
601
0
      g_free (tuples);
602
0
    }
603
0
}
604
605
/**
606
 * g_tuples_index:
607
 * @tuples: the tuple data, returned by g_relation_select().
608
 * @index_: the index of the record.
609
 * @field: the field to return.
610
 *
611
 * Gets a field from the records returned by g_relation_select(). It
612
 * returns the given field of the record at the given index. The
613
 * returned value should not be changed.
614
 *
615
 * Returns: the field of the record.
616
 *
617
 * Deprecated: 2.26: Rarely used API
618
 **/
619
gpointer
620
g_tuples_index (GTuples     *tuples0,
621
    gint         index,
622
    gint         field)
623
0
{
624
0
  GRealTuples *tuples = (GRealTuples*) tuples0;
625
0
  size_t unsigned_index, unsigned_field;
626
627
0
  g_return_val_if_fail (tuples0 != NULL, NULL);
628
0
  g_return_val_if_fail (index >= 0, NULL);
629
0
  g_return_val_if_fail (field >= 0 && (size_t) field < tuples->width, NULL);
630
631
0
  unsigned_index = (size_t) index;
632
0
  unsigned_field = (size_t) field;
633
634
0
  return tuples->data[unsigned_index * tuples->width + unsigned_field];
635
0
}
636
637
/* Print
638
 */
639
640
static void
641
g_relation_print_one (gpointer tuple_key,
642
          gpointer tuple_value,
643
          gpointer user_data)
644
0
{
645
0
  GString *gstring;
646
0
  GRelation* rel = (GRelation*) user_data;
647
0
  gpointer* tuples = (gpointer*) tuple_value;
648
649
0
  gstring = g_string_new ("[");
650
  
651
0
  for (size_t i = 0; i < rel->fields; i += 1)
652
0
    {
653
0
      g_string_append_printf (gstring, "%p", tuples[i]);
654
      
655
0
      if (i < (rel->fields - 1))
656
0
  g_string_append (gstring, ",");
657
0
    }
658
  
659
0
  g_string_append (gstring, "]");
660
0
  g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "%s", gstring->str);
661
0
  g_string_free (gstring, TRUE);
662
0
}
663
664
static void
665
g_relation_print_index (gpointer tuple_key,
666
      gpointer tuple_value,
667
      gpointer user_data)
668
0
{
669
0
  GRelation* rel = (GRelation*) user_data;
670
0
  GHashTable* table = (GHashTable*) tuple_value;
671
  
672
0
  g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "*** key %p", tuple_key);
673
  
674
0
  g_hash_table_foreach (table,
675
0
      g_relation_print_one,
676
0
      rel);
677
0
}
678
679
/**
680
 * g_relation_print:
681
 * @relation: a #GRelation.
682
 *
683
 * Outputs information about all records in a #GRelation, as well as
684
 * the indexes. It is for debugging.
685
 *
686
 * Deprecated: 2.26: Rarely used API
687
 **/
688
void
689
g_relation_print (GRelation *relation)
690
0
{
691
0
  g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "*** all tuples (%" G_GSIZE_FORMAT ")", relation->count);
692
  
693
0
  g_hash_table_foreach (relation->all_tuples,
694
0
      g_relation_print_one,
695
0
      relation);
696
  
697
0
  for (size_t i = 0; i < relation->fields; i += 1)
698
0
    {
699
0
      if (relation->hashed_tuple_tables[i] == NULL)
700
0
  continue;
701
      
702
0
      g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "*** index %" G_GSIZE_FORMAT, i);
703
      
704
0
      g_hash_table_foreach (relation->hashed_tuple_tables[i],
705
0
          g_relation_print_index,
706
0
          relation);
707
0
    }
708
  
709
0
}