From be21cd56b86a96010f8b1324c62215afb8c7946f Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 25 Oct 2010 13:33:01 -0400 Subject: Add directory index section 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 | 95 ++++++++++++++++++++++++++++++++++++++- girepository/gitypelib-internal.h | 29 +++++++++++- girepository/gitypelib.c | 52 +++++++++++++++++---- 3 files changed, 164 insertions(+), 12 deletions(-) diff --git a/girepository/girmodule.c b/girepository/girmodule.c index d15b940f..49daaa62 100644 --- a/girepository/girmodule.c +++ b/girepository/girmodule.c @@ -23,11 +23,13 @@ #include #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) diff --git a/girepository/gitypelib-internal.h b/girepository/gitypelib-internal.h index 227d6da2..e8f5c025 100644 --- a/girepository/gitypelib-internal.h +++ b/girepository/gitypelib-internal.h @@ -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; + /* */ - 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 diff --git a/girepository/gitypelib.c b/girepository/gitypelib.c index a0b74d3d..90f2c1bb 100644 --- a/girepository/gitypelib.c +++ b/girepository/gitypelib.c @@ -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 * -- cgit v1.2.1