Add directory index section
authorColin Walters <walters@verbum.org>
Mon, 25 Oct 2010 17:33:01 +0000 (13:33 -0400)
committerColin Walters <walters@verbum.org>
Fri, 3 Dec 2010 21:03:33 +0000 (16:03 -0500)
Use the internal perfect hashing API to add an index to the directory.

To support this, add the notion of additional "sections" to the
typelib.  A section index is inserted between the header and the
directory.

https://bugzilla.gnome.org/show_bug.cgi?id=554943

girepository/girmodule.c
girepository/gitypelib-internal.h
girepository/gitypelib.c

index d15b940f68ba6efa1cd48836d3a0fa09d9d7a6e8..49daaa620e52e03ffa7fe610932aad455ef2f318 100644 (file)
 #include <stdlib.h>
 
 #include "girmodule.h"
+#include "gitypelib-internal.h"
 #include "girnode.h"
 
 #define ALIGN_VALUE(this, boundary) \
   (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
 
+#define NUM_SECTIONS 2
 
 GIrModule *
 _g_ir_module_new (const gchar *name,
@@ -220,6 +222,73 @@ node_cmp_offset_func (gconstpointer a,
   return na->offset - nb->offset;
 }
 
+static void
+alloc_section (guint8 *data, SectionType section_id, guint32 offset)
+{
+  int i;
+  Header *header = (Header*)data;
+  Section *section_data = (Section*)&data[header->sections];
+
+  g_assert (section_id != GI_SECTION_END);
+
+  for (i = 0; i < NUM_SECTIONS; i++)
+    {
+      if (section_data->id == GI_SECTION_END)
+       {
+         section_data->id = section_id;
+         section_data->offset = offset;
+         return;
+       }
+      section_data++;
+    }
+  g_assert_not_reached ();
+}
+
+static guint8*
+add_directory_index_section (guint8 *data, GIrModule *module, guint32 *offset2)
+{
+  DirEntry *entry;
+  Header *header = (Header*)data;
+  GITypelibHashBuilder *dirindex_builder;
+  guint i, n_interfaces;
+  guint16 required_size;
+  guint32 new_offset;
+
+  dirindex_builder = _gi_typelib_hash_builder_new ();
+
+  n_interfaces = ((Header *)data)->n_local_entries;
+
+  for (i = 0; i < n_interfaces; i++)
+    {
+      entry = (DirEntry *)&data[header->directory + (i * header->entry_blob_size)];
+      const char *str = (const char *) (&data[entry->name]);
+      _gi_typelib_hash_builder_add_string (dirindex_builder, str, i);
+    }
+
+  if (!_gi_typelib_hash_builder_prepare (dirindex_builder))
+    {
+      /* This happens if CMPH couldn't create a perfect hash.  So
+       * we just punt and leave no directory index section.
+       */
+      _gi_typelib_hash_builder_destroy (dirindex_builder);
+      return data;
+    }
+
+  alloc_section (data, GI_SECTION_DIRECTORY_INDEX, *offset2);
+
+  required_size = _gi_typelib_hash_builder_get_buffer_size (dirindex_builder);
+
+  new_offset = *offset2 + ALIGN_VALUE (required_size, 4);
+
+  data = g_realloc (data, new_offset);
+
+  _gi_typelib_hash_builder_pack (dirindex_builder, ((guint8*)data) + *offset2, required_size);
+
+  *offset2 = new_offset;
+
+  _gi_typelib_hash_builder_destroy (dirindex_builder);
+  return data;
+}
 
 GITypelib *
 _g_ir_module_build_typelib (GIrModule  *module)
@@ -241,6 +310,7 @@ _g_ir_module_build_typelib (GIrModule  *module)
   GList *nodes_with_attributes;
   char *dependencies;
   guchar *data;
+  Section *section;
 
   header_size = ALIGN_VALUE (sizeof (Header), 4);
   n_local_entries = g_list_length (module->entries);
@@ -301,6 +371,8 @@ _g_ir_module_build_typelib (GIrModule  *module)
   if (module->c_prefix != NULL)
     size += ALIGN_VALUE (strlen (module->c_prefix) + 1, 4);
 
+  size += sizeof (Section) * NUM_SECTIONS;
+
   g_message ("allocating %d bytes (%d header, %d directory, %d entries)\n",
          size, header_size, dir_size, size - header_size - dir_size);
 
@@ -333,7 +405,6 @@ _g_ir_module_build_typelib (GIrModule  *module)
     header->c_prefix = _g_ir_write_string (module->c_prefix, strings, data, &header_size);
   else
     header->c_prefix = 0;
-  header->directory = ALIGN_VALUE (header_size, 4);
   header->entry_blob_size = sizeof (DirEntry);
   header->function_blob_size = sizeof (FunctionBlob);
   header->callback_blob_size = sizeof (CallbackBlob);
@@ -353,10 +424,26 @@ _g_ir_module_build_typelib (GIrModule  *module)
   header->interface_blob_size = sizeof (InterfaceBlob);
   header->union_blob_size = sizeof (UnionBlob);
 
+  offset2 = ALIGN_VALUE (header_size, 4);
+  header->sections = offset2;
+
+  /* Initialize all the sections to _END/0; we fill them in later using
+   * alloc_section().  (Right now there's just the directory index
+   * though, note)
+   */
+  for (i = 0; i < NUM_SECTIONS; i++)
+    {
+      section = (Section*) &data[offset2];
+      section->id = GI_SECTION_END;
+      section->offset = 0;
+      offset2 += sizeof(Section);
+    }
+  header->directory = offset2;
+
   /* fill in directory and content */
   entry = (DirEntry *)&data[header->directory];
 
-  offset2 = header->directory + dir_size;
+  offset2 += dir_size;
 
   for (e = module->entries, i = 0; e; e = e->next, i++)
     {
@@ -452,6 +539,10 @@ _g_ir_module_build_typelib (GIrModule  *module)
 
   data = g_realloc (data, offset2);
   header = (Header*) data;
+
+  data = add_directory_index_section (data, module, &offset2);
+  header = (Header *)data;
+
   length = header->size = offset2;
   typelib = g_typelib_new_from_memory (data, length, &error);
   if (!typelib)
index 227d6da296dd883bd4c315b3b5560f468e656368..e8f5c02507849747ce743b15923448f1c1327aab 100644 (file)
@@ -52,7 +52,7 @@ G_BEGIN_DECLS
  *
  * The typelib has the following general format.
  *
- * typelib ::= header, directory, blobs, attributes, attributedata
+ * typelib ::= header, section-index, directory, blobs, attributes, attributedata
  *
  * directory ::= list of entries
  *
@@ -233,6 +233,7 @@ typedef enum {
  * write parser which continue to work if the format is extended by
  * adding new fields before the first flexible array member in
  * variable-size blobs.
+ * @sections: Offset of section blob array
  *
  * The header structure appears exactly once at the beginning of a typelib.  It is a
  * collection of meta-information, such as the number of entries and dependencies.
@@ -278,10 +279,34 @@ typedef struct {
   guint16 interface_blob_size;
   guint16 union_blob_size;
 
+  guint32 sections;
+
   /* <private> */
-  guint16 padding[7];
+  guint16 padding[5];
 } Header;
 
+typedef enum {
+  GI_SECTION_END = 0,
+  GI_SECTION_DIRECTORY_INDEX = 1
+} SectionType;
+
+/**
+ * Section:
+ * @id: A #SectionType
+ * @offset: Integer offset for this section
+ *
+ * A section is a blob of data that's (at least theoretically) optional,
+ * and may or may not be present in the typelib.  Presently, just used
+ * for the directory index.  This allows a form of dynamic extensibility
+ * with different tradeoffs from the format minor version.
+ *
+ */
+typedef struct {
+  guint32 id;
+  guint32 offset;
+} Section;
+
+
 /**
  * DirEntry:
  * @blob_type: A #GTypelibBlobType
index a0b74d3d603dc75d16cdf546c954497f37e9ce26..90f2c1bbb64363bd79087e4b5aa363a4ba7091af 100644 (file)
@@ -139,25 +139,61 @@ g_typelib_get_dir_entry (GITypelib *typelib,
   return (DirEntry *)&typelib->data[header->directory + (index - 1) * header->entry_blob_size];
 }
 
+static Section *
+get_section_by_id (GITypelib   *typelib,
+                  SectionType  section_type)
+{
+  Header *header = (Header *)typelib->data;
+  Section *section;
+
+  if (header->sections == 0)
+    return NULL;
+
+  for (section = (Section*)&typelib->data[header->sections];
+       section->id != GI_SECTION_END;
+       section++)
+    {
+      if (section->id == section_type)
+       return section;
+    }
+  return NULL;
+}
+
 DirEntry *
-g_typelib_get_dir_entry_by_name (GITypelib  *typelib,
+g_typelib_get_dir_entry_by_name (GITypelib *typelib,
                                 const char *name)
 {
-  Header *header = (Header *)typelib->data;
-  guint n_entries = header->n_local_entries;
+  Section *dirindex;
+  gint i;
+  const char *entry_name;
   DirEntry *entry;
-  guint i;
 
-  for (i = 1; i <= n_entries; i++)
+  dirindex = get_section_by_id (typelib, GI_SECTION_DIRECTORY_INDEX);
+
+  if (dirindex == NULL)
+    {
+      gint n_entries = ((Header *)typelib->data)->n_local_entries;
+      for (i = 1; i <= n_entries; i++)
+       {
+         entry = g_typelib_get_dir_entry (typelib, i);
+         entry_name = g_typelib_get_string (typelib, entry->name);
+         if (strcmp (name, entry_name) == 0)
+           return entry;
+       }
+      return NULL;
+    }
+  else
     {
-      const char *entry_name;
+      guint8 *hash = (guint8*) &typelib->data[dirindex->offset];
+      guint16 index;
 
-      entry = g_typelib_get_dir_entry (typelib, i);
+      index = _gi_typelib_hash_search (hash, name);
+      entry = g_typelib_get_dir_entry (typelib, index + 1);
       entry_name = g_typelib_get_string (typelib, entry->name);
       if (strcmp (name, entry_name) == 0)
        return entry;
+      return NULL;
     }
-  return NULL;
 }
 
 DirEntry *