Coverage Report

Created: 2026-11-10 07:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/glib-2.88.3/subprojects/gvdb/gvdb/gvdb-reader.c
Line
Count
Source
1
/*
2
 * Copyright © 2010 Codethink Limited
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, see <http://www.gnu.org/licenses/>.
18
 *
19
 * Author: Ryan Lortie <desrt@desrt.ca>
20
 */
21
22
#include "gvdb-reader.h"
23
#include "gvdb-format.h"
24
25
#include <string.h>
26
27
struct _GvdbTable {
28
  GBytes *bytes;
29
30
  const gchar *data;
31
  gsize size;
32
33
  gboolean byteswapped;
34
  gboolean trusted;
35
36
  const guint32_le *bloom_words;
37
  guint32 n_bloom_words;
38
  guint bloom_shift;
39
40
  const guint32_le *hash_buckets;
41
  guint32 n_buckets;
42
43
  struct gvdb_hash_item *hash_items;
44
  guint32 n_hash_items;
45
};
46
47
static const gchar *
48
gvdb_table_item_get_key (GvdbTable                   *file,
49
                         const struct gvdb_hash_item *item,
50
                         gsize                       *size)
51
0
{
52
0
  guint32 start, end;
53
54
0
  start = guint32_from_le (item->key_start);
55
0
  *size = guint16_from_le (item->key_size);
56
0
  end = start + *size;
57
58
0
  if G_UNLIKELY (start > end || end > file->size)
59
0
    return NULL;
60
61
0
  return file->data + start;
62
0
}
63
64
static gconstpointer
65
gvdb_table_dereference (GvdbTable                 *file,
66
                        const struct gvdb_pointer *pointer,
67
                        gint                       alignment,
68
                        gsize                     *size)
69
0
{
70
0
  guint32 start, end;
71
72
0
  start = guint32_from_le (pointer->start);
73
0
  end = guint32_from_le (pointer->end);
74
75
0
  if G_UNLIKELY (start > end || end > file->size || start & (alignment - 1))
76
0
    return NULL;
77
78
0
  *size = end - start;
79
80
0
  return file->data + start;
81
0
}
82
83
static void
84
gvdb_table_setup_root (GvdbTable                 *file,
85
                       const struct gvdb_pointer *pointer)
86
0
{
87
0
  const struct gvdb_hash_header *header;
88
0
  guint32 n_bloom_words;
89
0
  guint32 n_buckets;
90
0
  gsize size;
91
92
0
  header = gvdb_table_dereference (file, pointer, 4, &size);
93
94
0
  if G_UNLIKELY (header == NULL || size < sizeof *header)
95
0
    return;
96
97
0
  size -= sizeof *header;
98
99
0
  n_bloom_words = guint32_from_le (header->n_bloom_words);
100
0
  n_buckets = guint32_from_le (header->n_buckets);
101
0
  n_bloom_words &= (1u << 27) - 1;
102
103
0
  if G_UNLIKELY (n_bloom_words * sizeof (guint32_le) > size)
104
0
    return;
105
106
0
  file->bloom_words = (gpointer) (header + 1);
107
0
  size -= n_bloom_words * sizeof (guint32_le);
108
0
  file->n_bloom_words = n_bloom_words;
109
110
0
  if G_UNLIKELY (n_buckets > G_MAXUINT / sizeof (guint32_le) ||
111
0
                 n_buckets * sizeof (guint32_le) > size)
112
0
    return;
113
114
0
  file->hash_buckets = file->bloom_words + file->n_bloom_words;
115
0
  size -= n_buckets * sizeof (guint32_le);
116
0
  file->n_buckets = n_buckets;
117
118
0
  if G_UNLIKELY (size % sizeof (struct gvdb_hash_item))
119
0
    return;
120
121
0
  file->hash_items = (gpointer) (file->hash_buckets + n_buckets);
122
0
  file->n_hash_items = size / sizeof (struct gvdb_hash_item);
123
0
}
124
125
/**
126
 * gvdb_table_new_from_bytes:
127
 * @bytes: the #GBytes with the data
128
 * @trusted: if the contents of @bytes are trusted
129
 * @error: %NULL, or a pointer to a %NULL #GError
130
 *
131
 * Creates a new #GvdbTable from the contents of @bytes.
132
 *
133
 * This call can fail if the header contained in @bytes is invalid or if @bytes
134
 * is empty; if so, %G_FILE_ERROR_INVAL will be returned.
135
 *
136
 * You should call gvdb_table_free() on the return result when you no
137
 * longer require it.
138
 *
139
 * Returns: a new #GvdbTable
140
 **/
