Branch data Line data Source code
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 : 1396279 : tuple_equal_2 (gconstpointer v_a,
133 : : gconstpointer v_b)
134 : : {
135 : 1396279 : gpointer* a = (gpointer*) v_a;
136 : 1396279 : gpointer* b = (gpointer*) v_b;
137 : :
138 : 1396279 : return a[0] == b[0] && a[1] == b[1];
139 : : }
140 : :
141 : : static guint
142 : 11246 : tuple_hash_2 (gconstpointer v_a)
143 : : {
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 : 11246 : gpointer* a = (gpointer*) v_a;
154 : :
155 : 11246 : return (gulong)a[0] ^ (gulong)a[1];
156 : : #endif
157 : : }
158 : :
159 : : static GHashFunc
160 : 2047 : tuple_hash (gint fields)
161 : : {
162 : 2047 : switch (fields)
163 : : {
164 : 2047 : case 2:
165 : 2047 : return tuple_hash_2;
166 : 0 : default:
167 : 0 : g_error ("no tuple hash for %d", fields);
168 : : }
169 : :
170 : : return NULL;
171 : : }
172 : :
173 : : static GEqualFunc
174 : 2047 : tuple_equal (gint fields)
175 : : {
176 : 2047 : switch (fields)
177 : : {
178 : 2047 : case 2:
179 : 2047 : return tuple_equal_2;
180 : 0 : default:
181 : 0 : g_error ("no tuple equal for %d", fields);
182 : : }
183 : :
184 : : return NULL;
185 : : }
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 : 1 : g_relation_new (gint fields)
200 : : {
201 : 1 : GRelation* rel = g_new0 (GRelation, 1);
202 : : size_t unsigned_fields;
203 : :
204 : 1 : g_return_val_if_fail (fields == 2, NULL);
205 : :
206 : 1 : unsigned_fields = (size_t) fields;
207 : :
208 : 1 : rel->fields = unsigned_fields;
209 : 1 : rel->all_tuples = g_hash_table_new (tuple_hash (unsigned_fields), tuple_equal (unsigned_fields));
210 : 1 : rel->hashed_tuple_tables = g_new0 (GHashTable*, unsigned_fields);
211 : :
212 : 1 : return rel;
213 : : }
214 : :
215 : : static void
216 : 2040 : relation_delete_value_tuple (gpointer tuple_key,
217 : : gpointer tuple_value,
218 : : gpointer user_data)
219 : : {
220 : 2040 : GRelation *relation = user_data;
221 : 2040 : gpointer *tuple = tuple_value;
222 : 2040 : g_slice_free1 (relation->fields * sizeof (gpointer), tuple);
223 : 2040 : }
224 : :
225 : : static void
226 : 2044 : g_relation_free_array (gpointer key, gpointer value, gpointer user_data)
227 : : {
228 : 2044 : g_hash_table_destroy ((GHashTable*) value);
229 : 2044 : }
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 : 1 : g_relation_destroy (GRelation *relation)
243 : : {
244 : 1 : if (relation)
245 : : {
246 : 3 : for (size_t i = 0; i < relation->fields; i += 1)
247 : : {
248 : 2 : if (relation->hashed_tuple_tables[i])
249 : : {
250 : 2 : g_hash_table_foreach (relation->hashed_tuple_tables[i], g_relation_free_array, NULL);
251 : 2 : g_hash_table_destroy (relation->hashed_tuple_tables[i]);
252 : : }
253 : : }
254 : :
255 : 1 : g_hash_table_foreach (relation->all_tuples, relation_delete_value_tuple, relation);
256 : 1 : g_hash_table_destroy (relation->all_tuples);
257 : :
258 : 1 : g_free (relation->hashed_tuple_tables);
259 : 1 : g_free (relation);
260 : : }
261 : 1 : }
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 : 2 : g_relation_index (GRelation *relation,
277 : : gint field,
278 : : GHashFunc hash_func,
279 : : GEqualFunc key_equal_func)
280 : : {
281 : 2 : g_return_if_fail (relation != NULL);
282 : :
283 : 2 : g_return_if_fail (relation->count == 0 && relation->hashed_tuple_tables[field] == NULL);
284 : :
285 : 2 : relation->hashed_tuple_tables[field] = g_hash_table_new (hash_func, key_equal_func);
286 : : }
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 : 2044 : g_relation_insert (GRelation *relation,
301 : : ...)
302 : : {
303 : 2044 : gpointer* tuple = g_slice_alloc (relation->fields * sizeof (gpointer));
304 : : va_list args;
305 : :
306 : 2044 : va_start (args, relation);
307 : :
308 : 6132 : for (size_t i = 0; i < relation->fields; i += 1)
309 : 4088 : tuple[i] = va_arg (args, gpointer);
310 : :
311 : 2044 : va_end (args);
312 : :
313 : 2044 : g_hash_table_insert (relation->all_tuples, tuple, tuple);
314 : :
315 : 2044 : relation->count += 1;
316 : :
317 : 6132 : for (size_t i = 0; i < relation->fields; i += 1)
318 : : {
319 : : GHashTable *table;
320 : : gpointer key;
321 : : GHashTable *per_key_table;
322 : :
323 : 4088 : table = relation->hashed_tuple_tables[i];
324 : :
325 : 4088 : if (table == NULL)
326 : 0 : continue;
327 : :
328 : 4088 : key = tuple[i];
329 : 4088 : per_key_table = g_hash_table_lookup (table, key);
330 : :
331 : 4088 : if (per_key_table == NULL)
332 : : {
333 : 2046 : per_key_table = g_hash_table_new (tuple_hash (relation->fields), tuple_equal (relation->fields));
334 : 2046 : g_hash_table_insert (table, key, per_key_table);
335 : : }
336 : :
337 : 4088 : g_hash_table_insert (per_key_table, tuple, tuple);
338 : : }
339 : 2044 : }
340 : :
341 : : static void
342 : 4 : g_relation_delete_tuple (gpointer tuple_key,
343 : : gpointer tuple_value,
344 : : gpointer user_data)
345 : : {
346 : 4 : gpointer *tuple = (gpointer*) tuple_value;
347 : 4 : GRelation *relation = (GRelation *) user_data;
348 : :
349 : 4 : g_assert (tuple_key == tuple_value);
350 : :
351 : 12 : for (size_t j = 0; j < relation->fields; j += 1)
352 : : {
353 : 8 : GHashTable *one_table = relation->hashed_tuple_tables[j];
354 : : gpointer one_key;
355 : : GHashTable *per_key_table;
356 : :
357 : 8 : if (one_table == NULL)
358 : 0 : continue;
359 : :
360 : 8 : if (j == relation->current_field)
361 : : /* can't delete from the table we're foreaching in */
362 : 4 : continue;
363 : :
364 : 4 : one_key = tuple[j];
365 : :
366 : 4 : per_key_table = g_hash_table_lookup (one_table, one_key);
367 : :
368 : 4 : g_hash_table_remove (per_key_table, tuple);
369 : : }
370 : :
371 : 4 : if (g_hash_table_remove (relation->all_tuples, tuple))
372 : 4 : g_slice_free1 (relation->fields * sizeof (gpointer), tuple);
373 : :
374 : 4 : relation->count -= 1;
375 : 4 : }
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 : 2 : g_relation_delete (GRelation *relation,
392 : : gconstpointer key,
393 : : gint field)
394 : : {
395 : : GHashTable *table;
396 : : GHashTable *key_table;
397 : : size_t count;
398 : : size_t unsigned_field;
399 : :
400 : 2 : g_return_val_if_fail (relation != NULL, 0);
401 : 2 : g_return_val_if_fail (field >= 0 && (size_t) field < relation->fields, 0);
402 : :
403 : 2 : unsigned_field = (size_t) field;
404 : 2 : table = relation->hashed_tuple_tables[unsigned_field];
405 : 2 : count = relation->count;
406 : :
407 : 2 : g_return_val_if_fail (table != NULL, 0);
408 : :
409 : 2 : key_table = g_hash_table_lookup (table, key);
410 : :
411 : 2 : if (!key_table)
412 : 0 : return 0;
413 : :
414 : 2 : relation->current_field = unsigned_field;
415 : :
416 : 2 : g_hash_table_foreach (key_table, g_relation_delete_tuple, relation);
417 : :
418 : 2 : g_hash_table_remove (table, key);
419 : :
420 : 2 : g_hash_table_destroy (key_table);
421 : :
422 : : /* @@@ FIXME: Remove empty hash tables. */
423 : :
424 : 2 : return count - relation->count;
425 : : }
426 : :
427 : : static void
428 : 2 : g_relation_select_tuple (gpointer tuple_key,
429 : : gpointer tuple_value,
430 : : gpointer user_data)
431 : : {
432 : 2 : gpointer *tuple = (gpointer*) tuple_value;
433 : 2 : GRealTuples *tuples = (GRealTuples*) user_data;
434 : 2 : size_t stride = sizeof (gpointer) * tuples->width;
435 : :
436 : 2 : g_assert (tuple_key == tuple_value);
437 : :
438 : 2 : memcpy (tuples->data + (tuples->len * tuples->width),
439 : : tuple,
440 : : stride);
441 : :
442 : 2 : tuples->len += 1;
443 : 2 : }
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 : 1 : g_relation_select (GRelation *relation,
461 : : gconstpointer key,
462 : : gint field)
463 : : {
464 : : GHashTable *table;
465 : : GHashTable *key_table;
466 : : GRealTuples *tuples;
467 : : size_t count;
468 : : size_t unsigned_field;
469 : :
470 : 1 : g_return_val_if_fail (relation != NULL, NULL);
471 : 1 : g_return_val_if_fail (field >= 0 && (size_t) field < relation->fields, NULL);
472 : :
473 : 1 : unsigned_field = (size_t) field;
474 : 1 : table = relation->hashed_tuple_tables[unsigned_field];
475 : :
476 : 1 : g_return_val_if_fail (table != NULL, NULL);
477 : :
478 : 1 : tuples = g_new0 (GRealTuples, 1);
479 : 1 : key_table = g_hash_table_lookup (table, key);
480 : :
481 : 1 : if (!key_table)
482 : 0 : return (GTuples*)tuples;
483 : :
484 : 1 : count = relation_count_internal (relation, key, unsigned_field);
485 : :
486 : 1 : tuples->data = g_malloc (sizeof (gpointer) * relation->fields * count);
487 : 1 : tuples->width = relation->fields;
488 : :
489 : 1 : g_hash_table_foreach (key_table, g_relation_select_tuple, tuples);
490 : :
491 : 1 : g_assert (count == (size_t) tuples->len);
492 : :
493 : 1 : return (GTuples*)tuples;
494 : : }
495 : :
496 : : static size_t
497 : 2048 : relation_count_internal (GRelation *relation,
498 : : gconstpointer key,
499 : : size_t field)
500 : : {
501 : : GHashTable *table;
502 : : GHashTable *key_table;
503 : :
504 : 2048 : g_return_val_if_fail (relation != NULL, 0);
505 : 2048 : g_return_val_if_fail (field < relation->fields, 0);
506 : :
507 : 2048 : table = relation->hashed_tuple_tables[field];
508 : :
509 : 2048 : g_return_val_if_fail (table != NULL, 0);
510 : :
511 : 2048 : key_table = g_hash_table_lookup (table, key);
512 : :
513 : 2048 : if (!key_table)
514 : 2 : return 0;
515 : :
516 : 2046 : return g_hash_table_size (key_table);
517 : : }
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 : 2047 : g_relation_count (GRelation *relation,
534 : : gconstpointer key,
535 : : gint field)
536 : : {
537 : : unsigned int n_matches;
538 : :
539 : 2047 : 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 : 2047 : n_matches = relation_count_internal (relation, key, (size_t) field);
543 : 2047 : g_return_val_if_fail (n_matches <= G_MAXINT, G_MAXINT);
544 : 2047 : return (gint) n_matches;
545 : : }
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 : 5106 : g_relation_exists (GRelation *relation, ...)
563 : : {
564 : 5106 : gpointer *tuple = g_slice_alloc (relation->fields * sizeof (gpointer));
565 : : va_list args;
566 : : gboolean result;
567 : :
568 : 5106 : va_start(args, relation);
569 : :
570 : 15318 : for (size_t i = 0; i < relation->fields; i += 1)
571 : 10212 : tuple[i] = va_arg(args, gpointer);
572 : :
573 : 5106 : va_end(args);
574 : :
575 : 5106 : result = g_hash_table_lookup (relation->all_tuples, tuple) != NULL;
576 : :
577 : 5106 : g_slice_free1 (relation->fields * sizeof (gpointer), tuple);
578 : :
579 : 5106 : return result;
580 : : }
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 : 1 : g_tuples_destroy (GTuples *tuples0)
595 : : {
596 : 1 : GRealTuples *tuples = (GRealTuples*) tuples0;
597 : :
598 : 1 : if (tuples)
599 : : {
600 : 1 : g_free (tuples->data);
601 : 1 : g_free (tuples);
602 : : }
603 : 1 : }
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 : 0 : g_tuples_index (GTuples *tuples0,
621 : : gint index,
622 : : gint field)
623 : : {
624 : 0 : GRealTuples *tuples = (GRealTuples*) tuples0;
625 : : 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 : : }
636 : :
637 : : /* Print
638 : : */
639 : :
640 : : static void
641 : 0 : g_relation_print_one (gpointer tuple_key,
642 : : gpointer tuple_value,
643 : : gpointer user_data)
644 : : {
645 : : 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 : : {
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 : : }
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 : 0 : g_relation_print_index (gpointer tuple_key,
666 : : gpointer tuple_value,
667 : : gpointer user_data)
668 : : {
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 : : g_relation_print_one,
676 : : 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 : 0 : g_relation_print (GRelation *relation)
690 : : {
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 : : g_relation_print_one,
695 : : relation);
696 : :
697 : 0 : for (size_t i = 0; i < relation->fields; i += 1)
698 : : {
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 : : g_relation_print_index,
706 : : relation);
707 : : }
708 : :
709 : 0 : }
|