2 * Copyright © 2010 Codethink Limited
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the licence, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
19 * Author: Ryan Lortie <desrt@desrt.ca>
22 #include "gvdb-reader.h"
23 #include "gvdb-format.h"
34 GvdbRefFunc ref_user_data;
35 GDestroyNotify unref_user_data;
40 const guint32_le *bloom_words;
41 guint32 n_bloom_words;
44 const guint32_le *hash_buckets;
47 struct gvdb_hash_item *hash_items;
52 gvdb_table_item_get_key (GvdbTable *file,
53 const struct gvdb_hash_item *item,
58 start = guint32_from_le (item->key_start);
59 *size = guint16_from_le (item->key_size);
62 if G_UNLIKELY (start > end || end > file->size)
65 return file->data + start;
69 gvdb_table_dereference (GvdbTable *file,
70 const struct gvdb_pointer *pointer,
76 start = guint32_from_le (pointer->start);
77 end = guint32_from_le (pointer->end);
79 if G_UNLIKELY (start > end || end > file->size || start & (alignment - 1))
84 return file->data + start;
88 gvdb_table_setup_root (GvdbTable *file,
89 const struct gvdb_pointer *pointer)
91 const struct gvdb_hash_header *header;
92 guint32 n_bloom_words;
96 header = gvdb_table_dereference (file, pointer, 4, &size);
98 if G_UNLIKELY (header == NULL || size < sizeof *header)
101 size -= sizeof *header;
103 n_bloom_words = guint32_from_le (header->n_bloom_words);
104 n_buckets = guint32_from_le (header->n_buckets);
105 n_bloom_words &= (1u << 27) - 1;
107 if G_UNLIKELY (n_bloom_words * sizeof (guint32_le) > size)
110 file->bloom_words = (gpointer) (header + 1);
111 size -= n_bloom_words * sizeof (guint32_le);
112 file->n_bloom_words = n_bloom_words;
114 if G_UNLIKELY (n_buckets > G_MAXUINT / sizeof (guint32_le) ||
115 n_buckets * sizeof (guint32_le) > size)
118 file->hash_buckets = file->bloom_words + file->n_bloom_words;
119 size -= n_buckets * sizeof (guint32_le);
120 file->n_buckets = n_buckets;
122 if G_UNLIKELY (size % sizeof (struct gvdb_hash_item))
125 file->hash_items = (gpointer) (file->hash_buckets + n_buckets);
126 file->n_hash_items = size / sizeof (struct gvdb_hash_item);
130 new_from_data (const void *data,
135 GDestroyNotify unref,
136 const char *filename,
141 file = g_slice_new0 (GvdbTable);
143 file->size = data_len;
144 file->trusted = trusted;
146 file->ref_user_data = ref;
147 file->unref_user_data = unref;
148 file->user_data = user_data;
150 if (sizeof (struct gvdb_header) <= file->size)
152 const struct gvdb_header *header = (gpointer) file->data;
154 if (header->signature[0] == GVDB_SIGNATURE0 &&
155 header->signature[1] == GVDB_SIGNATURE1 &&
156 guint32_from_le (header->version) == 0)
157 file->byteswapped = FALSE;
159 else if (header->signature[0] == GVDB_SWAPPED_SIGNATURE0 &&
160 header->signature[1] == GVDB_SWAPPED_SIGNATURE1 &&
161 guint32_from_le (header->version) == 0)
162 file->byteswapped = TRUE;
167 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL,
168 "%s: invalid header", filename);
170 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL,
171 "invalid gvdb header");
172 g_slice_free (GvdbTable, file);
179 gvdb_table_setup_root (file, &header->root);
187 * @filename: the path to the hash file
188 * @trusted: if the contents of @filename are trusted
189 * @error: %NULL, or a pointer to a %NULL #GError
191 * Creates a new #GvdbTable from the contents of the file found at
194 * The only time this function fails is if the file cannot be opened.
195 * In that case, the #GError that is returned will be an error from
196 * g_mapped_file_new().
198 * An empty or otherwise corrupted file is considered to be a valid
199 * #GvdbTable with no entries.
201 * You should call gvdb_table_unref() on the return result when you no
204 * Returns: a new #GvdbTable
207 gvdb_table_new (const gchar *filename,
213 if ((mapped = g_mapped_file_new (filename, FALSE, error)) == NULL)
216 return new_from_data (g_mapped_file_get_contents (mapped),
217 g_mapped_file_get_length (mapped),
220 (GvdbRefFunc)g_mapped_file_ref,
221 (GDestroyNotify)g_mapped_file_unref,
227 * gvdb_table_new_from_data:
229 * @data_len: the length of @data in bytes
230 * @trusted: if the contents of @data are trusted
231 * @user_data: User supplied data that owns @data
232 * @ref: Ref function for @user_data
233 * @unref: Unref function for @user_data
235 * Creates a new #GvdbTable from the data in @data.
237 * An empty or otherwise corrupted data is considered to be a valid
238 * #GvdbTable with no entries.
240 * You should call gvdb_table_unref() on the return result when you no
243 * Returns: a new #GvdbTable
246 gvdb_table_new_from_data (const void *data,
251 GDestroyNotify unref,
254 return new_from_data (data, data_len,
256 user_data, ref, unref,
262 gvdb_table_bloom_filter (GvdbTable *file,
267 if (file->n_bloom_words == 0)
270 word = (hash_value / 32) % file->n_bloom_words;
271 mask = 1 << (hash_value & 31);
272 mask |= 1 << ((hash_value >> file->bloom_shift) & 31);
274 return (guint32_from_le (file->bloom_words[word]) & mask) == mask;
278 gvdb_table_check_name (GvdbTable *file,
279 struct gvdb_hash_item *item,
283 const gchar *this_key;
287 this_key = gvdb_table_item_get_key (file, item, &this_size);
289 if G_UNLIKELY (this_key == NULL || this_size > key_length)
292 key_length -= this_size;
294 if G_UNLIKELY (memcmp (this_key, key + key_length, this_size) != 0)
297 parent = guint32_from_le (item->parent);
298 if (key_length == 0 && parent == 0xffffffffu)
301 if G_LIKELY (parent < file->n_hash_items && this_size > 0)
302 return gvdb_table_check_name (file,
303 &file->hash_items[parent],
309 static const struct gvdb_hash_item *
310 gvdb_table_lookup (GvdbTable *file,
314 guint32 hash_value = 5381;
320 if G_UNLIKELY (file->n_buckets == 0 || file->n_hash_items == 0)
323 for (key_length = 0; key[key_length]; key_length++)
324 hash_value = (hash_value * 33) + ((signed char *) key)[key_length];
326 if (!gvdb_table_bloom_filter (file, hash_value))
329 bucket = hash_value % file->n_buckets;
330 itemno = guint32_from_le (file->hash_buckets[bucket]);
332 if (bucket == file->n_buckets - 1 ||
333 (lastno = guint32_from_le(file->hash_buckets[bucket + 1])) > file->n_hash_items)
334 lastno = file->n_hash_items;
336 while G_LIKELY (itemno < lastno)
338 struct gvdb_hash_item *item = &file->hash_items[itemno];
340 if (hash_value == guint32_from_le (item->hash_value))
341 if G_LIKELY (gvdb_table_check_name (file, item, key, key_length))
342 if G_LIKELY (item->type == type)
351 static const struct gvdb_hash_item *
352 gvdb_table_get_item (GvdbTable *table,
355 guint32 item_no_native = guint32_from_le (item_no);
357 if G_LIKELY (item_no_native < table->n_hash_items)
358 return table->hash_items + item_no_native;
364 gvdb_table_list_from_item (GvdbTable *table,
365 const struct gvdb_hash_item *item,
366 const guint32_le **list,
371 *list = gvdb_table_dereference (table, &item->value.pointer, 4, &size);
373 if G_LIKELY (*list == NULL || size % 4)
384 * @file: a #GvdbTable
387 * List all of the keys that appear below @key. The nesting of keys
388 * within the hash file is defined by the program that created the hash
389 * file. One thing is constant: each item in the returned array can be
390 * concatenated to @key to obtain the full name of that key.
392 * It is not possible to tell from this function if a given key is
393 * itself a path, a value, or another hash table; you are expected to
394 * know this for yourself.
396 * You should call g_strfreev() on the return result when you no longer
399 * Returns: a %NULL-terminated string array
402 gvdb_table_list (GvdbTable *file,
405 const struct gvdb_hash_item *item;
406 const guint32_le *list;
411 if ((item = gvdb_table_lookup (file, key, 'L')) == NULL)
414 if (!gvdb_table_list_from_item (file, item, &list, &length))
417 strv = g_new (gchar *, length + 1);
418 for (i = 0; i < length; i++)
420 guint32 itemno = guint32_from_le (list[i]);
422 if (itemno < file->n_hash_items)
424 const struct gvdb_hash_item *item;
428 item = file->hash_items + itemno;
430 string = gvdb_table_item_get_key (file, item, &strsize);
433 strv[i] = g_strndup (string, strsize);
435 strv[i] = g_malloc0 (1);
438 strv[i] = g_malloc0 (1);
447 * gvdb_table_has_value:
448 * @file: a #GvdbTable
451 * Checks for a value named @key in @file.
453 * Note: this function does not consider non-value nodes (other hash
454 * tables, for example).
456 * Returns: %TRUE if @key is in the table
459 gvdb_table_has_value (GvdbTable *file,
462 return gvdb_table_lookup (file, key, 'v') != NULL;
466 gvdb_table_value_from_item (GvdbTable *table,
467 const struct gvdb_hash_item *item)
469 GVariant *variant, *value;
473 data = gvdb_table_dereference (table, &item->value.pointer, 8, &size);
475 if G_UNLIKELY (data == NULL)
478 variant = g_variant_new_from_data (G_VARIANT_TYPE_VARIANT,
479 data, size, table->trusted,
480 table->unref_user_data,
481 table->ref_user_data ? table->ref_user_data (table->user_data) : table->user_data);
482 value = g_variant_get_variant (variant);
483 g_variant_unref (variant);
489 * gvdb_table_get_value:
490 * @file: a #GvdbTable
493 * Looks up a value named @key in @file.
495 * If the value is not found then %NULL is returned. Otherwise, a new
496 * #GVariant instance is returned. The #GVariant does not depend on the
497 * continued existence of @file.
499 * You should call g_variant_unref() on the return result when you no
502 * Returns: a #GVariant, or %NULL
505 gvdb_table_get_value (GvdbTable *file,
508 const struct gvdb_hash_item *item;
511 if ((item = gvdb_table_lookup (file, key, 'v')) == NULL)
514 value = gvdb_table_value_from_item (file, item);
516 if (value && file->byteswapped)
520 tmp = g_variant_byteswap (value);
521 g_variant_unref (value);
529 * gvdb_table_get_raw_value:
530 * @table: a #GvdbTable
533 * Looks up a value named @key in @file.
535 * This call is equivalent to gvdb_table_get_value() except that it
536 * never byteswaps the value.
538 * Returns: a #GVariant, or %NULL
541 gvdb_table_get_raw_value (GvdbTable *table,
544 const struct gvdb_hash_item *item;
546 if ((item = gvdb_table_lookup (table, key, 'v')) == NULL)
549 return gvdb_table_value_from_item (table, item);
553 * gvdb_table_get_table:
554 * @file: a #GvdbTable
557 * Looks up the hash table named @key in @file.
559 * The toplevel hash table in a #GvdbTable can contain reference to
560 * child hash tables (and those can contain further references...).
562 * If @key is not found in @file then %NULL is returned. Otherwise, a
563 * new #GvdbTable is returned, referring to the child hashtable as
564 * contained in the file. This newly-created #GvdbTable does not depend
565 * on the continued existence of @file.
567 * You should call gvdb_table_unref() on the return result when you no
570 * Returns: a new #GvdbTable, or %NULL
573 gvdb_table_get_table (GvdbTable *file,
576 const struct gvdb_hash_item *item;
579 item = gvdb_table_lookup (file, key, 'H');
584 new = g_slice_new0 (GvdbTable);
585 new->user_data = file->ref_user_data ? file->ref_user_data (file->user_data) : file->user_data;
586 new->ref_user_data = file->ref_user_data;
587 new->unref_user_data = file->unref_user_data;
588 new->byteswapped = file->byteswapped;
589 new->trusted = file->trusted;
590 new->data = file->data;
591 new->size = file->size;
594 gvdb_table_setup_root (new, &item->value.pointer);
601 * @file: a #GvdbTable
603 * Increases the reference count on @file.
605 * Returns: a new reference on @file
608 gvdb_table_ref (GvdbTable *file)
610 g_atomic_int_inc (&file->ref_count);
617 * @file: a #GvdbTable
619 * Decreases the reference count on @file, possibly freeing it.
624 gvdb_table_unref (GvdbTable *file)
626 if (g_atomic_int_dec_and_test (&file->ref_count))
628 if (file->unref_user_data)
629 file->unref_user_data (file->user_data);
630 g_slice_free (GvdbTable, file);
635 * gvdb_table_is_valid:
636 * @table: a #GvdbTable
638 * Checks if the table is still valid.
640 * An on-disk GVDB can be marked as invalid. This happens when the file
641 * has been replaced. The appropriate action is typically to reopen the
644 * Returns: %TRUE if @table is still valid
647 gvdb_table_is_valid (GvdbTable *table)
649 return !!*table->data;
654 * @table: a #GvdbTable
655 * @key: a key corresponding to a list
656 * @open_func: the #GvdbWalkOpenFunc
657 * @value_func: the #GvdbWalkValueFunc
658 * @close_func: the #GvdbWalkCloseFunc
659 * @user_data: data to pass to the callbacks
661 * Looks up the list at @key and iterate over the items in it.
663 * First, @open_func is called to signal that we are starting to iterate over
664 * the list. Then the list is iterated. When all items in the list have been
665 * iterated over, the @close_func is called.
667 * When iterating, if a given item in the list is a value then @value_func is
670 * If a given item in the list is itself a list then @open_func is called. If
671 * that function returns %TRUE then the walk begins iterating the items in the
672 * sublist, until there are no more items, at which point a matching
673 * @close_func call is made. If @open_func returns %FALSE then no iteration of
674 * the sublist occurs and no corresponding @close_func call is made.
677 gvdb_table_walk (GvdbTable *table,
679 GvdbWalkOpenFunc open_func,
680 GvdbWalkValueFunc value_func,
681 GvdbWalkCloseFunc close_func,
684 const struct gvdb_hash_item *item;
685 const guint32_le *pointers[64];
686 const guint32_le *enders[64];
687 gsize name_lengths[64];
690 item = gvdb_table_lookup (table, key, 'L');
698 close_func (name_lengths[index], user_data);
701 while (pointers[index] < enders[index])
706 item = gvdb_table_get_item (table, *pointers[index]++);
710 (name = gvdb_table_item_get_key (table, item, &name_len)))
712 if (item->type == 'L')
714 if (open_func (name, name_len, user_data))
719 g_assert (index < 64);
721 gvdb_table_list_from_item (table, item,
724 enders[index] = pointers[index] + length;
725 name_lengths[index] = name_len;
728 else if (item->type == 'v')
732 value = gvdb_table_value_from_item (table, item);
736 if (table->byteswapped)
740 tmp = g_variant_byteswap (value);
741 g_variant_unref (value);
745 value_func (name, name_len, value, user_data);
746 g_variant_unref (value);