141
GvdbTable *
142
gvdb_table_new_from_bytes (GBytes    *bytes,
143
                           gboolean   trusted,
144
                           GError   **error)
145
0
{
146
0
  const struct gvdb_header *header;
147
0
  GvdbTable *file;
148
149
0
  file = g_slice_new0 (GvdbTable);
150
0
  file->bytes = g_bytes_ref (bytes);
151
0
  file->data = g_bytes_get_data (bytes, &file->size);
152
0
  file->trusted = trusted;
153
154
0
  if (file->size < sizeof (struct gvdb_header))
155
0
    goto invalid;
156
157
0
  header = (gpointer) file->data;
158
159
0
  if (header->signature[0] == GVDB_SIGNATURE0 &&
160
0
      header->signature[1] == GVDB_SIGNATURE1 &&
161
0
      guint32_from_le (header->version) == 0)
162
0
    file->byteswapped = FALSE;
163
164
0
  else if (header->signature[0] == GVDB_SWAPPED_SIGNATURE0 &&
165
0
           header->signature[1] == GVDB_SWAPPED_SIGNATURE1 &&
166
0
           guint32_from_le (header->version) == 0)
167
0
    file->byteswapped = TRUE;
168
169
0
  else
170
0
    goto invalid;
171
172
0
  gvdb_table_setup_root (file, &header->root);
173
174
0
  return file;
175
176
0
invalid:
177
0
  g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_INVAL, "invalid gvdb header");
178
179
0
  g_bytes_unref (file->bytes);
180
181
0
  g_slice_free (GvdbTable, file);
182
183
0
  return NULL;
184
0
}
185
186
/**
187
 * gvdb_table_new:
188
 * @filename: a filename
189
 * @trusted: if the contents of @bytes are trusted
190
 * @error: %NULL, or a pointer to a %NULL #GError
191
 *
192
 * Creates a new #GvdbTable using the #GMappedFile for @filename as the
193
 * #GBytes.
194
 *
195
 * This function will fail if the file cannot be opened.
196
 * In that case, the #GError that is returned will be an error from
197
 * g_mapped_file_new().
198
 *
199
 * An empty or corrupt file will result in %G_FILE_ERROR_INVAL.
200
 *
201
 * Returns: a new #GvdbTable
202
 **/
203
GvdbTable *
204
gvdb_table_new (const gchar  *filename,
205
                gboolean      trusted,
206
                GError      **error)
207
0
{
208
0
  GMappedFile *mapped;
209
0
  GvdbTable *table;
210
0
  GBytes *bytes;
211
212
0
  mapped = g_mapped_file_new (filename, FALSE, error);
213
0
  if (!mapped)
214
0
    return NULL;
215
216
0
  bytes = g_mapped_file_get_bytes (mapped);
217
0
  table = gvdb_table_new_from_bytes (bytes, trusted, error);
218
0
  g_mapped_file_unref (mapped);
219
0
  g_bytes_unref (bytes);
220
221
0
  g_prefix_error (error, "%s: ", filename);
222
223
0
  return table;
224
0
}
225
226
static gboolean
227
gvdb_table_bloom_filter (GvdbTable *file,
228
                          guint32    hash_value)
229
0
{
230
0
  guint32 word, mask;
231
232
0
  if (file->n_bloom_words == 0)
233
0
    return TRUE;
234
235
0
  word = (hash_value / 32) % file->n_bloom_words;
236
0
  mask = 1 << (hash_value & 31);
237
0
  mask |= 1 << ((hash_value >> file->bloom_shift) & 31);
238
239
0
  return (guint32_from_le (file->bloom_words[word]) & mask) == mask;
240
0
}
241
242
static gboolean
243
gvdb_table_check_name (GvdbTable             *file,
244
                       struct gvdb_hash_item *item,
245
                       const gchar           *key,
246
                       guint                  key_length)
247
0
{
248
0
  const gchar *this_key;
249
0
  gsize this_size;
250
0
  guint32 parent;
251
252
0
  this_key = gvdb_table_item_get_key (file, item, &this_size);
253
254
0
  if G_UNLIKELY (this_key == NULL || this_size > key_length)
255
0
    return FALSE;
256
257
0
  key_length -= this_size;
258
259
0
  if G_UNLIKELY (memcmp (this_key, key + key_length, this_size) != 0)
260
0
    return FALSE;
261
262
0
  parent = guint32_from_le (item->parent);
263
0
  if (key_length == 0 && parent == 0xffffffffu)
264
0
    return TRUE;
265
266
0
  if G_LIKELY (parent < file->n_hash_items && this_size > 0)
267
0
    return gvdb_table_check_name (file,
268
0
                                   &file->hash_items[parent],
269
0
                                   key, key_length);
270
271
0
  return FALSE;
272
0
}
273
274
static const struct gvdb_hash_item *
275
gvdb_table_lookup (GvdbTable   *file,
276
                   const gchar *key,
277
                   gchar        type)
278
0
{
279
0
  guint32 hash_value = 5381;
280
0
  guint key_length;
281
0
  guint32 bucket;
282
0
  guint32 lastno;
283
0
  guint32 itemno;
284
285
0
  if G_UNLIKELY (file->n_buckets == 0 || file->n_hash_items == 0)
286
0
    return NULL;
287
288
0
  for (key_length = 0; key[key_length]; key_length++)
289
0
    hash_value = (hash_value * 33) + ((signed char *) key)[key_length];
290
291
0
  if (!gvdb_table_bloom_filter (file, hash_value))
292
0
    return NULL;
293
294
0
  bucket = hash_value % file->n_buckets;
295
0
  itemno = guint32_from_le (file->hash_buckets[bucket]);
296
297
0
  if (bucket == file->n_buckets - 1 ||
298
0
      (lastno = guint32_from_le(file->hash_buckets[bucket + 1])) > file->n_hash_items)
299
0
    lastno = file->n_hash_items;
300
301
0
  while G_LIKELY (itemno < lastno)
302
0
    {
303
0
      struct gvdb_hash_item *item = &file->hash_items[itemno];
304
305
0
      if (hash_value == guint32_from_le (item->hash_value))
306
0
        if G_LIKELY (gvdb_table_check_name (file, item, key, key_length))
307
0
          if G_LIKELY (item->type == type)
308
0
            return item;
309
310
0
      itemno++;
311
0
    }
312
313
0
  return NULL;
314
0
}
315
316
static gboolean
317
gvdb_table_list_from_item (GvdbTable                    *table,
318
                           const struct gvdb_hash_item  *item,
319
                           const guint32_le            **list,
320
                           guint                        *length)
321
0
{
322
0
  gsize size;
323
324
0
  *list = gvdb_table_dereference (table, &item->value.pointer, 4, &size);
325
326
0
  if G_LIKELY (*list == NULL || size % 4)
327
0
    return FALSE;
328
329
0
  *length = size / 4;
330
331
0
  return TRUE;
332
0
}
333
334
/**
335
 * gvdb_table_get_names:
336
 * @table: a #GvdbTable
337
 * @length: (out) (optional): the number of items returned, or %NULL
338
 *
339
 * Gets a list of all names contained in @table.
340
 *
341
 * No call to gvdb_table_get_table(), gvdb_table_list() or
342
 * gvdb_table_get_value() will succeed unless it is for one of the
343
 * names returned by this function.
344
 *
345
 * Note that some names that are returned may still fail for all of the
346
 * above calls in the case of the corrupted file.  Note also that the
347
 * returned strings may not be utf8.
348
 *
349
 * Returns: (array length=length): a %NULL-terminated list of strings, of length @length
350
 **/
351
gchar **
352
gvdb_table_get_names (GvdbTable *table,
353
                      gsize     *length)
354
0
{
355
0
  gchar **names;
356
0
  guint n_names;
357
0
  guint filled;
358
0
  guint total;
359
0
  guint i;
360
361
  /* We generally proceed by iterating over the list of items in the
362
   * hash table (in order of appearance) recording them into an array.
363
   *
364
   * Each item has a parent item (except root items).  The parent item
365
   * forms part of the name of the item.  We could go fetching the
366
   * parent item chain at the point that we encounter each item but then
367
   * we would need to implement some sort of recursion along with checks
368
   * for self-referential items.
369
   *
370
   * Instead, we do a number of passes.  Each pass will build up one
371
   * level of names (starting from the root).  We continue to do passes
372
   * until no more items are left.  The first pass will only add root
373
   * items and each further pass will only add items whose direct parent
374
   * is an item added in the immediately previous pass.  It's also
375
   * possible that items get filled if they follow their parent within a
376
   * particular pass.
377
   *
378
   * At most we will have a number of passes equal to the depth of the
379
   * tree.  Self-referential items will never be filled in (since their
380
   * parent will have never been filled in).  We continue until we have
381
   * a pass that fills in no additional items.
382
   *
383
   * This takes an O(n) algorithm and turns it into O(n*m) where m is
384
   * the depth of the tree, but typically the tree won't be very
385
   * deep and the constant factor of this algorithm is lower (and the
386
   * complexity of coding it, as well).
387
   */
388
389
0
  n_names = table->n_hash_items;
390
0
  names = g_new0 (gchar *, n_names + 1);
391
392
  /* 'names' starts out all-NULL.  On each pass we record the number
393
   * of items changed from NULL to non-NULL in 'filled' so we know if we
394
   * should repeat the loop.  'total' counts the total number of items
395
   * filled.  If 'total' ends up equal to 'n_names' then we know that
396
   * 'names' has been completely filled.
397
   */
398
399
0
  total = 0;
400
0
  do
401
0
    {
402
      /* Loop until we have filled no more entries */
403
0
      filled = 0;
404
405
0
      for (i = 0; i < n_names; i++)
406
0
        {
407
0
          const struct gvdb_hash_item *item = &table->hash_items[i];
408
0
          const gchar *name;
409
0
          gsize name_length;
410
0
          guint32 parent;
411
412
          /* already got it on a previous pass */
413
0
          if (names[i] != NULL)
414
0
            continue;
415
416
0
          parent = guint32_from_le (item->parent);
417
418
0
          if (parent == 0xffffffffu)
419
0
            {
420
              /* it's a root item */
421
0
              name = gvdb_table_item_get_key (table, item, &name_length);
422
423
0
              if (name != NULL)
424
0
                {
425
0
                  names[i] = g_strndup (name, name_length);
426
0
                  filled++;
427
0
                }
428
0
            }
429
430
0
          else if (parent < n_names && names[parent] != NULL)
431
0
            {
432
              /* It's a non-root item whose parent was filled in already.
433
               *
434
               * Calculate the name of this item by combining it with
435
               * its parent name.
436
               */
437
0
              name = gvdb_table_item_get_key (table, item, &name_length);
438
439
0
              if (name != NULL)
440
0
                {
441
0
                  const gchar *parent_name = names[parent];
442
0
                  gsize parent_length;
443
0
                  gchar *fullname;
444
445
0
                  parent_length = strlen (parent_name);
446
0
                  fullname = g_malloc (parent_length + name_length + 1);
447
0
                  memcpy (fullname, parent_name, parent_length);
448
0
                  memcpy (fullname + parent_length, name, name_length);
449
0
                  fullname[parent_length + name_length] = '\0';
450
0
                  names[i] = fullname;
451
0
                  filled++;
452
0
                }
453
0
            }
454
0
        }
455
456
0
      total += filled;
457
0
    }
458
0
  while (filled && total < n_names);
459
460
  /* If the table was corrupted then 'names' may have holes in it.
461
   * Collapse those.
462
   */
463
0
  if G_UNLIKELY (total != n_names)
464
0
    {
465
0
      GPtrArray *fixed_names;
466
467
0
      fixed_names = g_ptr_array_sized_new (n_names + 1  /* NULL terminator */);
468
0
      for (i = 0; i < n_names; i++)
469
0
        if (names[i] != NULL)
470
0
          g_ptr_array_add (fixed_names, names[i]);
471
472
0
      g_free (names);
473
0
      n_names = fixed_names->len;
474
0
      g_ptr_array_add (fixed_names, NULL);
475
0
      names = (gchar **) g_ptr_array_free (fixed_names, FALSE);
476
0
    }
477
478
0
  if (length)
479
0
    {
480
0
      G_STATIC_ASSERT (sizeof (*length) >= sizeof (n_names));
481
0
      *length = n_names;
482
0
    }
483
484
0
  return names;
485
0
}
486
487
/**
488
 * gvdb_table_list:
489
 * @file: a #GvdbTable
490
 * @key: a string
491
 *
492
 * List all of the keys that appear below @key.  The nesting of keys
493
 * within the hash file is defined by the program that created the hash
494
 * file.  One thing is constant: each item in the returned array can be
495
 * concatenated to @key to obtain the full name of that key.
496
 *
497
 * It is not possible to tell from this function if a given key is
498
 * itself a path, a value, or another hash table; you are expected to
499
 * know this for yourself.
500
 *
501
 * You should call g_strfreev() on the return result when you no longer
502
 * require it.
503
 *
504
 * Returns: a %NULL-terminated string array
505
 **/
506
gchar **
507
gvdb_table_list (GvdbTable   *file,
508
                 const gchar *key)
509
0
{
510
0
  const struct gvdb_hash_item *item;
511
0
  const guint32_le *list;
512
0
  gchar **strv;
513
0
  guint length;
514
0
  guint i;
515
516
0
  if ((item = gvdb_table_lookup (file, key, 'L')) == NULL)
517
0
    return NULL;
518
519
0
  if (!gvdb_table_list_from_item (file, item, &list, &length))
520
0
    return NULL;
521
522
0
  strv = g_new (gchar *, length + 1);
523
0
  for (i = 0; i < length; i++)
524
0
    {
525
0
      guint32 itemno = guint32_from_le (list[i]);
526
527
0
      if (itemno < file->n_hash_items)
528
0
        {
529
0
          const struct gvdb_hash_item *child_item;
530
0
          const gchar *string;
531
0
          gsize strsize;
532
533
0
          child_item = file->hash_items + itemno;
534
535
0
          string = gvdb_table_item_get_key (file, child_item, &strsize);
536
537
0
          if (string != NULL)
538
0
            strv[i] = g_strndup (string, strsize);
539
0
          else
540
0
            strv[i] = g_malloc0 (1);
541
0
        }
542
0
      else
543
0
        strv[i] = g_malloc0 (1);
544
0
    }
545
546
0
  strv[i] = NULL;
547
548
0
  return strv;
549
0
}
550
551
/**
552
 * gvdb_table_n_children:
553
 * @file: a #GvdbTable
554
 * @key: a string
555
 *
556
 * Returns the number of keys that appear below @key.
557
 *
558
 * Returns: the number of keys below @key
559
 */
560
guint
561
gvdb_table_n_children (GvdbTable  *file,
562
                       const char *key)
563
0
{
564
0
  const struct gvdb_hash_item *item;
565
0
  const guint32_le *list;
566
0
  guint length;
567
568
0
  if ((item = gvdb_table_lookup (file, key, 'L')) == NULL)
569
0
    return 0;
570
571
0
  if (!gvdb_table_list_from_item (file, item, &list, &length))
572
0
    return 0;
573
574
0
  return length;
575
0
}
576
577
/**
578
 * gvdb_table_has_value:
579
 * @file: a #GvdbTable
580
 * @key: a string
581
 *
582
 * Checks for a value named @key in @file.
583
 *
584
 * Note: this function does not consider non-value nodes (other hash
585
 * tables, for example).
586
 *
587
 * Returns: %TRUE if @key is in the table
588
 **/
589
gboolean
590
gvdb_table_has_value (GvdbTable    *file,
591
                      const gchar  *key)
592
0
{
593
0
  static const struct gvdb_hash_item *item;
594
0
  gsize size;
595
596
0
  item = gvdb_table_lookup (file, key, 'v');
597
598
0
  if (item == NULL)
599
0
    return FALSE;
600
601
0
  return gvdb_table_dereference (file, &item->value.pointer, 8, &size) != NULL;
602
0
}
603
604
static GVariant *
605
gvdb_table_value_from_item (GvdbTable                   *table,
606
                            const struct gvdb_hash_item *item)
607
0
{
608
0
  GVariant *variant, *value;
609
0
  gconstpointer data;
610
0
  GBytes *bytes;
611
0
  gsize size;
612
613
0
  data = gvdb_table_dereference (table, &item->value.pointer, 8, &size);
614
615
0
  if G_UNLIKELY (data == NULL)
616
0
    return NULL;
617
618
0
  bytes = g_bytes_new_from_bytes (table->bytes, ((gchar *) data) - table->data, size);
619
0
  variant = g_variant_new_from_bytes (G_VARIANT_TYPE_VARIANT, bytes, table->trusted);
620
0
  value = g_variant_get_variant (variant);
621
0
  g_variant_unref (variant);
622
0
  g_bytes_unref (bytes);
623
624
0
  return value;
625
0
}
626
627
/**
628
 * gvdb_table_get_value:
629
 * @file: a #GvdbTable
630
 * @key: a string
631
 *
632
 * Looks up a value named @key in @file.
633
 *
634
 * If the value is not found then %NULL is returned.  Otherwise, a new
635
 * #GVariant instance is returned.  The #GVariant does not depend on the
636
 * continued existence of @file.
637
 *
638
 * You should call g_variant_unref() on the return result when you no
639
 * longer require it.
640
 *
641
 * Returns: (transfer full) (nullable): a #GVariant, or %NULL
642
 **/
643
GVariant *
644
gvdb_table_get_value (GvdbTable    *file,
645
                      const gchar  *key)
646
0
{
647
0
  const struct gvdb_hash_item *item;
648
0
  GVariant *value;
649
650
0
  if ((item = gvdb_table_lookup (file, key, 'v')) == NULL)
651
0
    return NULL;
652
653
0
  value = gvdb_table_value_from_item (file, item);
654
655
0
  if (value && file->byteswapped)
656
0
    {
657
0
      GVariant *tmp;
658
659
0
      tmp = g_variant_byteswap (value);
660
0
      g_variant_unref (value);
661
0
      value = tmp;
662
0
    }
663
664
0
  return value;
665
0
}
666
667
/**
668
 * gvdb_table_get_raw_value:
669
 * @table: a #GvdbTable
670
 * @key: a string
671
 *
672
 * Looks up a value named @key in @file.
673
 *
674
 * This call is equivalent to gvdb_table_get_value() except that it
675
 * never byteswaps the value.
676
 *
677
 * Returns: (transfer full) (nullable): a #GVariant, or %NULL
678
 **/
679
GVariant *
680
gvdb_table_get_raw_value (GvdbTable   *table,
681
                          const gchar *key)
682
0
{
683
0
  const struct gvdb_hash_item *item;
684
685
0
  if ((item = gvdb_table_lookup (table, key, 'v')) == NULL)
686
0
    return NULL;
687
688
0
  return gvdb_table_value_from_item (table, item);
689
0
}
690
691
/**
692
 * gvdb_table_get_table:
693
 * @file: a #GvdbTable
694
 * @key: a string
695
 *
696
 * Looks up the hash table named @key in @file.
697
 *
698
 * The toplevel hash table in a #GvdbTable can contain reference to
699
 * child hash tables (and those can contain further references...).
700
 *
701
 * If @key is not found in @file then %NULL is returned.  Otherwise, a
702
 * new #GvdbTable is returned, referring to the child hashtable as
703
 * contained in the file.  This newly-created #GvdbTable does not depend
704
 * on the continued existence of @file.
705
 *
706
 * You should call gvdb_table_free() on the return result when you no
707
 * longer require it.
708
 *
709
 * Returns: a new #GvdbTable, or %NULL
710
 **/
711
GvdbTable *
712
gvdb_table_get_table (GvdbTable   *file,
713
                      const gchar *key)
714
0
{
715
0
  const struct gvdb_hash_item *item;
716
0
  GvdbTable *new;
717
718
0
  item = gvdb_table_lookup (file, key, 'H');
719
720
0
  if (item == NULL)
721
0
    return NULL;
722
723
0
  new = g_slice_new0 (GvdbTable);
724
0
  new->bytes = g_bytes_ref (file->bytes);
725
0
  new->byteswapped = file->byteswapped;
726
0
  new->trusted = file->trusted;
727
0
  new->data = file->data;
728
0
  new->size = file->size;
729
730
0
  gvdb_table_setup_root (new, &item->value.pointer);
731
732
0
  return new;
733
0
}
734
735
/**
736
 * gvdb_table_free:
737
 * @file: a #GvdbTable
738
 *
739
 * Frees @file.
740
 **/
741
void
742
gvdb_table_free (GvdbTable *file)
743
0
{
744
0
  g_bytes_unref (file->bytes);
745
0
  g_slice_free (GvdbTable, file);
746
0
}
747
748
/**
749
 * gvdb_table_is_valid:
750
 * @table: a #GvdbTable
751
 *
752
 * Checks if the table is still valid.
753
 *
754
 * An on-disk GVDB can be marked as invalid.  This happens when the file
755
 * has been replaced.  The appropriate action is typically to reopen the
756
 * file.
757
 *
758
 * Returns: %TRUE if @table is still valid
759
 **/
760
gboolean
761
gvdb_table_is_valid (GvdbTable *table)
762
0
{
763
0
  return !!*table->data;
764
0
}