diff --git a/girepository/cmph-bdz-test.c b/girepository/cmph-bdz-test.c new file mode 100644 index 000000000..92c445f8f --- /dev/null +++ b/girepository/cmph-bdz-test.c @@ -0,0 +1,140 @@ +/* GObject introspection: Test cmph hashing + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "cmph.h" + +static cmph_t * +build (void) +{ + cmph_config_t *config; + cmph_io_adapter_t *io; + char **strings; + cmph_t *c; + guint32 size; + + strings = g_strsplit ("foo,bar,baz", ",", -1); + + io = cmph_io_vector_adapter (strings, g_strv_length (strings)); + config = cmph_config_new (io); + cmph_config_set_algo (config, CMPH_BDZ); + + c = cmph_new (config); + size = cmph_size (c); + g_assert (size == g_strv_length (strings)); + + return c; +} + +static void +assert_hashes_unique (guint n_hashes, + guint32* hashes) +{ + guint i; + + for (i = 0; i < n_hashes; i++) + { + guint j = 0; + for (j = 0; j < n_hashes; j++) + { + if (j != i) + g_assert (hashes[i] != hashes[j]); + } + } +} + +static void +test_search (void) +{ + cmph_t *c = build(); + guint i; + guint32 hash; + guint32 hashes[3]; + guint32 size; + + size = cmph_size (c); + + i = 0; + hash = cmph_search (c, "foo", 3); + g_assert (hash >= 0 && hash < size); + hashes[i++] = hash; + + hash = cmph_search (c, "bar", 3); + g_assert (hash >= 0 && hash < size); + hashes[i++] = hash; + + hash = cmph_search (c, "baz", 3); + g_assert (hash >= 0 && hash < size); + hashes[i++] = hash; + + assert_hashes_unique (G_N_ELEMENTS (hashes), &hashes[0]); +} + +static void +test_search_packed (void) +{ + cmph_t *c = build(); + guint32 bufsize; + guint i; + guint32 hash; + guint32 hashes[3]; + guint32 size; + guint8 *buf; + + bufsize = cmph_packed_size (c); + buf = g_malloc (bufsize); + cmph_pack (c, buf); + + size = cmph_size (c); + + cmph_destroy (c); + c = NULL; + + i = 0; + hash = cmph_search_packed (buf, "foo", 3); + g_assert (hash >= 0 && hash < size); + hashes[i++] = hash; + + hash = cmph_search_packed (buf, "bar", 3); + g_assert (hash >= 0 && hash < size); + hashes[i++] = hash; + + hash = cmph_search_packed (buf, "baz", 3); + g_assert (hash >= 0 && hash < size); + hashes[i++] = hash; + + assert_hashes_unique (G_N_ELEMENTS (hashes), &hashes[0]); +} + +int +main(int argc, char **argv) +{ + gint ret; + + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/cmph-bdz/search", test_search); + g_test_add_func ("/cmph-bdz/search-packed", test_search_packed); + + ret = g_test_run (); + + return ret; +} + diff --git a/girepository/cmph/README-CMPH-IMPORT.txt b/girepository/cmph/README-CMPH-IMPORT.txt new file mode 100644 index 000000000..a1c23c246 --- /dev/null +++ b/girepository/cmph/README-CMPH-IMPORT.txt @@ -0,0 +1,5 @@ +This import of CMPH was made from revision bfdcc3a3a18dfb9 of +git://cmph.git.sourceforge.net/gitroot/cmph/cmph + +Only the following files were taken, and everything else deleted: +COPYING src/*.[ch] diff --git a/girepository/cmph/bdz.c b/girepository/cmph/bdz.c new file mode 100644 index 000000000..e70f1183e --- /dev/null +++ b/girepository/cmph/bdz.c @@ -0,0 +1,721 @@ +#include "bdz.h" +#include "cmph_structs.h" +#include "bdz_structs.h" +#include "hash.h" +#include "bitbool.h" + +#include +#include +#include +#include +#include +#include +//#define DEBUG +#include "debug.h" +#define UNASSIGNED 3U +#define NULL_EDGE 0xffffffff + +//cmph_uint32 ngrafos = 0; +//cmph_uint32 ngrafos_aciclicos = 0; +// table used for looking up the number of assigned vertices a 8-bit integer +const cmph_uint8 bdz_lookup_table[] = +{ +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +4, 4, 4, 3, 4, 4, 4, 3, 4, 4, 4, 3, 3, 3, 3, 2, +3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, +3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, +3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, +3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 1, +2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 1, 1, 1, 0 +}; + +typedef struct +{ + cmph_uint32 vertices[3]; + cmph_uint32 next_edges[3]; +}bdz_edge_t; + +typedef cmph_uint32 * bdz_queue_t; + +static void bdz_alloc_queue(bdz_queue_t * queuep, cmph_uint32 nedges) +{ + (*queuep)=malloc(nedges*sizeof(cmph_uint32)); +}; +static void bdz_free_queue(bdz_queue_t * queue) +{ + free(*queue); +}; + +typedef struct +{ + cmph_uint32 nedges; + bdz_edge_t * edges; + cmph_uint32 * first_edge; + cmph_uint8 * vert_degree; +}bdz_graph3_t; + + +static void bdz_alloc_graph3(bdz_graph3_t * graph3, cmph_uint32 nedges, cmph_uint32 nvertices) +{ + graph3->edges=malloc(nedges*sizeof(bdz_edge_t)); + graph3->first_edge=malloc(nvertices*sizeof(cmph_uint32)); + graph3->vert_degree=malloc((size_t)nvertices); +}; +static void bdz_init_graph3(bdz_graph3_t * graph3, cmph_uint32 nedges, cmph_uint32 nvertices) +{ + memset(graph3->first_edge,0xff,nvertices*sizeof(cmph_uint32)); + memset(graph3->vert_degree,0,(size_t)nvertices); + graph3->nedges=0; +}; +static void bdz_free_graph3(bdz_graph3_t *graph3) +{ + free(graph3->edges); + free(graph3->first_edge); + free(graph3->vert_degree); +}; + +static void bdz_partial_free_graph3(bdz_graph3_t *graph3) +{ + free(graph3->first_edge); + free(graph3->vert_degree); + graph3->first_edge = NULL; + graph3->vert_degree = NULL; +}; + +static void bdz_add_edge(bdz_graph3_t * graph3, cmph_uint32 v0, cmph_uint32 v1, cmph_uint32 v2) +{ + graph3->edges[graph3->nedges].vertices[0]=v0; + graph3->edges[graph3->nedges].vertices[1]=v1; + graph3->edges[graph3->nedges].vertices[2]=v2; + graph3->edges[graph3->nedges].next_edges[0]=graph3->first_edge[v0]; + graph3->edges[graph3->nedges].next_edges[1]=graph3->first_edge[v1]; + graph3->edges[graph3->nedges].next_edges[2]=graph3->first_edge[v2]; + graph3->first_edge[v0]=graph3->first_edge[v1]=graph3->first_edge[v2]=graph3->nedges; + graph3->vert_degree[v0]++; + graph3->vert_degree[v1]++; + graph3->vert_degree[v2]++; + graph3->nedges++; +}; + +static void bdz_dump_graph(bdz_graph3_t* graph3, cmph_uint32 nedges, cmph_uint32 nvertices) +{ + cmph_uint32 i; + for(i=0;iedges[i].vertices[0], + graph3->edges[i].vertices[1],graph3->edges[i].vertices[2]); + printf(" nexts %d %d %d",graph3->edges[i].next_edges[0], + graph3->edges[i].next_edges[1],graph3->edges[i].next_edges[2]); + }; + + for(i=0;ifirst_edge[i]); + + }; +}; + +static void bdz_remove_edge(bdz_graph3_t * graph3, cmph_uint32 curr_edge) +{ + cmph_uint32 i,j=0,vert,edge1,edge2; + for(i=0;i<3;i++){ + vert=graph3->edges[curr_edge].vertices[i]; + edge1=graph3->first_edge[vert]; + edge2=NULL_EDGE; + while(edge1!=curr_edge&&edge1!=NULL_EDGE){ + edge2=edge1; + if(graph3->edges[edge1].vertices[0]==vert){ + j=0; + } else if(graph3->edges[edge1].vertices[1]==vert){ + j=1; + } else + j=2; + edge1=graph3->edges[edge1].next_edges[j]; + }; + if(edge1==NULL_EDGE){ + printf("\nerror remove edge %d dump graph",curr_edge); + bdz_dump_graph(graph3,graph3->nedges,graph3->nedges+graph3->nedges/4); + exit(-1); + }; + + if(edge2!=NULL_EDGE){ + graph3->edges[edge2].next_edges[j] = + graph3->edges[edge1].next_edges[i]; + } else + graph3->first_edge[vert]= + graph3->edges[edge1].next_edges[i]; + graph3->vert_degree[vert]--; + }; + +}; + +static int bdz_generate_queue(cmph_uint32 nedges, cmph_uint32 nvertices, bdz_queue_t queue, bdz_graph3_t* graph3) +{ + cmph_uint32 i,v0,v1,v2; + cmph_uint32 queue_head=0,queue_tail=0; + cmph_uint32 curr_edge; + cmph_uint32 tmp_edge; + cmph_uint8 * marked_edge =malloc((size_t)(nedges >> 3) + 1); + memset(marked_edge, 0, (size_t)(nedges >> 3) + 1); + + for(i=0;iedges[i].vertices[0]; + v1=graph3->edges[i].vertices[1]; + v2=graph3->edges[i].vertices[2]; + if(graph3->vert_degree[v0]==1 || + graph3->vert_degree[v1]==1 || + graph3->vert_degree[v2]==1){ + if(!GETBIT(marked_edge,i)) { + queue[queue_head++]=i; + SETBIT(marked_edge,i); + } + }; + }; + while(queue_tail!=queue_head){ + curr_edge=queue[queue_tail++]; + bdz_remove_edge(graph3,curr_edge); + v0=graph3->edges[curr_edge].vertices[0]; + v1=graph3->edges[curr_edge].vertices[1]; + v2=graph3->edges[curr_edge].vertices[2]; + if(graph3->vert_degree[v0]==1 ) { + tmp_edge=graph3->first_edge[v0]; + if(!GETBIT(marked_edge,tmp_edge)) { + queue[queue_head++]=tmp_edge; + SETBIT(marked_edge,tmp_edge); + }; + + }; + if(graph3->vert_degree[v1]==1) { + tmp_edge=graph3->first_edge[v1]; + if(!GETBIT(marked_edge,tmp_edge)){ + queue[queue_head++]=tmp_edge; + SETBIT(marked_edge,tmp_edge); + }; + + }; + if(graph3->vert_degree[v2]==1){ + tmp_edge=graph3->first_edge[v2]; + if(!GETBIT(marked_edge,tmp_edge)){ + queue[queue_head++]=tmp_edge; + SETBIT(marked_edge,tmp_edge); + }; + }; + }; + free(marked_edge); + return (int)(queue_head-nedges);/* returns 0 if successful otherwies return negative number*/ +}; + +static int bdz_mapping(cmph_config_t *mph, bdz_graph3_t* graph3, bdz_queue_t queue); +static void assigning(bdz_config_data_t *bdz, bdz_graph3_t* graph3, bdz_queue_t queue); +static void ranking(bdz_config_data_t *bdz); +static cmph_uint32 rank(cmph_uint32 b, cmph_uint32 * ranktable, cmph_uint8 * g, cmph_uint32 vertex); + +bdz_config_data_t *bdz_config_new(void) +{ + bdz_config_data_t *bdz; + bdz = (bdz_config_data_t *)malloc(sizeof(bdz_config_data_t)); + assert(bdz); + memset(bdz, 0, sizeof(bdz_config_data_t)); + bdz->hashfunc = CMPH_HASH_JENKINS; + bdz->g = NULL; + bdz->hl = NULL; + bdz->k = 0; //kth index in ranktable, $k = log_2(n=3r)/\varepsilon$ + bdz->b = 7; // number of bits of k + bdz->ranktablesize = 0; //number of entries in ranktable, $n/k +1$ + bdz->ranktable = NULL; // rank table + return bdz; +} + +void bdz_config_destroy(cmph_config_t *mph) +{ + bdz_config_data_t *data = (bdz_config_data_t *)mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void bdz_config_set_b(cmph_config_t *mph, cmph_uint32 b) +{ + bdz_config_data_t *bdz = (bdz_config_data_t *)mph->data; + if (b <= 2 || b > 10) b = 7; // validating restrictions over parameter b. + bdz->b = (cmph_uint8)b; + DEBUGP("b: %u\n", b); + +} + +void bdz_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + bdz_config_data_t *bdz = (bdz_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 1) break; //bdz only uses one linear hash function + bdz->hashfunc = *hashptr; + ++i, ++hashptr; + } +} + +cmph_t *bdz_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + bdz_data_t *bdzf = NULL; + cmph_uint32 iterations; + bdz_queue_t edges; + bdz_graph3_t graph3; + bdz_config_data_t *bdz = (bdz_config_data_t *)mph->data; + #ifdef CMPH_TIMING + double construction_time_begin = 0.0; + double construction_time = 0.0; + ELAPSED_TIME_IN_SECONDS(&construction_time_begin); + #endif + + + if (c == 0) c = 1.23; // validating restrictions over parameter c. + DEBUGP("c: %f\n", c); + bdz->m = mph->key_source->nkeys; + bdz->r = (cmph_uint32)ceil((c * mph->key_source->nkeys)/3); + if ((bdz->r % 2) == 0) bdz->r+=1; + bdz->n = 3*bdz->r; + + bdz->k = (1U << bdz->b); + DEBUGP("b: %u -- k: %u\n", bdz->b, bdz->k); + + bdz->ranktablesize = (cmph_uint32)ceil(bdz->n/(double)bdz->k); + DEBUGP("ranktablesize: %u\n", bdz->ranktablesize); + + + bdz_alloc_graph3(&graph3, bdz->m, bdz->n); + bdz_alloc_queue(&edges,bdz->m); + DEBUGP("Created hypergraph\n"); + + DEBUGP("m (edges): %u n (vertices): %u r: %u c: %f \n", bdz->m, bdz->n, bdz->r, c); + + // Mapping step + iterations = 1000; + if (mph->verbosity) + { + fprintf(stderr, "Entering mapping step for mph creation of %u keys with graph sized %u\n", bdz->m, bdz->n); + } + while(1) + { + int ok; + DEBUGP("linear hash function \n"); + bdz->hl = hash_state_new(bdz->hashfunc, 15); + + ok = bdz_mapping(mph, &graph3, edges); + //ok = 0; + if (!ok) + { + --iterations; + hash_state_destroy(bdz->hl); + bdz->hl = NULL; + DEBUGP("%u iterations remaining\n", iterations); + if (mph->verbosity) + { + fprintf(stderr, "acyclic graph creation failure - %u iterations remaining\n", iterations); + } + if (iterations == 0) break; + } + else break; + } + + if (iterations == 0) + { + bdz_free_queue(&edges); + bdz_free_graph3(&graph3); + return NULL; + } + bdz_partial_free_graph3(&graph3); + // Assigning step + if (mph->verbosity) + { + fprintf(stderr, "Entering assigning step for mph creation of %u keys with graph sized %u\n", bdz->m, bdz->n); + } + assigning(bdz, &graph3, edges); + + bdz_free_queue(&edges); + bdz_free_graph3(&graph3); + if (mph->verbosity) + { + fprintf(stderr, "Entering ranking step for mph creation of %u keys with graph sized %u\n", bdz->m, bdz->n); + } + ranking(bdz); + #ifdef CMPH_TIMING + ELAPSED_TIME_IN_SECONDS(&construction_time); + #endif + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + bdzf = (bdz_data_t *)malloc(sizeof(bdz_data_t)); + bdzf->g = bdz->g; + bdz->g = NULL; //transfer memory ownership + bdzf->hl = bdz->hl; + bdz->hl = NULL; //transfer memory ownership + bdzf->ranktable = bdz->ranktable; + bdz->ranktable = NULL; //transfer memory ownership + bdzf->ranktablesize = bdz->ranktablesize; + bdzf->k = bdz->k; + bdzf->b = bdz->b; + bdzf->n = bdz->n; + bdzf->m = bdz->m; + bdzf->r = bdz->r; + mphf->data = bdzf; + mphf->size = bdz->m; + + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + + + #ifdef CMPH_TIMING + register cmph_uint32 space_usage = bdz_packed_size(mphf)*8; + register cmph_uint32 keys_per_bucket = 1; + construction_time = construction_time - construction_time_begin; + fprintf(stdout, "%u\t%.2f\t%u\t%.4f\t%.4f\n", bdz->m, bdz->m/(double)bdz->n, keys_per_bucket, construction_time, space_usage/(double)bdz->m); + #endif + + return mphf; +} + + +static int bdz_mapping(cmph_config_t *mph, bdz_graph3_t* graph3, bdz_queue_t queue) +{ + cmph_uint32 e; + int cycles = 0; + cmph_uint32 hl[3]; + bdz_config_data_t *bdz = (bdz_config_data_t *)mph->data; + bdz_init_graph3(graph3, bdz->m, bdz->n); + mph->key_source->rewind(mph->key_source->data); + for (e = 0; e < mph->key_source->nkeys; ++e) + { + cmph_uint32 h0, h1, h2; + cmph_uint32 keylen; + char *key = NULL; + mph->key_source->read(mph->key_source->data, &key, &keylen); + hash_vector(bdz->hl, key, keylen,hl); + h0 = hl[0] % bdz->r; + h1 = hl[1] % bdz->r + bdz->r; + h2 = hl[2] % bdz->r + (bdz->r << 1); + mph->key_source->dispose(mph->key_source->data, key, keylen); + bdz_add_edge(graph3,h0,h1,h2); + } + cycles = bdz_generate_queue(bdz->m, bdz->n, queue, graph3); + return (cycles == 0); +} + +static void assigning(bdz_config_data_t *bdz, bdz_graph3_t* graph3, bdz_queue_t queue) +{ + cmph_uint32 i; + cmph_uint32 nedges=graph3->nedges; + cmph_uint32 curr_edge; + cmph_uint32 v0,v1,v2; + cmph_uint8 * marked_vertices =malloc((size_t)(bdz->n >> 3) + 1); + cmph_uint32 sizeg = (cmph_uint32)ceil(bdz->n/4.0); + bdz->g = (cmph_uint8 *)calloc((size_t)(sizeg), sizeof(cmph_uint8)); + memset(marked_vertices, 0, (size_t)(bdz->n >> 3) + 1); + memset(bdz->g, 0xff, (size_t)(sizeg)); + + for(i=nedges-1;i+1>0;i--){ + curr_edge=queue[i]; + v0=graph3->edges[curr_edge].vertices[0]; + v1=graph3->edges[curr_edge].vertices[1]; + v2=graph3->edges[curr_edge].vertices[2]; + DEBUGP("B:%u %u %u -- %u %u %u\n", v0, v1, v2, GETVALUE(bdz->g, v0), GETVALUE(bdz->g, v1), GETVALUE(bdz->g, v2)); + if(!GETBIT(marked_vertices, v0)){ + if(!GETBIT(marked_vertices,v1)) + { + SETVALUE1(bdz->g, v1, UNASSIGNED); + SETBIT(marked_vertices, v1); + } + if(!GETBIT(marked_vertices,v2)) + { + SETVALUE1(bdz->g, v2, UNASSIGNED); + SETBIT(marked_vertices, v2); + } + SETVALUE1(bdz->g, v0, (6-(GETVALUE(bdz->g, v1) + GETVALUE(bdz->g,v2)))%3); + SETBIT(marked_vertices, v0); + } else if(!GETBIT(marked_vertices, v1)) { + if(!GETBIT(marked_vertices, v2)) + { + SETVALUE1(bdz->g, v2, UNASSIGNED); + SETBIT(marked_vertices, v2); + } + SETVALUE1(bdz->g, v1, (7-(GETVALUE(bdz->g, v0)+GETVALUE(bdz->g, v2)))%3); + SETBIT(marked_vertices, v1); + }else { + SETVALUE1(bdz->g, v2, (8-(GETVALUE(bdz->g,v0)+GETVALUE(bdz->g, v1)))%3); + SETBIT(marked_vertices, v2); + } + DEBUGP("A:%u %u %u -- %u %u %u\n", v0, v1, v2, GETVALUE(bdz->g, v0), GETVALUE(bdz->g, v1), GETVALUE(bdz->g, v2)); + }; + free(marked_vertices); +} + + +static void ranking(bdz_config_data_t *bdz) +{ + cmph_uint32 i, j, offset = 0U, count = 0U, size = (bdz->k >> 2U), nbytes_total = (cmph_uint32)ceil(bdz->n/4.0), nbytes; + bdz->ranktable = (cmph_uint32 *)calloc((size_t)bdz->ranktablesize, sizeof(cmph_uint32)); + // ranktable computation + bdz->ranktable[0] = 0; + i = 1; + while(1) + { + if(i == bdz->ranktablesize) break; + nbytes = size < nbytes_total? size : nbytes_total; + for(j = 0; j < nbytes; j++) + { + count += bdz_lookup_table[*(bdz->g + offset + j)]; + } + bdz->ranktable[i] = count; + offset += nbytes; + nbytes_total -= size; + i++; + } +} + + +int bdz_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + register size_t nbytes; + bdz_data_t *data = (bdz_data_t *)mphf->data; + cmph_uint32 sizeg; +#ifdef DEBUG + cmph_uint32 i; +#endif + __cmph_dump(mphf, fd); + + hash_state_dump(data->hl, &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + nbytes = fwrite(&(data->n), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->m), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->r), sizeof(cmph_uint32), (size_t)1, fd); + + sizeg = (cmph_uint32)ceil(data->n/4.0); + nbytes = fwrite(data->g, sizeof(cmph_uint8)*sizeg, (size_t)1, fd); + + nbytes = fwrite(&(data->k), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->b), sizeof(cmph_uint8), (size_t)1, fd); + nbytes = fwrite(&(data->ranktablesize), sizeof(cmph_uint32), (size_t)1, fd); + + nbytes = fwrite(data->ranktable, sizeof(cmph_uint32)*(data->ranktablesize), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < data->n; ++i) fprintf(stderr, "%u ", GETVALUE(data->g, i)); + fprintf(stderr, "\n"); + #endif + return 1; +} + +void bdz_load(FILE *f, cmph_t *mphf) +{ + char *buf = NULL; + cmph_uint32 buflen, sizeg; + register size_t nbytes; + bdz_data_t *bdz = (bdz_data_t *)malloc(sizeof(bdz_data_t)); +#ifdef DEBUG + cmph_uint32 i = 0; +#endif + + DEBUGP("Loading bdz mphf\n"); + mphf->data = bdz; + + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + bdz->hl = hash_state_load(buf, buflen); + free(buf); + + + DEBUGP("Reading m and n\n"); + nbytes = fread(&(bdz->n), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(bdz->m), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(bdz->r), sizeof(cmph_uint32), (size_t)1, f); + sizeg = (cmph_uint32)ceil(bdz->n/4.0); + bdz->g = (cmph_uint8 *)calloc((size_t)(sizeg), sizeof(cmph_uint8)); + nbytes = fread(bdz->g, sizeg*sizeof(cmph_uint8), (size_t)1, f); + + nbytes = fread(&(bdz->k), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(bdz->b), sizeof(cmph_uint8), (size_t)1, f); + nbytes = fread(&(bdz->ranktablesize), sizeof(cmph_uint32), (size_t)1, f); + + bdz->ranktable = (cmph_uint32 *)calloc((size_t)bdz->ranktablesize, sizeof(cmph_uint32)); + nbytes = fread(bdz->ranktable, sizeof(cmph_uint32)*(bdz->ranktablesize), (size_t)1, f); + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return; + } + + #ifdef DEBUG + i = 0; + fprintf(stderr, "G: "); + for (i = 0; i < bdz->n; ++i) fprintf(stderr, "%u ", GETVALUE(bdz->g,i)); + fprintf(stderr, "\n"); + #endif + return; +} + + +/* +static cmph_uint32 bdz_search_ph(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + bdz_data_t *bdz = mphf->data; + cmph_uint32 hl[3]; + hash_vector(bdz->hl, key, keylen, hl); + cmph_uint32 vertex; + hl[0] = hl[0] % bdz->r; + hl[1] = hl[1] % bdz->r + bdz->r; + hl[2] = hl[2] % bdz->r + (bdz->r << 1); + vertex = hl[(GETVALUE(bdz->g, hl[0]) + GETVALUE(bdz->g, hl[1]) + GETVALUE(bdz->g, hl[2])) % 3]; + return vertex; +} +*/ + +static inline cmph_uint32 rank(cmph_uint32 b, cmph_uint32 * ranktable, cmph_uint8 * g, cmph_uint32 vertex) +{ + register cmph_uint32 index = vertex >> b; + register cmph_uint32 base_rank = ranktable[index]; + register cmph_uint32 beg_idx_v = index << b; + register cmph_uint32 beg_idx_b = beg_idx_v >> 2; + register cmph_uint32 end_idx_b = vertex >> 2; + while(beg_idx_b < end_idx_b) + { + base_rank += bdz_lookup_table[*(g + beg_idx_b++)]; + + } + beg_idx_v = beg_idx_b << 2; + while(beg_idx_v < vertex) + { + if(GETVALUE(g, beg_idx_v) != UNASSIGNED) base_rank++; + beg_idx_v++; + } + + return base_rank; +} + +cmph_uint32 bdz_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + register cmph_uint32 vertex; + register bdz_data_t *bdz = mphf->data; + cmph_uint32 hl[3]; + hash_vector(bdz->hl, key, keylen, hl); + hl[0] = hl[0] % bdz->r; + hl[1] = hl[1] % bdz->r + bdz->r; + hl[2] = hl[2] % bdz->r + (bdz->r << 1); + vertex = hl[(GETVALUE(bdz->g, hl[0]) + GETVALUE(bdz->g, hl[1]) + GETVALUE(bdz->g, hl[2])) % 3]; + return rank(bdz->b, bdz->ranktable, bdz->g, vertex); +} + + +void bdz_destroy(cmph_t *mphf) +{ + bdz_data_t *data = (bdz_data_t *)mphf->data; + free(data->g); + hash_state_destroy(data->hl); + free(data->ranktable); + free(data); + free(mphf); +} + +/** \fn void bdz_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bdz_pack(cmph_t *mphf, void *packed_mphf) +{ + bdz_data_t *data = (bdz_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + cmph_uint32 sizeg; + + // packing hl type + CMPH_HASH hl_type = hash_get_type(data->hl); + *((cmph_uint32 *) ptr) = hl_type; + ptr += sizeof(cmph_uint32); + + // packing hl + hash_state_pack(data->hl, ptr); + ptr += hash_state_packed_size(hl_type); + + // packing r + *((cmph_uint32 *) ptr) = data->r; + ptr += sizeof(data->r); + + // packing ranktablesize + *((cmph_uint32 *) ptr) = data->ranktablesize; + ptr += sizeof(data->ranktablesize); + + // packing ranktable + memcpy(ptr, data->ranktable, sizeof(cmph_uint32)*(data->ranktablesize)); + ptr += sizeof(cmph_uint32)*(data->ranktablesize); + + // packing b + *ptr++ = data->b; + + // packing g + sizeg = (cmph_uint32)ceil(data->n/4.0); + memcpy(ptr, data->g, sizeof(cmph_uint8)*sizeg); +} + +/** \fn cmph_uint32 bdz_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bdz_packed_size(cmph_t *mphf) +{ + bdz_data_t *data = (bdz_data_t *)mphf->data; + + CMPH_HASH hl_type = hash_get_type(data->hl); + + return (cmph_uint32)(sizeof(CMPH_ALGO) + hash_state_packed_size(hl_type) + 3*sizeof(cmph_uint32) + sizeof(cmph_uint32)*(data->ranktablesize) + sizeof(cmph_uint8) + sizeof(cmph_uint8)* (cmph_uint32)(ceil(data->n/4.0))); +} + +/** cmph_uint32 bdz_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 bdz_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + + register cmph_uint32 vertex; + register CMPH_HASH hl_type = *(cmph_uint32 *)packed_mphf; + register cmph_uint8 *hl_ptr = (cmph_uint8 *)(packed_mphf) + 4; + + register cmph_uint32 *ranktable = (cmph_uint32*)(hl_ptr + hash_state_packed_size(hl_type)); + + register cmph_uint32 r = *ranktable++; + register cmph_uint32 ranktablesize = *ranktable++; + register cmph_uint8 * g = (cmph_uint8 *)(ranktable + ranktablesize); + register cmph_uint8 b = *g++; + + cmph_uint32 hl[3]; + hash_vector_packed(hl_ptr, hl_type, key, keylen, hl); + hl[0] = hl[0] % r; + hl[1] = hl[1] % r + r; + hl[2] = hl[2] % r + (r << 1); + vertex = hl[(GETVALUE(g, hl[0]) + GETVALUE(g, hl[1]) + GETVALUE(g, hl[2])) % 3]; + return rank(b, ranktable, g, vertex); +} diff --git a/girepository/cmph/bdz.h b/girepository/cmph/bdz.h new file mode 100644 index 000000000..7116933b4 --- /dev/null +++ b/girepository/cmph/bdz.h @@ -0,0 +1,43 @@ +#ifndef __CMPH_BDZ_H__ +#define __CMPH_BDZ_H__ + +#include "cmph.h" + +typedef struct __bdz_data_t bdz_data_t; +typedef struct __bdz_config_data_t bdz_config_data_t; + +bdz_config_data_t *bdz_config_new(void); +void bdz_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void bdz_config_destroy(cmph_config_t *mph); +void bdz_config_set_b(cmph_config_t *mph, cmph_uint32 b); +cmph_t *bdz_new(cmph_config_t *mph, double c); + +void bdz_load(FILE *f, cmph_t *mphf); +int bdz_dump(cmph_t *mphf, FILE *f); +void bdz_destroy(cmph_t *mphf); +cmph_uint32 bdz_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void bdz_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bdz_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 bdz_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bdz_packed_size(cmph_t *mphf); + +/** cmph_uint32 bdz_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 bdz_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/bdz_gen_lookup_table.c b/girepository/cmph/bdz_gen_lookup_table.c new file mode 100644 index 000000000..b8f66068d --- /dev/null +++ b/girepository/cmph/bdz_gen_lookup_table.c @@ -0,0 +1,33 @@ +#include +#include +#include +void help(char * prname) +{ + fprintf(stderr, "USE: %s \n", prname); + exit(1); +} + +int main(int argc, char ** argv) +{ + if(argc != 3) help(argv[0]); + int n = atoi(argv[1]); + int wordsize = (atoi(argv[2]) >> 1); + int i, j, n_assigned; + for(i = 0; i < n; i++) + { + int num = i; + n_assigned = 0; + for(j = 0; j < wordsize; j++) + { + if ((num & 0x0003) != 3) + { + n_assigned++; + //fprintf(stderr, "num:%d\n", num); + } + num = num >> 2; + } + if(i%16 == 0) fprintf(stderr, "\n"); + fprintf(stderr, "%d, ", n_assigned); + } + fprintf(stderr, "\n"); +} diff --git a/girepository/cmph/bdz_ph.c b/girepository/cmph/bdz_ph.c new file mode 100644 index 000000000..2095f1161 --- /dev/null +++ b/girepository/cmph/bdz_ph.c @@ -0,0 +1,633 @@ +#include "bdz_ph.h" +#include "cmph_structs.h" +#include "bdz_structs_ph.h" +#include "hash.h" +#include "bitbool.h" + +#include +#include +#include +#include +#include +#include +//#define DEBUG +#include "debug.h" +#define UNASSIGNED 3 +#define NULL_EDGE 0xffffffff + + +static cmph_uint8 pow3_table[5] = {1,3,9,27,81}; +static cmph_uint8 lookup_table[5][256] = { + {0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0}, + {0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + +typedef struct +{ + cmph_uint32 vertices[3]; + cmph_uint32 next_edges[3]; +}bdz_ph_edge_t; + +typedef cmph_uint32 * bdz_ph_queue_t; + +static void bdz_ph_alloc_queue(bdz_ph_queue_t * queuep, cmph_uint32 nedges) +{ + (*queuep)=malloc(nedges*sizeof(cmph_uint32)); +}; +static void bdz_ph_free_queue(bdz_ph_queue_t * queue) +{ + free(*queue); +}; + +typedef struct +{ + cmph_uint32 nedges; + bdz_ph_edge_t * edges; + cmph_uint32 * first_edge; + cmph_uint8 * vert_degree; +}bdz_ph_graph3_t; + + +static void bdz_ph_alloc_graph3(bdz_ph_graph3_t * graph3, cmph_uint32 nedges, cmph_uint32 nvertices) +{ + graph3->edges=malloc(nedges*sizeof(bdz_ph_edge_t)); + graph3->first_edge=malloc(nvertices*sizeof(cmph_uint32)); + graph3->vert_degree=malloc((size_t)nvertices); +}; +static void bdz_ph_init_graph3(bdz_ph_graph3_t * graph3, cmph_uint32 nedges, cmph_uint32 nvertices) +{ + memset(graph3->first_edge,0xff,nvertices*sizeof(cmph_uint32)); + memset(graph3->vert_degree,0,(size_t)nvertices); + graph3->nedges=0; +}; +static void bdz_ph_free_graph3(bdz_ph_graph3_t *graph3) +{ + free(graph3->edges); + free(graph3->first_edge); + free(graph3->vert_degree); +}; + +static void bdz_ph_partial_free_graph3(bdz_ph_graph3_t *graph3) +{ + free(graph3->first_edge); + free(graph3->vert_degree); + graph3->first_edge = NULL; + graph3->vert_degree = NULL; +}; + +static void bdz_ph_add_edge(bdz_ph_graph3_t * graph3, cmph_uint32 v0, cmph_uint32 v1, cmph_uint32 v2) +{ + graph3->edges[graph3->nedges].vertices[0]=v0; + graph3->edges[graph3->nedges].vertices[1]=v1; + graph3->edges[graph3->nedges].vertices[2]=v2; + graph3->edges[graph3->nedges].next_edges[0]=graph3->first_edge[v0]; + graph3->edges[graph3->nedges].next_edges[1]=graph3->first_edge[v1]; + graph3->edges[graph3->nedges].next_edges[2]=graph3->first_edge[v2]; + graph3->first_edge[v0]=graph3->first_edge[v1]=graph3->first_edge[v2]=graph3->nedges; + graph3->vert_degree[v0]++; + graph3->vert_degree[v1]++; + graph3->vert_degree[v2]++; + graph3->nedges++; +}; + +static void bdz_ph_dump_graph(bdz_ph_graph3_t* graph3, cmph_uint32 nedges, cmph_uint32 nvertices) +{ + cmph_uint32 i; + for(i=0;iedges[i].vertices[0], + graph3->edges[i].vertices[1],graph3->edges[i].vertices[2]); + printf(" nexts %d %d %d",graph3->edges[i].next_edges[0], + graph3->edges[i].next_edges[1],graph3->edges[i].next_edges[2]); + }; + + for(i=0;ifirst_edge[i]); + + }; +}; + +static void bdz_ph_remove_edge(bdz_ph_graph3_t * graph3, cmph_uint32 curr_edge) +{ + cmph_uint32 i,j=0,vert,edge1,edge2; + for(i=0;i<3;i++){ + vert=graph3->edges[curr_edge].vertices[i]; + edge1=graph3->first_edge[vert]; + edge2=NULL_EDGE; + while(edge1!=curr_edge&&edge1!=NULL_EDGE){ + edge2=edge1; + if(graph3->edges[edge1].vertices[0]==vert){ + j=0; + } else if(graph3->edges[edge1].vertices[1]==vert){ + j=1; + } else + j=2; + edge1=graph3->edges[edge1].next_edges[j]; + }; + if(edge1==NULL_EDGE){ + printf("\nerror remove edge %d dump graph",curr_edge); + bdz_ph_dump_graph(graph3,graph3->nedges,graph3->nedges+graph3->nedges/4); + exit(-1); + }; + + if(edge2!=NULL_EDGE){ + graph3->edges[edge2].next_edges[j] = + graph3->edges[edge1].next_edges[i]; + } else + graph3->first_edge[vert]= + graph3->edges[edge1].next_edges[i]; + graph3->vert_degree[vert]--; + }; + +}; + +static int bdz_ph_generate_queue(cmph_uint32 nedges, cmph_uint32 nvertices, bdz_ph_queue_t queue, bdz_ph_graph3_t* graph3) +{ + cmph_uint32 i,v0,v1,v2; + cmph_uint32 queue_head=0,queue_tail=0; + cmph_uint32 curr_edge; + cmph_uint32 tmp_edge; + cmph_uint8 * marked_edge =malloc((size_t)(nedges >> 3) + 1); + memset(marked_edge, 0, (size_t)(nedges >> 3) + 1); + + for(i=0;iedges[i].vertices[0]; + v1=graph3->edges[i].vertices[1]; + v2=graph3->edges[i].vertices[2]; + if(graph3->vert_degree[v0]==1 || + graph3->vert_degree[v1]==1 || + graph3->vert_degree[v2]==1){ + if(!GETBIT(marked_edge,i)) { + queue[queue_head++]=i; + SETBIT(marked_edge,i); + } + }; + }; + while(queue_tail!=queue_head){ + curr_edge=queue[queue_tail++]; + bdz_ph_remove_edge(graph3,curr_edge); + v0=graph3->edges[curr_edge].vertices[0]; + v1=graph3->edges[curr_edge].vertices[1]; + v2=graph3->edges[curr_edge].vertices[2]; + if(graph3->vert_degree[v0]==1 ) { + tmp_edge=graph3->first_edge[v0]; + if(!GETBIT(marked_edge,tmp_edge)) { + queue[queue_head++]=tmp_edge; + SETBIT(marked_edge,tmp_edge); + }; + + }; + if(graph3->vert_degree[v1]==1) { + tmp_edge=graph3->first_edge[v1]; + if(!GETBIT(marked_edge,tmp_edge)){ + queue[queue_head++]=tmp_edge; + SETBIT(marked_edge,tmp_edge); + }; + + }; + if(graph3->vert_degree[v2]==1){ + tmp_edge=graph3->first_edge[v2]; + if(!GETBIT(marked_edge,tmp_edge)){ + queue[queue_head++]=tmp_edge; + SETBIT(marked_edge,tmp_edge); + }; + }; + }; + free(marked_edge); + return (int)queue_head - (int)nedges;/* returns 0 if successful otherwies return negative number*/ +}; + +static int bdz_ph_mapping(cmph_config_t *mph, bdz_ph_graph3_t* graph3, bdz_ph_queue_t queue); +static void assigning(bdz_ph_config_data_t *bdz_ph, bdz_ph_graph3_t* graph3, bdz_ph_queue_t queue); +static void bdz_ph_optimization(bdz_ph_config_data_t *bdz_ph); + +bdz_ph_config_data_t *bdz_ph_config_new(void) +{ + bdz_ph_config_data_t *bdz_ph; + bdz_ph = (bdz_ph_config_data_t *)malloc(sizeof(bdz_ph_config_data_t)); + assert(bdz_ph); + memset(bdz_ph, 0, sizeof(bdz_ph_config_data_t)); + bdz_ph->hashfunc = CMPH_HASH_JENKINS; + bdz_ph->g = NULL; + bdz_ph->hl = NULL; + return bdz_ph; +} + +void bdz_ph_config_destroy(cmph_config_t *mph) +{ + bdz_ph_config_data_t *data = (bdz_ph_config_data_t *)mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void bdz_ph_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + bdz_ph_config_data_t *bdz_ph = (bdz_ph_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 1) break; //bdz_ph only uses one linear hash function + bdz_ph->hashfunc = *hashptr; + ++i, ++hashptr; + } +} + +cmph_t *bdz_ph_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + bdz_ph_data_t *bdz_phf = NULL; + cmph_uint32 iterations; + bdz_ph_queue_t edges; + bdz_ph_graph3_t graph3; + bdz_ph_config_data_t *bdz_ph = (bdz_ph_config_data_t *)mph->data; + #ifdef CMPH_TIMING + double construction_time_begin = 0.0; + double construction_time = 0.0; + ELAPSED_TIME_IN_SECONDS(&construction_time_begin); + #endif + + + if (c == 0) c = 1.23; // validating restrictions over parameter c. + DEBUGP("c: %f\n", c); + bdz_ph->m = mph->key_source->nkeys; + bdz_ph->r = (cmph_uint32)ceil((c * mph->key_source->nkeys)/3); + if ((bdz_ph->r % 2) == 0) bdz_ph->r += 1; + bdz_ph->n = 3*bdz_ph->r; + + + bdz_ph_alloc_graph3(&graph3, bdz_ph->m, bdz_ph->n); + bdz_ph_alloc_queue(&edges,bdz_ph->m); + DEBUGP("Created hypergraph\n"); + + DEBUGP("m (edges): %u n (vertices): %u r: %u c: %f \n", bdz_ph->m, bdz_ph->n, bdz_ph->r, c); + + // Mapping step + iterations = 100; + if (mph->verbosity) + { + fprintf(stderr, "Entering mapping step for mph creation of %u keys with graph sized %u\n", bdz_ph->m, bdz_ph->n); + } + while(1) + { + int ok; + DEBUGP("linear hash function \n"); + bdz_ph->hl = hash_state_new(bdz_ph->hashfunc, 15); + + ok = bdz_ph_mapping(mph, &graph3, edges); + if (!ok) + { + --iterations; + hash_state_destroy(bdz_ph->hl); + bdz_ph->hl = NULL; + DEBUGP("%u iterations remaining\n", iterations); + if (mph->verbosity) + { + fprintf(stderr, "acyclic graph creation failure - %u iterations remaining\n", iterations); + } + if (iterations == 0) break; + } + else break; + } + + if (iterations == 0) + { +// free(bdz_ph->g); + bdz_ph_free_queue(&edges); + bdz_ph_free_graph3(&graph3); + return NULL; + } + bdz_ph_partial_free_graph3(&graph3); + // Assigning step + if (mph->verbosity) + { + fprintf(stderr, "Entering assigning step for mph creation of %u keys with graph sized %u\n", bdz_ph->m, bdz_ph->n); + } + assigning(bdz_ph, &graph3, edges); + + bdz_ph_free_queue(&edges); + bdz_ph_free_graph3(&graph3); + + if (mph->verbosity) + { + fprintf(stderr, "Starting optimization step\n"); + } + + bdz_ph_optimization(bdz_ph); + + #ifdef CMPH_TIMING + ELAPSED_TIME_IN_SECONDS(&construction_time); + #endif + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + bdz_phf = (bdz_ph_data_t *)malloc(sizeof(bdz_ph_data_t)); + bdz_phf->g = bdz_ph->g; + bdz_ph->g = NULL; //transfer memory ownership + bdz_phf->hl = bdz_ph->hl; + bdz_ph->hl = NULL; //transfer memory ownership + bdz_phf->n = bdz_ph->n; + bdz_phf->m = bdz_ph->m; + bdz_phf->r = bdz_ph->r; + mphf->data = bdz_phf; + mphf->size = bdz_ph->n; + + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + + #ifdef CMPH_TIMING + register cmph_uint32 space_usage = bdz_ph_packed_size(mphf)*8; + register cmph_uint32 keys_per_bucket = 1; + construction_time = construction_time - construction_time_begin; + fprintf(stdout, "%u\t%.2f\t%u\t%.4f\t%.4f\n", bdz_ph->m, bdz_ph->m/(double)bdz_ph->n, keys_per_bucket, construction_time, space_usage/(double)bdz_ph->m); + #endif + + return mphf; +} + + +static int bdz_ph_mapping(cmph_config_t *mph, bdz_ph_graph3_t* graph3, bdz_ph_queue_t queue) +{ + cmph_uint32 e; + int cycles = 0; + cmph_uint32 hl[3]; + + bdz_ph_config_data_t *bdz_ph = (bdz_ph_config_data_t *)mph->data; + bdz_ph_init_graph3(graph3, bdz_ph->m, bdz_ph->n); + mph->key_source->rewind(mph->key_source->data); + for (e = 0; e < mph->key_source->nkeys; ++e) + { + cmph_uint32 h0, h1, h2; + cmph_uint32 keylen; + char *key = NULL; + mph->key_source->read(mph->key_source->data, &key, &keylen); + hash_vector(bdz_ph->hl, key, keylen, hl); + h0 = hl[0] % bdz_ph->r; + h1 = hl[1] % bdz_ph->r + bdz_ph->r; + h2 = hl[2] % bdz_ph->r + (bdz_ph->r << 1); + mph->key_source->dispose(mph->key_source->data, key, keylen); + bdz_ph_add_edge(graph3,h0,h1,h2); + } + cycles = bdz_ph_generate_queue(bdz_ph->m, bdz_ph->n, queue, graph3); + return (cycles == 0); +} + +static void assigning(bdz_ph_config_data_t *bdz_ph, bdz_ph_graph3_t* graph3, bdz_ph_queue_t queue) +{ + cmph_uint32 i; + cmph_uint32 nedges=graph3->nedges; + cmph_uint32 curr_edge; + cmph_uint32 v0,v1,v2; + cmph_uint8 * marked_vertices =malloc((size_t)(bdz_ph->n >> 3) + 1); + cmph_uint32 sizeg = (cmph_uint32)ceil(bdz_ph->n/4.0); + bdz_ph->g = (cmph_uint8 *)calloc((size_t)sizeg, sizeof(cmph_uint8)); + memset(marked_vertices, 0, (size_t)(bdz_ph->n >> 3) + 1); + //memset(bdz_ph->g, 0xff, sizeg); + + for(i=nedges-1;i+1>=1;i--){ + curr_edge=queue[i]; + v0=graph3->edges[curr_edge].vertices[0]; + v1=graph3->edges[curr_edge].vertices[1]; + v2=graph3->edges[curr_edge].vertices[2]; + DEBUGP("B:%u %u %u -- %u %u %u\n", v0, v1, v2, GETVALUE(bdz_ph->g, v0), GETVALUE(bdz_ph->g, v1), GETVALUE(bdz_ph->g, v2)); + if(!GETBIT(marked_vertices, v0)){ + if(!GETBIT(marked_vertices,v1)) + { + //SETVALUE(bdz_ph->g, v1, UNASSIGNED); + SETBIT(marked_vertices, v1); + } + if(!GETBIT(marked_vertices,v2)) + { + //SETVALUE(bdz_ph->g, v2, UNASSIGNED); + SETBIT(marked_vertices, v2); + } + SETVALUE0(bdz_ph->g, v0, (6-(GETVALUE(bdz_ph->g, v1) + GETVALUE(bdz_ph->g,v2)))%3); + SETBIT(marked_vertices, v0); + } else if(!GETBIT(marked_vertices, v1)) { + if(!GETBIT(marked_vertices, v2)) + { + //SETVALUE(bdz_ph->g, v2, UNASSIGNED); + SETBIT(marked_vertices, v2); + } + SETVALUE0(bdz_ph->g, v1, (7 - (GETVALUE(bdz_ph->g, v0)+GETVALUE(bdz_ph->g, v2)))%3); + SETBIT(marked_vertices, v1); + }else { + SETVALUE0(bdz_ph->g, v2, (8-(GETVALUE(bdz_ph->g,v0)+GETVALUE(bdz_ph->g, v1)))%3); + SETBIT(marked_vertices, v2); + } + DEBUGP("A:%u %u %u -- %u %u %u\n", v0, v1, v2, GETVALUE(bdz_ph->g, v0), GETVALUE(bdz_ph->g, v1), GETVALUE(bdz_ph->g, v2)); + }; + free(marked_vertices); +} + +static void bdz_ph_optimization(bdz_ph_config_data_t *bdz_ph) +{ + cmph_uint32 i; + cmph_uint8 byte = 0; + cmph_uint32 sizeg = (cmph_uint32)ceil(bdz_ph->n/5.0); + cmph_uint8 * new_g = (cmph_uint8 *)calloc((size_t)sizeg, sizeof(cmph_uint8)); + cmph_uint8 value; + cmph_uint32 idx; + for(i = 0; i < bdz_ph->n; i++) + { + idx = i/5; + byte = new_g[idx]; + value = GETVALUE(bdz_ph->g, i); + byte = (cmph_uint8) (byte + value*pow3_table[i%5U]); + new_g[idx] = byte; + } + free(bdz_ph->g); + bdz_ph->g = new_g; +} + + +int bdz_ph_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 sizeg = 0; + register size_t nbytes; + bdz_ph_data_t *data = (bdz_ph_data_t *)mphf->data; +#ifdef DEBUG + cmph_uint32 i; +#endif + + __cmph_dump(mphf, fd); + + hash_state_dump(data->hl, &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + nbytes = fwrite(&(data->n), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->m), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->r), sizeof(cmph_uint32), (size_t)1, fd); + sizeg = (cmph_uint32)ceil(data->n/5.0); + nbytes = fwrite(data->g, sizeof(cmph_uint8)*sizeg, (size_t)1, fd); + + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < data->n; ++i) fprintf(stderr, "%u ", GETVALUE(data->g, i)); + fprintf(stderr, "\n"); + #endif + return 1; +} + +void bdz_ph_load(FILE *f, cmph_t *mphf) +{ + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 sizeg = 0; + register size_t nbytes; + bdz_ph_data_t *bdz_ph = (bdz_ph_data_t *)malloc(sizeof(bdz_ph_data_t)); + + DEBUGP("Loading bdz_ph mphf\n"); + mphf->data = bdz_ph; + + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + bdz_ph->hl = hash_state_load(buf, buflen); + free(buf); + + + DEBUGP("Reading m and n\n"); + nbytes = fread(&(bdz_ph->n), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(bdz_ph->m), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(bdz_ph->r), sizeof(cmph_uint32), (size_t)1, f); + sizeg = (cmph_uint32)ceil(bdz_ph->n/5.0); + bdz_ph->g = (cmph_uint8 *)calloc((size_t)sizeg, sizeof(cmph_uint8)); + nbytes = fread(bdz_ph->g, sizeg*sizeof(cmph_uint8), (size_t)1, f); + + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + } + return; +} + + +cmph_uint32 bdz_ph_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + register bdz_ph_data_t *bdz_ph = mphf->data; + cmph_uint32 hl[3]; + register cmph_uint8 byte0, byte1, byte2; + register cmph_uint32 vertex; + + hash_vector(bdz_ph->hl, key, keylen,hl); + hl[0] = hl[0] % bdz_ph->r; + hl[1] = hl[1] % bdz_ph->r + bdz_ph->r; + hl[2] = hl[2] % bdz_ph->r + (bdz_ph->r << 1); + + byte0 = bdz_ph->g[hl[0]/5]; + byte1 = bdz_ph->g[hl[1]/5]; + byte2 = bdz_ph->g[hl[2]/5]; + + byte0 = lookup_table[hl[0]%5U][byte0]; + byte1 = lookup_table[hl[1]%5U][byte1]; + byte2 = lookup_table[hl[2]%5U][byte2]; + vertex = hl[(byte0 + byte1 + byte2)%3]; + + return vertex; +} + + +void bdz_ph_destroy(cmph_t *mphf) +{ + bdz_ph_data_t *data = (bdz_ph_data_t *)mphf->data; + free(data->g); + hash_state_destroy(data->hl); + free(data); + free(mphf); +} + +/** \fn void bdz_ph_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bdz_ph_pack(cmph_t *mphf, void *packed_mphf) +{ + bdz_ph_data_t *data = (bdz_ph_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + cmph_uint32 sizeg; + + // packing hl type + CMPH_HASH hl_type = hash_get_type(data->hl); + *((cmph_uint32 *) ptr) = hl_type; + ptr += sizeof(cmph_uint32); + + // packing hl + hash_state_pack(data->hl, ptr); + ptr += hash_state_packed_size(hl_type); + + // packing r + *((cmph_uint32 *) ptr) = data->r; + ptr += sizeof(data->r); + + // packing g + sizeg = (cmph_uint32)ceil(data->n/5.0); + memcpy(ptr, data->g, sizeof(cmph_uint8)*sizeg); +} + +/** \fn cmph_uint32 bdz_ph_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bdz_ph_packed_size(cmph_t *mphf) +{ + bdz_ph_data_t *data = (bdz_ph_data_t *)mphf->data; + CMPH_HASH hl_type = hash_get_type(data->hl); + cmph_uint32 sizeg = (cmph_uint32)ceil(data->n/5.0); + return (cmph_uint32) (sizeof(CMPH_ALGO) + hash_state_packed_size(hl_type) + 2*sizeof(cmph_uint32) + sizeof(cmph_uint8)*sizeg); +} + +/** cmph_uint32 bdz_ph_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 bdz_ph_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + + register CMPH_HASH hl_type = *(cmph_uint32 *)packed_mphf; + register cmph_uint8 *hl_ptr = (cmph_uint8 *)(packed_mphf) + 4; + + register cmph_uint8 * ptr = hl_ptr + hash_state_packed_size(hl_type); + + register cmph_uint32 r = *((cmph_uint32*) ptr); + register cmph_uint8 * g = ptr + 4; + + cmph_uint32 hl[3]; + register cmph_uint8 byte0, byte1, byte2; + register cmph_uint32 vertex; + + hash_vector_packed(hl_ptr, hl_type, key, keylen, hl); + + hl[0] = hl[0] % r; + hl[1] = hl[1] % r + r; + hl[2] = hl[2] % r + (r << 1); + + byte0 = g[hl[0]/5]; + byte1 = g[hl[1]/5]; + byte2 = g[hl[2]/5]; + + byte0 = lookup_table[hl[0]%5][byte0]; + byte1 = lookup_table[hl[1]%5][byte1]; + byte2 = lookup_table[hl[2]%5][byte2]; + vertex = hl[(byte0 + byte1 + byte2)%3]; + + return vertex; +} diff --git a/girepository/cmph/bdz_ph.h b/girepository/cmph/bdz_ph.h new file mode 100644 index 000000000..67b1facbe --- /dev/null +++ b/girepository/cmph/bdz_ph.h @@ -0,0 +1,42 @@ +#ifndef __CMPH_BDZ_PH_H__ +#define __CMPH_BDZ_PH_H__ + +#include "cmph.h" + +typedef struct __bdz_ph_data_t bdz_ph_data_t; +typedef struct __bdz_ph_config_data_t bdz_ph_config_data_t; + +bdz_ph_config_data_t *bdz_ph_config_new(void); +void bdz_ph_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void bdz_ph_config_destroy(cmph_config_t *mph); +cmph_t *bdz_ph_new(cmph_config_t *mph, double c); + +void bdz_ph_load(FILE *f, cmph_t *mphf); +int bdz_ph_dump(cmph_t *mphf, FILE *f); +void bdz_ph_destroy(cmph_t *mphf); +cmph_uint32 bdz_ph_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void bdz_ph_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bdz_ph_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 bdz_ph_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bdz_ph_packed_size(cmph_t *mphf); + +/** cmph_uint32 bdz_ph_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 bdz_ph_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/bdz_structs.h b/girepository/cmph/bdz_structs.h new file mode 100644 index 000000000..ba7dc3c66 --- /dev/null +++ b/girepository/cmph/bdz_structs.h @@ -0,0 +1,36 @@ +#ifndef __CMPH_BDZ_STRUCTS_H__ +#define __CMPH_BDZ_STRUCTS_H__ + +#include "hash_state.h" + +struct __bdz_data_t +{ + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + cmph_uint32 r; //partition vertex count + cmph_uint8 *g; + hash_state_t *hl; // linear hashing + + cmph_uint32 k; //kth index in ranktable, $k = log_2(n=3r)/\varepsilon$ + cmph_uint8 b; // number of bits of k + cmph_uint32 ranktablesize; //number of entries in ranktable, $n/k +1$ + cmph_uint32 *ranktable; // rank table +}; + + +struct __bdz_config_data_t +{ + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + cmph_uint32 r; //partition vertex count + cmph_uint8 *g; + hash_state_t *hl; // linear hashing + + cmph_uint32 k; //kth index in ranktable, $k = log_2(n=3r)/\varepsilon$ + cmph_uint8 b; // number of bits of k + cmph_uint32 ranktablesize; //number of entries in ranktable, $n/k +1$ + cmph_uint32 *ranktable; // rank table + CMPH_HASH hashfunc; +}; + +#endif diff --git a/girepository/cmph/bdz_structs_ph.h b/girepository/cmph/bdz_structs_ph.h new file mode 100644 index 000000000..5874a26df --- /dev/null +++ b/girepository/cmph/bdz_structs_ph.h @@ -0,0 +1,26 @@ +#ifndef __CMPH_BDZ_STRUCTS_PH_H__ +#define __CMPH_BDZ_STRUCTS_PH_H__ + +#include "hash_state.h" + +struct __bdz_ph_data_t +{ + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + cmph_uint32 r; //partition vertex count + cmph_uint8 *g; + hash_state_t *hl; // linear hashing +}; + + +struct __bdz_ph_config_data_t +{ + CMPH_HASH hashfunc; + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + cmph_uint32 r; //partition vertex count + cmph_uint8 *g; + hash_state_t *hl; // linear hashing +}; + +#endif diff --git a/girepository/cmph/bitbool.h b/girepository/cmph/bitbool.h new file mode 100644 index 000000000..a3286c3c9 --- /dev/null +++ b/girepository/cmph/bitbool.h @@ -0,0 +1,179 @@ +#ifndef _CMPH_BITBOOL_H__ +#define _CMPH_BITBOOL_H__ +#include "cmph_types.h" + +static const cmph_uint8 bitmask[] = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7 }; + +static const cmph_uint32 bitmask32[] = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7, + 1 << 8, 1 << 9, 1 << 10, 1 << 11, 1 << 12, 1 << 13, 1 << 14, 1 << 15, + 1 << 16, 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 21, 1 << 22, 1 << 23, + 1 << 24, 1 << 25, 1 << 26, 1 << 27, 1 << 28, 1 << 29, 1 << 30, 1U << 31 + }; + +static const cmph_uint8 valuemask[] = { 0xfc, 0xf3, 0xcf, 0x3f}; + + +/** \def GETBIT(array, i) + * \brief get the value of an 1-bit integer stored in an array. + * \param array to get 1-bit integer values from + * \param i is the index in array to get the 1-bit integer value from + * + * GETBIT(array, i) is a macro that gets the value of an 1-bit integer stored in array. + */ +#define GETBIT(array, i) ((array[i >> 3] & bitmask[i & 0x00000007]) >> (i & 0x00000007)) + +/** \def SETBIT(array, i) + * \brief set 1 to an 1-bit integer stored in an array. + * \param array to store 1-bit integer values + * \param i is the index in array to set the the bit to 1 + * + * SETBIT(array, i) is a macro that sets 1 to an 1-bit integer stored in an array. + */ +#define SETBIT(array, i) (array[i >> 3] |= bitmask[i & 0x00000007]) + +/** \def UNSETBIT(array, i) + * \brief set 0 to an 1-bit integer stored in an array. + * \param array to store 1-bit integer values + * \param i is the index in array to set the the bit to 0 + * + * UNSETBIT(array, i) is a macro that sets 0 to an 1-bit integer stored in an array. + */ +#define UNSETBIT(array, i) (array[i >> 3] ^= ((bitmask[i & 0x00000007]))) + +//#define GETBIT(array, i) (array[(i) / 8] & bitmask[(i) % 8]) +//#define SETBIT(array, i) (array[(i) / 8] |= bitmask[(i) % 8]) +//#define UNSETBIT(array, i) (array[(i) / 8] ^= ((bitmask[(i) % 8]))) + + +/** \def SETVALUE1(array, i, v) + * \brief set a value for a 2-bit integer stored in an array initialized with 1s. + * \param array to store 2-bit integer values + * \param i is the index in array to set the value v + * \param v is the value to be set + * + * SETVALUE1(array, i, v) is a macro that set a value for a 2-bit integer stored in an array. + * The array should be initialized with all bits set to 1. For example: + * memset(array, 0xff, arraySize); + */ +#define SETVALUE1(array, i, v) (array[i >> 2] &= (cmph_uint8)((v << ((i & 0x00000003) << 1)) | valuemask[i & 0x00000003])) + +/** \def SETVALUE0(array, i, v) + * \brief set a value for a 2-bit integer stored in an array initialized with 0s. + * \param array to store 2-bit integer values + * \param i is the index in array to set the value v + * \param v is the value to be set + * + * SETVALUE0(array, i, v) is a macro that set a value for a 2-bit integer stored in an array. + * The array should be initialized with all bits set to 0. For example: + * memset(array, 0, arraySize); + */ +#define SETVALUE0(array, i, v) (array[i >> 2] |= (cmph_uint8)(v << ((i & 0x00000003) << 1))) + + +/** \def GETVALUE(array, i) + * \brief get a value for a 2-bit integer stored in an array. + * \param array to get 2-bit integer values from + * \param i is the index in array to get the value from + * + * GETVALUE(array, i) is a macro that get a value for a 2-bit integer stored in an array. + */ +#define GETVALUE(array, i) ((cmph_uint8)((array[i >> 2] >> ((i & 0x00000003U) << 1U)) & 0x00000003U)) + + + +/** \def SETBIT32(array, i) + * \brief set 1 to an 1-bit integer stored in an array of 32-bit words. + * \param array to store 1-bit integer values. The entries are 32-bit words. + * \param i is the index in array to set the the bit to 1 + * + * SETBIT32(array, i) is a macro that sets 1 to an 1-bit integer stored in an array of 32-bit words. + */ +#define SETBIT32(array, i) (array[i >> 5] |= bitmask32[i & 0x0000001f]) + +/** \def GETBIT32(array, i) + * \brief get the value of an 1-bit integer stored in an array of 32-bit words. + * \param array to get 1-bit integer values from. The entries are 32-bit words. + * \param i is the index in array to get the 1-bit integer value from + * + * GETBIT32(array, i) is a macro that gets the value of an 1-bit integer stored in an array of 32-bit words. + */ +#define GETBIT32(array, i) (array[i >> 5] & bitmask32[i & 0x0000001f]) + +/** \def UNSETBIT32(array, i) + * \brief set 0 to an 1-bit integer stored in an array of 32-bit words. + * \param array to store 1-bit integer values. The entries ar 32-bit words + * \param i is the index in array to set the the bit to 0 + * + * UNSETBIT32(array, i) is a macro that sets 0 to an 1-bit integer stored in an array of 32-bit words. + */ +#define UNSETBIT32(array, i) (array[i >> 5] ^= ((bitmask32[i & 0x0000001f]))) + +#define BITS_TABLE_SIZE(n, bits_length) ((n * bits_length + 31) >> 5) + +static inline void set_bits_value(cmph_uint32 * bits_table, cmph_uint32 index, cmph_uint32 bits_string, + cmph_uint32 string_length, cmph_uint32 string_mask) +{ + register cmph_uint32 bit_idx = index * string_length; + register cmph_uint32 word_idx = bit_idx >> 5; + register cmph_uint32 shift1 = bit_idx & 0x0000001f; + register cmph_uint32 shift2 = 32 - shift1; + + bits_table[word_idx] &= ~((string_mask) << shift1); + bits_table[word_idx] |= bits_string << shift1; + + if(shift2 < string_length) + { + bits_table[word_idx+1] &= ~((string_mask) >> shift2); + bits_table[word_idx+1] |= bits_string >> shift2; + }; +}; + +static inline cmph_uint32 get_bits_value(cmph_uint32 * bits_table,cmph_uint32 index, cmph_uint32 string_length, cmph_uint32 string_mask) +{ + register cmph_uint32 bit_idx = index * string_length; + register cmph_uint32 word_idx = bit_idx >> 5; + register cmph_uint32 shift1 = bit_idx & 0x0000001f; + register cmph_uint32 shift2 = 32-shift1; + register cmph_uint32 bits_string; + + bits_string = (bits_table[word_idx] >> shift1) & string_mask; + + if(shift2 < string_length) + bits_string |= (bits_table[word_idx+1] << shift2) & string_mask; + + return bits_string; +}; + +static inline void set_bits_at_pos(cmph_uint32 * bits_table, cmph_uint32 pos, cmph_uint32 bits_string, cmph_uint32 string_length) +{ + register cmph_uint32 word_idx = pos >> 5; + register cmph_uint32 shift1 = pos & 0x0000001f; + register cmph_uint32 shift2 = 32-shift1; + register cmph_uint32 string_mask = (1U << string_length) - 1; + + bits_table[word_idx] &= ~((string_mask) << shift1); + bits_table[word_idx] |= bits_string << shift1; + if(shift2 < string_length) + { + bits_table[word_idx+1] &= ~((string_mask) >> shift2); + bits_table[word_idx+1] |= bits_string >> shift2; + } +}; + +static inline cmph_uint32 get_bits_at_pos(cmph_uint32 * bits_table,cmph_uint32 pos,cmph_uint32 string_length) +{ + register cmph_uint32 word_idx = pos >> 5; + register cmph_uint32 shift1 = pos & 0x0000001f; + register cmph_uint32 shift2 = 32 - shift1; + register cmph_uint32 string_mask = (1U << string_length) - 1; + register cmph_uint32 bits_string; + + bits_string = (bits_table[word_idx] >> shift1) & string_mask; + + if(shift2 < string_length) + bits_string |= (bits_table[word_idx+1] << shift2) & string_mask; + return bits_string; +} + + +#endif diff --git a/girepository/cmph/bmz.c b/girepository/cmph/bmz.c new file mode 100644 index 000000000..9573825af --- /dev/null +++ b/girepository/cmph/bmz.c @@ -0,0 +1,638 @@ +#include "graph.h" +#include "bmz.h" +#include "cmph_structs.h" +#include "bmz_structs.h" +#include "hash.h" +#include "vqueue.h" +#include "bitbool.h" + +#include +#include +#include +#include +#include +#include + +//#define DEBUG +#include "debug.h" + +static int bmz_gen_edges(cmph_config_t *mph); +static cmph_uint8 bmz_traverse_critical_nodes(bmz_config_data_t *bmz, cmph_uint32 v, cmph_uint32 * biggest_g_value, cmph_uint32 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited); +static cmph_uint8 bmz_traverse_critical_nodes_heuristic(bmz_config_data_t *bmz, cmph_uint32 v, cmph_uint32 * biggest_g_value, cmph_uint32 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited); +static void bmz_traverse_non_critical_nodes(bmz_config_data_t *bmz, cmph_uint8 * used_edges, cmph_uint8 * visited); + +bmz_config_data_t *bmz_config_new(void) +{ + bmz_config_data_t *bmz = NULL; + bmz = (bmz_config_data_t *)malloc(sizeof(bmz_config_data_t)); + assert(bmz); + memset(bmz, 0, sizeof(bmz_config_data_t)); + bmz->hashfuncs[0] = CMPH_HASH_JENKINS; + bmz->hashfuncs[1] = CMPH_HASH_JENKINS; + bmz->g = NULL; + bmz->graph = NULL; + bmz->hashes = NULL; + return bmz; +} + +void bmz_config_destroy(cmph_config_t *mph) +{ + bmz_config_data_t *data = (bmz_config_data_t *)mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void bmz_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + bmz_config_data_t *bmz = (bmz_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 2) break; //bmz only uses two hash functions + bmz->hashfuncs[i] = *hashptr; + ++i, ++hashptr; + } +} + +cmph_t *bmz_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + bmz_data_t *bmzf = NULL; + cmph_uint32 i; + cmph_uint32 iterations; + cmph_uint32 iterations_map = 20; + cmph_uint8 *used_edges = NULL; + cmph_uint8 restart_mapping = 0; + cmph_uint8 * visited = NULL; + + bmz_config_data_t *bmz = (bmz_config_data_t *)mph->data; + if (c == 0) c = 1.15; // validating restrictions over parameter c. + DEBUGP("c: %f\n", c); + bmz->m = mph->key_source->nkeys; + bmz->n = (cmph_uint32)ceil(c * mph->key_source->nkeys); + DEBUGP("m (edges): %u n (vertices): %u c: %f\n", bmz->m, bmz->n, c); + bmz->graph = graph_new(bmz->n, bmz->m); + DEBUGP("Created graph\n"); + + bmz->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*3); + for(i = 0; i < 3; ++i) bmz->hashes[i] = NULL; + + do + { + // Mapping step + cmph_uint32 biggest_g_value = 0; + cmph_uint32 biggest_edge_value = 1; + iterations = 100; + if (mph->verbosity) + { + fprintf(stderr, "Entering mapping step for mph creation of %u keys with graph sized %u\n", bmz->m, bmz->n); + } + while(1) + { + int ok; + DEBUGP("hash function 1\n"); + bmz->hashes[0] = hash_state_new(bmz->hashfuncs[0], bmz->n); + DEBUGP("hash function 2\n"); + bmz->hashes[1] = hash_state_new(bmz->hashfuncs[1], bmz->n); + DEBUGP("Generating edges\n"); + ok = bmz_gen_edges(mph); + if (!ok) + { + --iterations; + hash_state_destroy(bmz->hashes[0]); + bmz->hashes[0] = NULL; + hash_state_destroy(bmz->hashes[1]); + bmz->hashes[1] = NULL; + DEBUGP("%u iterations remaining\n", iterations); + if (mph->verbosity) + { + fprintf(stderr, "simple graph creation failure - %u iterations remaining\n", iterations); + } + if (iterations == 0) break; + } + else break; + } + if (iterations == 0) + { + graph_destroy(bmz->graph); + return NULL; + } + // Ordering step + if (mph->verbosity) + { + fprintf(stderr, "Starting ordering step\n"); + } + graph_obtain_critical_nodes(bmz->graph); + + // Searching step + if (mph->verbosity) + { + fprintf(stderr, "Starting Searching step.\n"); + fprintf(stderr, "\tTraversing critical vertices.\n"); + } + DEBUGP("Searching step\n"); + visited = (cmph_uint8 *)malloc((size_t)bmz->n/8 + 1); + memset(visited, 0, (size_t)bmz->n/8 + 1); + used_edges = (cmph_uint8 *)malloc((size_t)bmz->m/8 + 1); + memset(used_edges, 0, (size_t)bmz->m/8 + 1); + free(bmz->g); + bmz->g = (cmph_uint32 *)calloc((size_t)bmz->n, sizeof(cmph_uint32)); + assert(bmz->g); + for (i = 0; i < bmz->n; ++i) // critical nodes + { + if (graph_node_is_critical(bmz->graph, i) && (!GETBIT(visited,i))) + { + if(c > 1.14) restart_mapping = bmz_traverse_critical_nodes(bmz, i, &biggest_g_value, &biggest_edge_value, used_edges, visited); + else restart_mapping = bmz_traverse_critical_nodes_heuristic(bmz, i, &biggest_g_value, &biggest_edge_value, used_edges, visited); + if(restart_mapping) break; + } + } + if(!restart_mapping) + { + if (mph->verbosity) + { + fprintf(stderr, "\tTraversing non critical vertices.\n"); + } + bmz_traverse_non_critical_nodes(bmz, used_edges, visited); // non_critical_nodes + } + else + { + iterations_map--; + if (mph->verbosity) fprintf(stderr, "Restarting mapping step. %u iterations remaining.\n", iterations_map); + } + free(used_edges); + free(visited); + }while(restart_mapping && iterations_map > 0); + graph_destroy(bmz->graph); + bmz->graph = NULL; + if (iterations_map == 0) + { + return NULL; + } + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + bmzf = (bmz_data_t *)malloc(sizeof(bmz_data_t)); + bmzf->g = bmz->g; + bmz->g = NULL; //transfer memory ownership + bmzf->hashes = bmz->hashes; + bmz->hashes = NULL; //transfer memory ownership + bmzf->n = bmz->n; + bmzf->m = bmz->m; + mphf->data = bmzf; + mphf->size = bmz->m; + + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + return mphf; +} + +static cmph_uint8 bmz_traverse_critical_nodes(bmz_config_data_t *bmz, cmph_uint32 v, cmph_uint32 * biggest_g_value, cmph_uint32 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited) +{ + cmph_uint32 next_g; + cmph_uint32 u; /* Auxiliary vertex */ + cmph_uint32 lav; /* lookahead vertex */ + cmph_uint8 collision; + vqueue_t * q = vqueue_new((cmph_uint32)(graph_ncritical_nodes(bmz->graph)) + 1); + graph_iterator_t it, it1; + + DEBUGP("Labelling critical vertices\n"); + bmz->g[v] = (cmph_uint32)ceil ((double)(*biggest_edge_value)/2) - 1; + SETBIT(visited, v); + next_g = (cmph_uint32)floor((double)(*biggest_edge_value/2)); /* next_g is incremented in the do..while statement*/ + vqueue_insert(q, v); + while(!vqueue_is_empty(q)) + { + v = vqueue_remove(q); + it = graph_neighbors_it(bmz->graph, v); + while ((u = graph_next_neighbor(bmz->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz->graph, u) && (!GETBIT(visited,u))) + { + collision = 1; + while(collision) // lookahead to resolve collisions + { + next_g = *biggest_g_value + 1; + it1 = graph_neighbors_it(bmz->graph, u); + collision = 0; + while((lav = graph_next_neighbor(bmz->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz->graph, lav) && GETBIT(visited,lav)) + { + if(next_g + bmz->g[lav] >= bmz->m) + { + vqueue_destroy(q); + return 1; // restart mapping step. + } + if (GETBIT(used_edges, (next_g + bmz->g[lav]))) + { + collision = 1; + break; + } + } + } + if (next_g > *biggest_g_value) *biggest_g_value = next_g; + } + // Marking used edges... + it1 = graph_neighbors_it(bmz->graph, u); + while((lav = graph_next_neighbor(bmz->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz->graph, lav) && GETBIT(visited, lav)) + { + SETBIT(used_edges,(next_g + bmz->g[lav])); + if(next_g + bmz->g[lav] > *biggest_edge_value) *biggest_edge_value = next_g + bmz->g[lav]; + } + } + bmz->g[u] = next_g; // Labelling vertex u. + SETBIT(visited,u); + vqueue_insert(q, u); + } + } + + } + vqueue_destroy(q); + return 0; +} + +static cmph_uint8 bmz_traverse_critical_nodes_heuristic(bmz_config_data_t *bmz, cmph_uint32 v, cmph_uint32 * biggest_g_value, cmph_uint32 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited) +{ + cmph_uint32 next_g; + cmph_uint32 u; /* Auxiliary vertex */ + cmph_uint32 lav; /* lookahead vertex */ + cmph_uint8 collision; + cmph_uint32 * unused_g_values = NULL; + cmph_uint32 unused_g_values_capacity = 0; + cmph_uint32 nunused_g_values = 0; + vqueue_t * q = vqueue_new((cmph_uint32)(0.5*graph_ncritical_nodes(bmz->graph))+1); + graph_iterator_t it, it1; + + DEBUGP("Labelling critical vertices\n"); + bmz->g[v] = (cmph_uint32)ceil ((double)(*biggest_edge_value)/2) - 1; + SETBIT(visited, v); + next_g = (cmph_uint32)floor((double)(*biggest_edge_value/2)); /* next_g is incremented in the do..while statement*/ + vqueue_insert(q, v); + while(!vqueue_is_empty(q)) + { + v = vqueue_remove(q); + it = graph_neighbors_it(bmz->graph, v); + while ((u = graph_next_neighbor(bmz->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz->graph, u) && (!GETBIT(visited,u))) + { + cmph_uint32 next_g_index = 0; + collision = 1; + while(collision) // lookahead to resolve collisions + { + if (next_g_index < nunused_g_values) + { + next_g = unused_g_values[next_g_index++]; + } + else + { + next_g = *biggest_g_value + 1; + next_g_index = UINT_MAX; + } + it1 = graph_neighbors_it(bmz->graph, u); + collision = 0; + while((lav = graph_next_neighbor(bmz->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz->graph, lav) && GETBIT(visited,lav)) + { + if(next_g + bmz->g[lav] >= bmz->m) + { + vqueue_destroy(q); + free(unused_g_values); + return 1; // restart mapping step. + } + if (GETBIT(used_edges, (next_g + bmz->g[lav]))) + { + collision = 1; + break; + } + } + } + if(collision && (next_g > *biggest_g_value)) // saving the current g value stored in next_g. + { + if(nunused_g_values == unused_g_values_capacity) + { + unused_g_values = (cmph_uint32 *)realloc(unused_g_values, (unused_g_values_capacity + BUFSIZ)*sizeof(cmph_uint32)); + unused_g_values_capacity += BUFSIZ; + } + unused_g_values[nunused_g_values++] = next_g; + + } + if (next_g > *biggest_g_value) *biggest_g_value = next_g; + } + next_g_index--; + if (next_g_index < nunused_g_values) unused_g_values[next_g_index] = unused_g_values[--nunused_g_values]; + + // Marking used edges... + it1 = graph_neighbors_it(bmz->graph, u); + while((lav = graph_next_neighbor(bmz->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz->graph, lav) && GETBIT(visited, lav)) + { + SETBIT(used_edges,(next_g + bmz->g[lav])); + if(next_g + bmz->g[lav] > *biggest_edge_value) *biggest_edge_value = next_g + bmz->g[lav]; + } + } + bmz->g[u] = next_g; // Labelling vertex u. + SETBIT(visited, u); + vqueue_insert(q, u); + } + } + + } + vqueue_destroy(q); + free(unused_g_values); + return 0; +} + +static cmph_uint32 next_unused_edge(bmz_config_data_t *bmz, cmph_uint8 * used_edges, cmph_uint32 unused_edge_index) +{ + while(1) + { + assert(unused_edge_index < bmz->m); + if(GETBIT(used_edges, unused_edge_index)) unused_edge_index ++; + else break; + } + return unused_edge_index; +} + +static void bmz_traverse(bmz_config_data_t *bmz, cmph_uint8 * used_edges, cmph_uint32 v, cmph_uint32 * unused_edge_index, cmph_uint8 * visited) +{ + graph_iterator_t it = graph_neighbors_it(bmz->graph, v); + cmph_uint32 neighbor = 0; + while((neighbor = graph_next_neighbor(bmz->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + if(GETBIT(visited,neighbor)) continue; + //DEBUGP("Visiting neighbor %u\n", neighbor); + *unused_edge_index = next_unused_edge(bmz, used_edges, *unused_edge_index); + bmz->g[neighbor] = *unused_edge_index - bmz->g[v]; + //if (bmz->g[neighbor] >= bmz->m) bmz->g[neighbor] += bmz->m; + SETBIT(visited, neighbor); + (*unused_edge_index)++; + bmz_traverse(bmz, used_edges, neighbor, unused_edge_index, visited); + + } +} + +static void bmz_traverse_non_critical_nodes(bmz_config_data_t *bmz, cmph_uint8 * used_edges, cmph_uint8 * visited) +{ + + cmph_uint32 i, v1, v2, unused_edge_index = 0; + DEBUGP("Labelling non critical vertices\n"); + for(i = 0; i < bmz->m; i++) + { + v1 = graph_vertex_id(bmz->graph, i, 0); + v2 = graph_vertex_id(bmz->graph, i, 1); + if((GETBIT(visited,v1) && GETBIT(visited,v2)) || (!GETBIT(visited,v1) && !GETBIT(visited,v2))) continue; + if(GETBIT(visited,v1)) bmz_traverse(bmz, used_edges, v1, &unused_edge_index, visited); + else bmz_traverse(bmz, used_edges, v2, &unused_edge_index, visited); + + } + + for(i = 0; i < bmz->n; i++) + { + if(!GETBIT(visited,i)) + { + bmz->g[i] = 0; + SETBIT(visited, i); + bmz_traverse(bmz, used_edges, i, &unused_edge_index, visited); + } + } + +} + +static int bmz_gen_edges(cmph_config_t *mph) +{ + cmph_uint32 e; + bmz_config_data_t *bmz = (bmz_config_data_t *)mph->data; + cmph_uint8 multiple_edges = 0; + DEBUGP("Generating edges for %u vertices\n", bmz->n); + graph_clear_edges(bmz->graph); + mph->key_source->rewind(mph->key_source->data); + for (e = 0; e < mph->key_source->nkeys; ++e) + { + cmph_uint32 h1, h2; + cmph_uint32 keylen; + char *key = NULL; + mph->key_source->read(mph->key_source->data, &key, &keylen); + +// if (key == NULL)fprintf(stderr, "key = %s -- read BMZ\n", key); + h1 = hash(bmz->hashes[0], key, keylen) % bmz->n; + h2 = hash(bmz->hashes[1], key, keylen) % bmz->n; + if (h1 == h2) if (++h2 >= bmz->n) h2 = 0; + if (h1 == h2) + { + if (mph->verbosity) fprintf(stderr, "Self loop for key %u\n", e); + mph->key_source->dispose(mph->key_source->data, key, keylen); + return 0; + } + //DEBUGP("Adding edge: %u -> %u for key %s\n", h1, h2, key); + mph->key_source->dispose(mph->key_source->data, key, keylen); +// fprintf(stderr, "key = %s -- dispose BMZ\n", key); + multiple_edges = graph_contains_edge(bmz->graph, h1, h2); + if (mph->verbosity && multiple_edges) fprintf(stderr, "A non simple graph was generated\n"); + if (multiple_edges) return 0; // checking multiple edge restriction. + graph_add_edge(bmz->graph, h1, h2); + } + return !multiple_edges; +} + +int bmz_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 two = 2; //number of hash functions + bmz_data_t *data = (bmz_data_t *)mphf->data; + register size_t nbytes; +#ifdef DEBUG + cmph_uint32 i; +#endif + + __cmph_dump(mphf, fd); + + nbytes = fwrite(&two, sizeof(cmph_uint32), (size_t)1, fd); + + hash_state_dump(data->hashes[0], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + hash_state_dump(data->hashes[1], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + nbytes = fwrite(&(data->n), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->m), sizeof(cmph_uint32), (size_t)1, fd); + + nbytes = fwrite(data->g, sizeof(cmph_uint32)*(data->n), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < data->n; ++i) fprintf(stderr, "%u ", data->g[i]); + fprintf(stderr, "\n"); + #endif + return 1; +} + +void bmz_load(FILE *f, cmph_t *mphf) +{ + cmph_uint32 nhashes; + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 i; + bmz_data_t *bmz = (bmz_data_t *)malloc(sizeof(bmz_data_t)); + register size_t nbytes; + DEBUGP("Loading bmz mphf\n"); + mphf->data = bmz; + nbytes = fread(&nhashes, sizeof(cmph_uint32), (size_t)1, f); + bmz->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*(nhashes + 1)); + bmz->hashes[nhashes] = NULL; + DEBUGP("Reading %u hashes\n", nhashes); + for (i = 0; i < nhashes; ++i) + { + hash_state_t *state = NULL; + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + state = hash_state_load(buf, buflen); + bmz->hashes[i] = state; + free(buf); + } + + DEBUGP("Reading m and n\n"); + nbytes = fread(&(bmz->n), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(bmz->m), sizeof(cmph_uint32), (size_t)1, f); + + bmz->g = (cmph_uint32 *)malloc(sizeof(cmph_uint32)*bmz->n); + nbytes = fread(bmz->g, bmz->n*sizeof(cmph_uint32), (size_t)1, f); + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return; + } + + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < bmz->n; ++i) fprintf(stderr, "%u ", bmz->g[i]); + fprintf(stderr, "\n"); + #endif + return; +} + + +cmph_uint32 bmz_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + bmz_data_t *bmz = mphf->data; + cmph_uint32 h1 = hash(bmz->hashes[0], key, keylen) % bmz->n; + cmph_uint32 h2 = hash(bmz->hashes[1], key, keylen) % bmz->n; + DEBUGP("key: %s h1: %u h2: %u\n", key, h1, h2); + if (h1 == h2 && ++h2 > bmz->n) h2 = 0; + DEBUGP("key: %s g[h1]: %u g[h2]: %u edges: %u\n", key, bmz->g[h1], bmz->g[h2], bmz->m); + return bmz->g[h1] + bmz->g[h2]; +} +void bmz_destroy(cmph_t *mphf) +{ + bmz_data_t *data = (bmz_data_t *)mphf->data; + free(data->g); + hash_state_destroy(data->hashes[0]); + hash_state_destroy(data->hashes[1]); + free(data->hashes); + free(data); + free(mphf); +} + +/** \fn void bmz_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bmz_pack(cmph_t *mphf, void *packed_mphf) +{ + + bmz_data_t *data = (bmz_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + CMPH_HASH h2_type; + + // packing h1 type + CMPH_HASH h1_type = hash_get_type(data->hashes[0]); + *((cmph_uint32 *) ptr) = h1_type; + ptr += sizeof(cmph_uint32); + + // packing h1 + hash_state_pack(data->hashes[0], ptr); + ptr += hash_state_packed_size(h1_type); + + // packing h2 type + h2_type = hash_get_type(data->hashes[1]); + *((cmph_uint32 *) ptr) = h2_type; + ptr += sizeof(cmph_uint32); + + // packing h2 + hash_state_pack(data->hashes[1], ptr); + ptr += hash_state_packed_size(h2_type); + + // packing n + *((cmph_uint32 *) ptr) = data->n; + ptr += sizeof(data->n); + + // packing g + memcpy(ptr, data->g, sizeof(cmph_uint32)*data->n); +} + +/** \fn cmph_uint32 bmz_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bmz_packed_size(cmph_t *mphf) +{ + bmz_data_t *data = (bmz_data_t *)mphf->data; + CMPH_HASH h1_type = hash_get_type(data->hashes[0]); + CMPH_HASH h2_type = hash_get_type(data->hashes[1]); + + return (cmph_uint32)(sizeof(CMPH_ALGO) + hash_state_packed_size(h1_type) + hash_state_packed_size(h2_type) + + 3*sizeof(cmph_uint32) + sizeof(cmph_uint32)*data->n); +} + +/** cmph_uint32 bmz_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 bmz_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + register cmph_uint8 *h1_ptr = packed_mphf; + register CMPH_HASH h1_type = *((cmph_uint32 *)h1_ptr); + register cmph_uint8 *h2_ptr; + register CMPH_HASH h2_type; + register cmph_uint32 *g_ptr, n, h1, h2; + + h1_ptr += 4; + + h2_ptr = h1_ptr + hash_state_packed_size(h1_type); + h2_type = *((cmph_uint32 *)h2_ptr); + h2_ptr += 4; + + g_ptr = (cmph_uint32 *)(h2_ptr + hash_state_packed_size(h2_type)); + + n = *g_ptr++; + + h1 = hash_packed(h1_ptr, h1_type, key, keylen) % n; + h2 = hash_packed(h2_ptr, h2_type, key, keylen) % n; + if (h1 == h2 && ++h2 > n) h2 = 0; + return (g_ptr[h1] + g_ptr[h2]); +} diff --git a/girepository/cmph/bmz.h b/girepository/cmph/bmz.h new file mode 100644 index 000000000..9821aa88c --- /dev/null +++ b/girepository/cmph/bmz.h @@ -0,0 +1,42 @@ +#ifndef __CMPH_BMZ_H__ +#define __CMPH_BMZ_H__ + +#include "cmph.h" + +typedef struct __bmz_data_t bmz_data_t; +typedef struct __bmz_config_data_t bmz_config_data_t; + +bmz_config_data_t *bmz_config_new(void); +void bmz_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void bmz_config_destroy(cmph_config_t *mph); +cmph_t *bmz_new(cmph_config_t *mph, double c); + +void bmz_load(FILE *f, cmph_t *mphf); +int bmz_dump(cmph_t *mphf, FILE *f); +void bmz_destroy(cmph_t *mphf); +cmph_uint32 bmz_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void bmz_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bmz_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 bmz_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bmz_packed_size(cmph_t *mphf); + +/** cmph_uint32 bmz_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 bmz_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/bmz8.c b/girepository/cmph/bmz8.c new file mode 100644 index 000000000..15853c00b --- /dev/null +++ b/girepository/cmph/bmz8.c @@ -0,0 +1,647 @@ +#include "graph.h" +#include "bmz8.h" +#include "cmph_structs.h" +#include "bmz8_structs.h" +#include "hash.h" +#include "vqueue.h" +#include "bitbool.h" +#include +#include +#include +#include +#include +#include + +//#define DEBUG +#include "debug.h" + +static int bmz8_gen_edges(cmph_config_t *mph); +static cmph_uint8 bmz8_traverse_critical_nodes(bmz8_config_data_t *bmz8, cmph_uint32 v, cmph_uint8 * biggest_g_value, cmph_uint8 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited); +static cmph_uint8 bmz8_traverse_critical_nodes_heuristic(bmz8_config_data_t *bmz8, cmph_uint32 v, cmph_uint8 * biggest_g_value, cmph_uint8 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited); +static void bmz8_traverse_non_critical_nodes(bmz8_config_data_t *bmz8, cmph_uint8 * used_edges, cmph_uint8 * visited); + +bmz8_config_data_t *bmz8_config_new(void) +{ + bmz8_config_data_t *bmz8; + bmz8 = (bmz8_config_data_t *)malloc(sizeof(bmz8_config_data_t)); + assert(bmz8); + memset(bmz8, 0, sizeof(bmz8_config_data_t)); + bmz8->hashfuncs[0] = CMPH_HASH_JENKINS; + bmz8->hashfuncs[1] = CMPH_HASH_JENKINS; + bmz8->g = NULL; + bmz8->graph = NULL; + bmz8->hashes = NULL; + return bmz8; +} + +void bmz8_config_destroy(cmph_config_t *mph) +{ + bmz8_config_data_t *data = (bmz8_config_data_t *)mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void bmz8_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + bmz8_config_data_t *bmz8 = (bmz8_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint8 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 2) break; //bmz8 only uses two hash functions + bmz8->hashfuncs[i] = *hashptr; + ++i, ++hashptr; + } +} + +cmph_t *bmz8_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + bmz8_data_t *bmz8f = NULL; + cmph_uint8 i; + cmph_uint8 iterations; + cmph_uint8 iterations_map = 20; + cmph_uint8 *used_edges = NULL; + cmph_uint8 restart_mapping = 0; + cmph_uint8 * visited = NULL; + bmz8_config_data_t *bmz8 = (bmz8_config_data_t *)mph->data; + + if (mph->key_source->nkeys >= 256) + { + if (mph->verbosity) fprintf(stderr, "The number of keys in BMZ8 must be lower than 256.\n"); + return NULL; + } + if (c == 0) c = 1.15; // validating restrictions over parameter c. + DEBUGP("c: %f\n", c); + bmz8->m = (cmph_uint8) mph->key_source->nkeys; + bmz8->n = (cmph_uint8) ceil(c * mph->key_source->nkeys); + DEBUGP("m (edges): %u n (vertices): %u c: %f\n", bmz8->m, bmz8->n, c); + bmz8->graph = graph_new(bmz8->n, bmz8->m); + DEBUGP("Created graph\n"); + + bmz8->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*3); + for(i = 0; i < 3; ++i) bmz8->hashes[i] = NULL; + + do + { + // Mapping step + cmph_uint8 biggest_g_value = 0; + cmph_uint8 biggest_edge_value = 1; + iterations = 100; + if (mph->verbosity) + { + fprintf(stderr, "Entering mapping step for mph creation of %u keys with graph sized %u\n", bmz8->m, bmz8->n); + } + while(1) + { + int ok; + DEBUGP("hash function 1\n"); + bmz8->hashes[0] = hash_state_new(bmz8->hashfuncs[0], bmz8->n); + DEBUGP("hash function 2\n"); + bmz8->hashes[1] = hash_state_new(bmz8->hashfuncs[1], bmz8->n); + DEBUGP("Generating edges\n"); + ok = bmz8_gen_edges(mph); + if (!ok) + { + --iterations; + hash_state_destroy(bmz8->hashes[0]); + bmz8->hashes[0] = NULL; + hash_state_destroy(bmz8->hashes[1]); + bmz8->hashes[1] = NULL; + DEBUGP("%u iterations remaining\n", iterations); + if (mph->verbosity) + { + fprintf(stderr, "simple graph creation failure - %u iterations remaining\n", iterations); + } + if (iterations == 0) break; + } + else break; + } + if (iterations == 0) + { + graph_destroy(bmz8->graph); + return NULL; + } + + // Ordering step + if (mph->verbosity) + { + fprintf(stderr, "Starting ordering step\n"); + } + + graph_obtain_critical_nodes(bmz8->graph); + + // Searching step + if (mph->verbosity) + { + fprintf(stderr, "Starting Searching step.\n"); + fprintf(stderr, "\tTraversing critical vertices.\n"); + } + DEBUGP("Searching step\n"); + visited = (cmph_uint8 *)malloc((size_t)bmz8->n/8 + 1); + memset(visited, 0, (size_t)bmz8->n/8 + 1); + used_edges = (cmph_uint8 *)malloc((size_t)bmz8->m/8 + 1); + memset(used_edges, 0, (size_t)bmz8->m/8 + 1); + free(bmz8->g); + bmz8->g = (cmph_uint8 *)calloc((size_t)bmz8->n, sizeof(cmph_uint8)); + assert(bmz8->g); + for (i = 0; i < bmz8->n; ++i) // critical nodes + { + if (graph_node_is_critical(bmz8->graph, i) && (!GETBIT(visited,i))) + { + if(c > 1.14) restart_mapping = bmz8_traverse_critical_nodes(bmz8, i, &biggest_g_value, &biggest_edge_value, used_edges, visited); + else restart_mapping = bmz8_traverse_critical_nodes_heuristic(bmz8, i, &biggest_g_value, &biggest_edge_value, used_edges, visited); + if(restart_mapping) break; + } + } + if(!restart_mapping) + { + if (mph->verbosity) + { + fprintf(stderr, "\tTraversing non critical vertices.\n"); + } + bmz8_traverse_non_critical_nodes(bmz8, used_edges, visited); // non_critical_nodes + } + else + { + iterations_map--; + if (mph->verbosity) fprintf(stderr, "Restarting mapping step. %u iterations remaining.\n", iterations_map); + } + + free(used_edges); + free(visited); + + }while(restart_mapping && iterations_map > 0); + graph_destroy(bmz8->graph); + bmz8->graph = NULL; + if (iterations_map == 0) + { + return NULL; + } + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + bmz8f = (bmz8_data_t *)malloc(sizeof(bmz8_data_t)); + bmz8f->g = bmz8->g; + bmz8->g = NULL; //transfer memory ownership + bmz8f->hashes = bmz8->hashes; + bmz8->hashes = NULL; //transfer memory ownership + bmz8f->n = bmz8->n; + bmz8f->m = bmz8->m; + mphf->data = bmz8f; + mphf->size = bmz8->m; + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + return mphf; +} + +static cmph_uint8 bmz8_traverse_critical_nodes(bmz8_config_data_t *bmz8, cmph_uint32 v, cmph_uint8 * biggest_g_value, cmph_uint8 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited) +{ + cmph_uint8 next_g; + cmph_uint32 u; /* Auxiliary vertex */ + cmph_uint32 lav; /* lookahead vertex */ + cmph_uint8 collision; + vqueue_t * q = vqueue_new((cmph_uint32)(graph_ncritical_nodes(bmz8->graph))); + graph_iterator_t it, it1; + + DEBUGP("Labelling critical vertices\n"); + bmz8->g[v] = (cmph_uint8)(ceil ((double)(*biggest_edge_value)/2) - 1); + SETBIT(visited, v); + next_g = (cmph_uint8)floor((double)(*biggest_edge_value/2)); /* next_g is incremented in the do..while statement*/ + vqueue_insert(q, v); + while(!vqueue_is_empty(q)) + { + v = vqueue_remove(q); + it = graph_neighbors_it(bmz8->graph, v); + while ((u = graph_next_neighbor(bmz8->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz8->graph, u) && (!GETBIT(visited,u))) + { + collision = 1; + while(collision) // lookahead to resolve collisions + { + next_g = (cmph_uint8)(*biggest_g_value + 1); + it1 = graph_neighbors_it(bmz8->graph, u); + collision = 0; + while((lav = graph_next_neighbor(bmz8->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz8->graph, lav) && GETBIT(visited,lav)) + { + if(next_g + bmz8->g[lav] >= bmz8->m) + { + vqueue_destroy(q); + return 1; // restart mapping step. + } + if (GETBIT(used_edges, (next_g + bmz8->g[lav]))) + { + collision = 1; + break; + } + } + } + if (next_g > *biggest_g_value) *biggest_g_value = next_g; + } + // Marking used edges... + it1 = graph_neighbors_it(bmz8->graph, u); + while((lav = graph_next_neighbor(bmz8->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz8->graph, lav) && GETBIT(visited, lav)) + { + SETBIT(used_edges,(next_g + bmz8->g[lav])); + + if(next_g + bmz8->g[lav] > *biggest_edge_value) + *biggest_edge_value = (cmph_uint8)(next_g + bmz8->g[lav]); + } + } + bmz8->g[u] = next_g; // Labelling vertex u. + SETBIT(visited,u); + vqueue_insert(q, u); + } + } + + } + vqueue_destroy(q); + return 0; +} + +static cmph_uint8 bmz8_traverse_critical_nodes_heuristic(bmz8_config_data_t *bmz8, cmph_uint32 v, cmph_uint8 * biggest_g_value, cmph_uint8 * biggest_edge_value, cmph_uint8 * used_edges, cmph_uint8 * visited) +{ + cmph_uint8 next_g; + cmph_uint32 u; + cmph_uint32 lav; + cmph_uint8 collision; + cmph_uint8 * unused_g_values = NULL; + cmph_uint8 unused_g_values_capacity = 0; + cmph_uint8 nunused_g_values = 0; + vqueue_t * q = vqueue_new((cmph_uint32)(graph_ncritical_nodes(bmz8->graph))); + graph_iterator_t it, it1; + + DEBUGP("Labelling critical vertices\n"); + bmz8->g[v] = (cmph_uint8)(ceil ((double)(*biggest_edge_value)/2) - 1); + SETBIT(visited, v); + next_g = (cmph_uint8)floor((double)(*biggest_edge_value/2)); + vqueue_insert(q, v); + while(!vqueue_is_empty(q)) + { + v = vqueue_remove(q); + it = graph_neighbors_it(bmz8->graph, v); + while ((u = graph_next_neighbor(bmz8->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz8->graph, u) && (!GETBIT(visited,u))) + { + cmph_uint8 next_g_index = 0; + collision = 1; + while(collision) // lookahead to resolve collisions + { + if (next_g_index < nunused_g_values) + { + next_g = unused_g_values[next_g_index++]; + } + else + { + next_g = (cmph_uint8)(*biggest_g_value + 1); + next_g_index = 255;//UINT_MAX; + } + it1 = graph_neighbors_it(bmz8->graph, u); + collision = 0; + while((lav = graph_next_neighbor(bmz8->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz8->graph, lav) && GETBIT(visited,lav)) + { + if(next_g + bmz8->g[lav] >= bmz8->m) + { + vqueue_destroy(q); + free(unused_g_values); + return 1; // restart mapping step. + } + if (GETBIT(used_edges, (next_g + bmz8->g[lav]))) + { + collision = 1; + break; + } + } + } + if(collision && (next_g > *biggest_g_value)) // saving the current g value stored in next_g. + { + if(nunused_g_values == unused_g_values_capacity) + { + unused_g_values = (cmph_uint8*)realloc(unused_g_values, ((size_t)(unused_g_values_capacity + BUFSIZ))*sizeof(cmph_uint8)); + unused_g_values_capacity += (cmph_uint8)BUFSIZ; + } + unused_g_values[nunused_g_values++] = next_g; + + } + if (next_g > *biggest_g_value) *biggest_g_value = next_g; + } + + next_g_index--; + if (next_g_index < nunused_g_values) unused_g_values[next_g_index] = unused_g_values[--nunused_g_values]; + + // Marking used edges... + it1 = graph_neighbors_it(bmz8->graph, u); + while((lav = graph_next_neighbor(bmz8->graph, &it1)) != GRAPH_NO_NEIGHBOR) + { + if (graph_node_is_critical(bmz8->graph, lav) && GETBIT(visited, lav)) + { + SETBIT(used_edges,(next_g + bmz8->g[lav])); + if(next_g + bmz8->g[lav] > *biggest_edge_value) + *biggest_edge_value = (cmph_uint8)(next_g + bmz8->g[lav]); + } + } + + bmz8->g[u] = next_g; // Labelling vertex u. + SETBIT(visited, u); + vqueue_insert(q, u); + + } + } + + } + vqueue_destroy(q); + free(unused_g_values); + return 0; +} + +static cmph_uint8 next_unused_edge(bmz8_config_data_t *bmz8, cmph_uint8 * used_edges, cmph_uint32 unused_edge_index) +{ + while(1) + { + assert(unused_edge_index < bmz8->m); + if(GETBIT(used_edges, unused_edge_index)) unused_edge_index ++; + else break; + } + return (cmph_uint8)unused_edge_index; +} + +static void bmz8_traverse(bmz8_config_data_t *bmz8, cmph_uint8 * used_edges, cmph_uint32 v, cmph_uint8 * unused_edge_index, cmph_uint8 * visited) +{ + graph_iterator_t it = graph_neighbors_it(bmz8->graph, v); + cmph_uint32 neighbor = 0; + while((neighbor = graph_next_neighbor(bmz8->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + if(GETBIT(visited,neighbor)) continue; + //DEBUGP("Visiting neighbor %u\n", neighbor); + *unused_edge_index = next_unused_edge(bmz8, used_edges, *unused_edge_index); + bmz8->g[neighbor] = (cmph_uint8)(*unused_edge_index - bmz8->g[v]); + //if (bmz8->g[neighbor] >= bmz8->m) bmz8->g[neighbor] += bmz8->m; + SETBIT(visited, neighbor); + (*unused_edge_index)++; + bmz8_traverse(bmz8, used_edges, neighbor, unused_edge_index, visited); + + } +} + +static void bmz8_traverse_non_critical_nodes(bmz8_config_data_t *bmz8, cmph_uint8 * used_edges, cmph_uint8 * visited) +{ + + cmph_uint8 i, v1, v2, unused_edge_index = 0; + DEBUGP("Labelling non critical vertices\n"); + for(i = 0; i < bmz8->m; i++) + { + v1 = (cmph_uint8)graph_vertex_id(bmz8->graph, i, 0); + v2 = (cmph_uint8)graph_vertex_id(bmz8->graph, i, 1); + if((GETBIT(visited,v1) && GETBIT(visited,v2)) || (!GETBIT(visited,v1) && !GETBIT(visited,v2))) continue; + if(GETBIT(visited,v1)) bmz8_traverse(bmz8, used_edges, v1, &unused_edge_index, visited); + else bmz8_traverse(bmz8, used_edges, v2, &unused_edge_index, visited); + + } + + for(i = 0; i < bmz8->n; i++) + { + if(!GETBIT(visited,i)) + { + bmz8->g[i] = 0; + SETBIT(visited, i); + bmz8_traverse(bmz8, used_edges, i, &unused_edge_index, visited); + } + } + +} + +static int bmz8_gen_edges(cmph_config_t *mph) +{ + cmph_uint8 e; + bmz8_config_data_t *bmz8 = (bmz8_config_data_t *)mph->data; + cmph_uint8 multiple_edges = 0; + DEBUGP("Generating edges for %u vertices\n", bmz8->n); + graph_clear_edges(bmz8->graph); + mph->key_source->rewind(mph->key_source->data); + for (e = 0; e < mph->key_source->nkeys; ++e) + { + cmph_uint8 h1, h2; + cmph_uint32 keylen; + char *key = NULL; + mph->key_source->read(mph->key_source->data, &key, &keylen); + +// if (key == NULL)fprintf(stderr, "key = %s -- read BMZ\n", key); + h1 = (cmph_uint8)(hash(bmz8->hashes[0], key, keylen) % bmz8->n); + h2 = (cmph_uint8)(hash(bmz8->hashes[1], key, keylen) % bmz8->n); + if (h1 == h2) if (++h2 >= bmz8->n) h2 = 0; + if (h1 == h2) + { + if (mph->verbosity) fprintf(stderr, "Self loop for key %u\n", e); + mph->key_source->dispose(mph->key_source->data, key, keylen); + return 0; + } + //DEBUGP("Adding edge: %u -> %u for key %s\n", h1, h2, key); + mph->key_source->dispose(mph->key_source->data, key, keylen); +// fprintf(stderr, "key = %s -- dispose BMZ\n", key); + multiple_edges = graph_contains_edge(bmz8->graph, h1, h2); + if (mph->verbosity && multiple_edges) fprintf(stderr, "A non simple graph was generated\n"); + if (multiple_edges) return 0; // checking multiple edge restriction. + graph_add_edge(bmz8->graph, h1, h2); + } + return !multiple_edges; +} + +int bmz8_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint8 two = 2; //number of hash functions + bmz8_data_t *data = (bmz8_data_t *)mphf->data; + register size_t nbytes; + __cmph_dump(mphf, fd); + + nbytes = fwrite(&two, sizeof(cmph_uint8), (size_t)1, fd); + + hash_state_dump(data->hashes[0], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + hash_state_dump(data->hashes[1], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + nbytes = fwrite(&(data->n), sizeof(cmph_uint8), (size_t)1, fd); + nbytes = fwrite(&(data->m), sizeof(cmph_uint8), (size_t)1, fd); + + nbytes = fwrite(data->g, sizeof(cmph_uint8)*(data->n), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } +/* #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < data->n; ++i) fprintf(stderr, "%u ", data->g[i]); + fprintf(stderr, "\n"); + #endif*/ + return 1; +} + +void bmz8_load(FILE *f, cmph_t *mphf) +{ + cmph_uint8 nhashes; + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint8 i; + register size_t nbytes; + bmz8_data_t *bmz8 = (bmz8_data_t *)malloc(sizeof(bmz8_data_t)); + + DEBUGP("Loading bmz8 mphf\n"); + mphf->data = bmz8; + nbytes = fread(&nhashes, sizeof(cmph_uint8), (size_t)1, f); + bmz8->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*(size_t)(nhashes + 1)); + bmz8->hashes[nhashes] = NULL; + DEBUGP("Reading %u hashes\n", nhashes); + for (i = 0; i < nhashes; ++i) + { + hash_state_t *state = NULL; + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + state = hash_state_load(buf, buflen); + bmz8->hashes[i] = state; + free(buf); + } + + DEBUGP("Reading m and n\n"); + nbytes = fread(&(bmz8->n), sizeof(cmph_uint8), (size_t)1, f); + nbytes = fread(&(bmz8->m), sizeof(cmph_uint8), (size_t)1, f); + + bmz8->g = (cmph_uint8 *)malloc(sizeof(cmph_uint8)*bmz8->n); + nbytes = fread(bmz8->g, bmz8->n*sizeof(cmph_uint8), (size_t)1, f); + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return; + } + + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < bmz8->n; ++i) fprintf(stderr, "%u ", bmz8->g[i]); + fprintf(stderr, "\n"); + #endif + return; +} + + +cmph_uint8 bmz8_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + bmz8_data_t *bmz8 = mphf->data; + cmph_uint8 h1 = (cmph_uint8)(hash(bmz8->hashes[0], key, keylen) % bmz8->n); + cmph_uint8 h2 = (cmph_uint8)(hash(bmz8->hashes[1], key, keylen) % bmz8->n); + DEBUGP("key: %s h1: %u h2: %u\n", key, h1, h2); + if (h1 == h2 && ++h2 > bmz8->n) h2 = 0; + DEBUGP("key: %s g[h1]: %u g[h2]: %u edges: %u\n", key, bmz8->g[h1], bmz8->g[h2], bmz8->m); + return (cmph_uint8)(bmz8->g[h1] + bmz8->g[h2]); +} +void bmz8_destroy(cmph_t *mphf) +{ + bmz8_data_t *data = (bmz8_data_t *)mphf->data; + free(data->g); + hash_state_destroy(data->hashes[0]); + hash_state_destroy(data->hashes[1]); + free(data->hashes); + free(data); + free(mphf); +} + +/** \fn void bmz8_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bmz8_pack(cmph_t *mphf, void *packed_mphf) +{ + bmz8_data_t *data = (bmz8_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + CMPH_HASH h2_type; + + // packing h1 type + CMPH_HASH h1_type = hash_get_type(data->hashes[0]); + *((cmph_uint32 *) ptr) = h1_type; + ptr += sizeof(cmph_uint32); + + // packing h1 + hash_state_pack(data->hashes[0], ptr); + ptr += hash_state_packed_size(h1_type); + + // packing h2 type + h2_type = hash_get_type(data->hashes[1]); + *((cmph_uint32 *) ptr) = h2_type; + ptr += sizeof(cmph_uint32); + + // packing h2 + hash_state_pack(data->hashes[1], ptr); + ptr += hash_state_packed_size(h2_type); + + // packing n + *ptr++ = data->n; + + // packing g + memcpy(ptr, data->g, sizeof(cmph_uint8)*data->n); +} + +/** \fn cmph_uint32 bmz8_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bmz8_packed_size(cmph_t *mphf) +{ + bmz8_data_t *data = (bmz8_data_t *)mphf->data; + CMPH_HASH h1_type = hash_get_type(data->hashes[0]); + CMPH_HASH h2_type = hash_get_type(data->hashes[1]); + + return (cmph_uint32)(sizeof(CMPH_ALGO) + hash_state_packed_size(h1_type) + hash_state_packed_size(h2_type) + + 2*sizeof(cmph_uint32) + sizeof(cmph_uint8) + sizeof(cmph_uint8)*data->n); +} + +/** cmph_uint8 bmz8_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint8 bmz8_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + register cmph_uint8 *h1_ptr = packed_mphf; + register CMPH_HASH h1_type = *((cmph_uint32 *)h1_ptr); + register cmph_uint8 *h2_ptr; + register CMPH_HASH h2_type; + register cmph_uint8 *g_ptr, n, h1, h2; + + h1_ptr += 4; + + h2_ptr = h1_ptr + hash_state_packed_size(h1_type); + h2_type = *((cmph_uint32 *)h2_ptr); + h2_ptr += 4; + + g_ptr = h2_ptr + hash_state_packed_size(h2_type); + + n = *g_ptr++; + + h1 = (cmph_uint8)(hash_packed(h1_ptr, h1_type, key, keylen) % n); + h2 = (cmph_uint8)(hash_packed(h2_ptr, h2_type, key, keylen) % n); + DEBUGP("key: %s h1: %u h2: %u\n", key, h1, h2); + if (h1 == h2 && ++h2 > n) h2 = 0; + return (cmph_uint8)(g_ptr[h1] + g_ptr[h2]); +} diff --git a/girepository/cmph/bmz8.h b/girepository/cmph/bmz8.h new file mode 100644 index 000000000..99f7e30d6 --- /dev/null +++ b/girepository/cmph/bmz8.h @@ -0,0 +1,42 @@ +#ifndef __CMPH_BMZ8_H__ +#define __CMPH_BMZ8_H__ + +#include "cmph.h" + +typedef struct __bmz8_data_t bmz8_data_t; +typedef struct __bmz8_config_data_t bmz8_config_data_t; + +bmz8_config_data_t *bmz8_config_new(void); +void bmz8_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void bmz8_config_destroy(cmph_config_t *mph); +cmph_t *bmz8_new(cmph_config_t *mph, double c); + +void bmz8_load(FILE *f, cmph_t *mphf); +int bmz8_dump(cmph_t *mphf, FILE *f); +void bmz8_destroy(cmph_t *mphf); +cmph_uint8 bmz8_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void bmz8_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void bmz8_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 bmz8_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 bmz8_packed_size(cmph_t *mphf); + +/** cmph_uint8 bmz8_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint8 bmz8_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/bmz8_structs.h b/girepository/cmph/bmz8_structs.h new file mode 100644 index 000000000..408b52998 --- /dev/null +++ b/girepository/cmph/bmz8_structs.h @@ -0,0 +1,25 @@ +#ifndef __CMPH_BMZ8_STRUCTS_H__ +#define __CMPH_BMZ8_STRUCTS_H__ + +#include "hash_state.h" + +struct __bmz8_data_t +{ + cmph_uint8 m; //edges (words) count + cmph_uint8 n; //vertex count + cmph_uint8 *g; + hash_state_t **hashes; +}; + + +struct __bmz8_config_data_t +{ + CMPH_HASH hashfuncs[2]; + cmph_uint8 m; //edges (words) count + cmph_uint8 n; //vertex count + graph_t *graph; + cmph_uint8 *g; + hash_state_t **hashes; +}; + +#endif diff --git a/girepository/cmph/bmz_structs.h b/girepository/cmph/bmz_structs.h new file mode 100644 index 000000000..67065a005 --- /dev/null +++ b/girepository/cmph/bmz_structs.h @@ -0,0 +1,25 @@ +#ifndef __CMPH_BMZ_STRUCTS_H__ +#define __CMPH_BMZ_STRUCTS_H__ + +#include "hash_state.h" + +struct __bmz_data_t +{ + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + cmph_uint32 *g; + hash_state_t **hashes; +}; + + +struct __bmz_config_data_t +{ + CMPH_HASH hashfuncs[2]; + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + graph_t *graph; + cmph_uint32 *g; + hash_state_t **hashes; +}; + +#endif diff --git a/girepository/cmph/brz.c b/girepository/cmph/brz.c new file mode 100644 index 000000000..25feb6536 --- /dev/null +++ b/girepository/cmph/brz.c @@ -0,0 +1,1040 @@ +#include "graph.h" +#include "fch.h" +#include "fch_structs.h" +#include "bmz8.h" +#include "bmz8_structs.h" +#include "brz.h" +#include "cmph_structs.h" +#include "brz_structs.h" +#include "buffer_manager.h" +#include "cmph.h" +#include "hash.h" +#include "bitbool.h" +#include +#include +#include +#include +#include +#include +#define MAX_BUCKET_SIZE 255 +//#define DEBUG +#include "debug.h" + +#if defined (__ia64) || defined (__x86_64__) || defined (_WIN64) +# define __brz_use_64bit__ +#endif + +static int brz_gen_mphf(cmph_config_t *mph); +static cmph_uint32 brz_min_index(cmph_uint32 * vector, cmph_uint32 n); +static void brz_destroy_keys_vd(cmph_uint8 ** keys_vd, cmph_uint32 nkeys); +static char * brz_copy_partial_fch_mphf(brz_config_data_t *brz, fch_data_t * fchf, cmph_uint32 index, cmph_uint32 *buflen); +static char * brz_copy_partial_bmz8_mphf(brz_config_data_t *brz, bmz8_data_t * bmzf, cmph_uint32 index, cmph_uint32 *buflen); +brz_config_data_t *brz_config_new(void) +{ + brz_config_data_t *brz = NULL; + brz = (brz_config_data_t *)malloc(sizeof(brz_config_data_t)); + brz->algo = CMPH_FCH; + brz->b = 128; + brz->hashfuncs[0] = CMPH_HASH_JENKINS; + brz->hashfuncs[1] = CMPH_HASH_JENKINS; + brz->hashfuncs[2] = CMPH_HASH_JENKINS; + brz->size = NULL; + brz->offset = NULL; + brz->g = NULL; + brz->h1 = NULL; + brz->h2 = NULL; + brz->h0 = NULL; + brz->memory_availability = 1024*1024; + brz->tmp_dir = (cmph_uint8 *)calloc((size_t)10, sizeof(cmph_uint8)); + brz->mphf_fd = NULL; + strcpy((char *)(brz->tmp_dir), "/var/tmp/"); + assert(brz); + return brz; +} + +void brz_config_destroy(cmph_config_t *mph) +{ + brz_config_data_t *data = (brz_config_data_t *)mph->data; + free(data->tmp_dir); + DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void brz_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + brz_config_data_t *brz = (brz_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 3) break; //brz only uses three hash functions + brz->hashfuncs[i] = *hashptr; + ++i, ++hashptr; + } +} + +void brz_config_set_memory_availability(cmph_config_t *mph, cmph_uint32 memory_availability) +{ + brz_config_data_t *brz = (brz_config_data_t *)mph->data; + if(memory_availability > 0) brz->memory_availability = memory_availability*1024*1024; +} + +void brz_config_set_tmp_dir(cmph_config_t *mph, cmph_uint8 *tmp_dir) +{ + brz_config_data_t *brz = (brz_config_data_t *)mph->data; + if(tmp_dir) + { + size_t len = strlen((char *)tmp_dir); + free(brz->tmp_dir); + if(tmp_dir[len-1] != '/') + { + brz->tmp_dir = (cmph_uint8 *)calloc((size_t)len+2, sizeof(cmph_uint8)); + sprintf((char *)(brz->tmp_dir), "%s/", (char *)tmp_dir); + } + else + { + brz->tmp_dir = (cmph_uint8 *)calloc((size_t)len+1, sizeof(cmph_uint8)); + sprintf((char *)(brz->tmp_dir), "%s", (char *)tmp_dir); + } + + } +} + +void brz_config_set_mphf_fd(cmph_config_t *mph, FILE *mphf_fd) +{ + brz_config_data_t *brz = (brz_config_data_t *)mph->data; + brz->mphf_fd = mphf_fd; + assert(brz->mphf_fd); +} + +void brz_config_set_b(cmph_config_t *mph, cmph_uint32 b) +{ + brz_config_data_t *brz = (brz_config_data_t *)mph->data; + if(b <= 64 || b >= 175) + { + b = 128; + } + brz->b = (cmph_uint8)b; +} + +void brz_config_set_algo(cmph_config_t *mph, CMPH_ALGO algo) +{ + if (algo == CMPH_BMZ8 || algo == CMPH_FCH) // supported algorithms + { + brz_config_data_t *brz = (brz_config_data_t *)mph->data; + brz->algo = algo; + } +} + +cmph_t *brz_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + brz_data_t *brzf = NULL; + cmph_uint32 i; + cmph_uint32 iterations = 20; + brz_config_data_t *brz; + + DEBUGP("c: %f\n", c); + brz = (brz_config_data_t *)mph->data; + switch(brz->algo) // validating restrictions over parameter c. + { + case CMPH_BMZ8: + if (c == 0 || c >= 2.0) c = 1; + break; + case CMPH_FCH: + if (c <= 2.0) c = 2.6; + break; + default: + assert(0); + } + brz->c = c; + brz->m = mph->key_source->nkeys; + DEBUGP("m: %u\n", brz->m); + brz->k = (cmph_uint32)ceil(brz->m/((double)brz->b)); + DEBUGP("k: %u\n", brz->k); + brz->size = (cmph_uint8 *) calloc((size_t)brz->k, sizeof(cmph_uint8)); + + // Clustering the keys by graph id. + if (mph->verbosity) + { + fprintf(stderr, "Partioning the set of keys.\n"); + } + + while(1) + { + int ok; + DEBUGP("hash function 3\n"); + brz->h0 = hash_state_new(brz->hashfuncs[2], brz->k); + DEBUGP("Generating graphs\n"); + ok = brz_gen_mphf(mph); + if (!ok) + { + --iterations; + hash_state_destroy(brz->h0); + brz->h0 = NULL; + DEBUGP("%u iterations remaining to create the graphs in a external file\n", iterations); + if (mph->verbosity) + { + fprintf(stderr, "Failure: A graph with more than 255 keys was created - %u iterations remaining\n", iterations); + } + if (iterations == 0) break; + } + else break; + } + if (iterations == 0) + { + DEBUGP("Graphs with more than 255 keys were created in all 20 iterations\n"); + free(brz->size); + return NULL; + } + DEBUGP("Graphs generated\n"); + + brz->offset = (cmph_uint32 *)calloc((size_t)brz->k, sizeof(cmph_uint32)); + for (i = 1; i < brz->k; ++i) + { + brz->offset[i] = brz->size[i-1] + brz->offset[i-1]; + } + // Generating a mphf + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + brzf = (brz_data_t *)malloc(sizeof(brz_data_t)); + brzf->g = brz->g; + brz->g = NULL; //transfer memory ownership + brzf->h1 = brz->h1; + brz->h1 = NULL; //transfer memory ownership + brzf->h2 = brz->h2; + brz->h2 = NULL; //transfer memory ownership + brzf->h0 = brz->h0; + brz->h0 = NULL; //transfer memory ownership + brzf->size = brz->size; + brz->size = NULL; //transfer memory ownership + brzf->offset = brz->offset; + brz->offset = NULL; //transfer memory ownership + brzf->k = brz->k; + brzf->c = brz->c; + brzf->m = brz->m; + brzf->algo = brz->algo; + mphf->data = brzf; + mphf->size = brz->m; + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + return mphf; +} + +static int brz_gen_mphf(cmph_config_t *mph) +{ + cmph_uint32 i, e, error; + brz_config_data_t *brz = (brz_config_data_t *)mph->data; + cmph_uint32 memory_usage = 0; + cmph_uint32 nkeys_in_buffer = 0; + cmph_uint8 *buffer = (cmph_uint8 *)malloc((size_t)brz->memory_availability); + cmph_uint32 *buckets_size = (cmph_uint32 *)calloc((size_t)brz->k, sizeof(cmph_uint32)); + cmph_uint32 *keys_index = NULL; + cmph_uint8 **buffer_merge = NULL; + cmph_uint32 *buffer_h0 = NULL; + cmph_uint32 nflushes = 0; + cmph_uint32 h0; + register size_t nbytes; + FILE * tmp_fd = NULL; + buffer_manager_t * buff_manager = NULL; + char *filename = NULL; + char *key = NULL; + cmph_uint32 keylen; + cmph_uint32 cur_bucket = 0; + cmph_uint8 nkeys_vd = 0; + cmph_uint8 ** keys_vd = NULL; + + mph->key_source->rewind(mph->key_source->data); + DEBUGP("Generating graphs from %u keys\n", brz->m); + // Partitioning + for (e = 0; e < brz->m; ++e) + { + mph->key_source->read(mph->key_source->data, &key, &keylen); + + /* Buffers management */ + if (memory_usage + keylen + sizeof(keylen) > brz->memory_availability) // flush buffers + { + cmph_uint32 value, sum, keylen1; + if(mph->verbosity) + { + fprintf(stderr, "Flushing %u\n", nkeys_in_buffer); + } + value = buckets_size[0]; + sum = 0; + keylen1 = 0; + buckets_size[0] = 0; + for(i = 1; i < brz->k; i++) + { + if(buckets_size[i] == 0) continue; + sum += value; + value = buckets_size[i]; + buckets_size[i] = sum; + + } + memory_usage = 0; + keys_index = (cmph_uint32 *)calloc((size_t)nkeys_in_buffer, sizeof(cmph_uint32)); + for(i = 0; i < nkeys_in_buffer; i++) + { + memcpy(&keylen1, buffer + memory_usage, sizeof(keylen1)); + h0 = hash(brz->h0, (char *)(buffer + memory_usage + sizeof(keylen1)), keylen1) % brz->k; + keys_index[buckets_size[h0]] = memory_usage; + buckets_size[h0]++; + memory_usage += keylen1 + (cmph_uint32)sizeof(keylen1); + } + filename = (char *)calloc(strlen((char *)(brz->tmp_dir)) + 11, sizeof(char)); + sprintf(filename, "%s%u.cmph",brz->tmp_dir, nflushes); + tmp_fd = fopen(filename, "wb"); + free(filename); + filename = NULL; + for(i = 0; i < nkeys_in_buffer; i++) + { + memcpy(&keylen1, buffer + keys_index[i], sizeof(keylen1)); + nbytes = fwrite(buffer + keys_index[i], (size_t)1, keylen1 + sizeof(keylen1), tmp_fd); + } + nkeys_in_buffer = 0; + memory_usage = 0; + memset((void *)buckets_size, 0, brz->k*sizeof(cmph_uint32)); + nflushes++; + free(keys_index); + fclose(tmp_fd); + } + memcpy(buffer + memory_usage, &keylen, sizeof(keylen)); + memcpy(buffer + memory_usage + sizeof(keylen), key, (size_t)keylen); + memory_usage += keylen + (cmph_uint32)sizeof(keylen); + h0 = hash(brz->h0, key, keylen) % brz->k; + + if ((brz->size[h0] == MAX_BUCKET_SIZE) || (brz->algo == CMPH_BMZ8 && ((brz->c >= 1.0) && (cmph_uint8)(brz->c * brz->size[h0]) < brz->size[h0]))) + { + free(buffer); + free(buckets_size); + return 0; + } + brz->size[h0] = (cmph_uint8)(brz->size[h0] + 1U); + buckets_size[h0] ++; + nkeys_in_buffer++; + mph->key_source->dispose(mph->key_source->data, key, keylen); + } + if (memory_usage != 0) // flush buffers + { + cmph_uint32 value; + cmph_uint32 sum, keylen1; + if(mph->verbosity) + { + fprintf(stderr, "Flushing %u\n", nkeys_in_buffer); + } + value = buckets_size[0]; + sum = 0; + keylen1 = 0; + buckets_size[0] = 0; + for(i = 1; i < brz->k; i++) + { + if(buckets_size[i] == 0) continue; + sum += value; + value = buckets_size[i]; + buckets_size[i] = sum; + } + memory_usage = 0; + keys_index = (cmph_uint32 *)calloc((size_t)nkeys_in_buffer, sizeof(cmph_uint32)); + for(i = 0; i < nkeys_in_buffer; i++) + { + memcpy(&keylen1, buffer + memory_usage, sizeof(keylen1)); + h0 = hash(brz->h0, (char *)(buffer + memory_usage + sizeof(keylen1)), keylen1) % brz->k; + keys_index[buckets_size[h0]] = memory_usage; + buckets_size[h0]++; + memory_usage += keylen1 + (cmph_uint32)sizeof(keylen1); + } + filename = (char *)calloc(strlen((char *)(brz->tmp_dir)) + 11, sizeof(char)); + sprintf(filename, "%s%u.cmph",brz->tmp_dir, nflushes); + tmp_fd = fopen(filename, "wb"); + free(filename); + filename = NULL; + for(i = 0; i < nkeys_in_buffer; i++) + { + memcpy(&keylen1, buffer + keys_index[i], sizeof(keylen1)); + nbytes = fwrite(buffer + keys_index[i], (size_t)1, keylen1 + sizeof(keylen1), tmp_fd); + } + nkeys_in_buffer = 0; + memory_usage = 0; + memset((void *)buckets_size, 0, brz->k*sizeof(cmph_uint32)); + nflushes++; + free(keys_index); + fclose(tmp_fd); + } + + free(buffer); + free(buckets_size); + if(nflushes > 1024) return 0; // Too many files generated. + // mphf generation + if(mph->verbosity) + { + fprintf(stderr, "\nMPHF generation \n"); + } + /* Starting to dump to disk the resultant MPHF: __cmph_dump function */ + nbytes = fwrite(cmph_names[CMPH_BRZ], (size_t)(strlen(cmph_names[CMPH_BRZ]) + 1), (size_t)1, brz->mphf_fd); + nbytes = fwrite(&(brz->m), sizeof(brz->m), (size_t)1, brz->mphf_fd); + nbytes = fwrite(&(brz->c), sizeof(double), (size_t)1, brz->mphf_fd); + nbytes = fwrite(&(brz->algo), sizeof(brz->algo), (size_t)1, brz->mphf_fd); + nbytes = fwrite(&(brz->k), sizeof(cmph_uint32), (size_t)1, brz->mphf_fd); // number of MPHFs + nbytes = fwrite(brz->size, sizeof(cmph_uint8)*(brz->k), (size_t)1, brz->mphf_fd); + if (nbytes == 0 && ferror(brz->mphf_fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + + //tmp_fds = (FILE **)calloc(nflushes, sizeof(FILE *)); + buff_manager = buffer_manager_new(brz->memory_availability, nflushes); + buffer_merge = (cmph_uint8 **)calloc((size_t)nflushes, sizeof(cmph_uint8 *)); + buffer_h0 = (cmph_uint32 *)calloc((size_t)nflushes, sizeof(cmph_uint32)); + + memory_usage = 0; + for(i = 0; i < nflushes; i++) + { + filename = (char *)calloc(strlen((char *)(brz->tmp_dir)) + 11, sizeof(char)); + sprintf(filename, "%s%u.cmph",brz->tmp_dir, i); + buffer_manager_open(buff_manager, i, filename); + free(filename); + filename = NULL; + key = (char *)buffer_manager_read_key(buff_manager, i, &keylen); + h0 = hash(brz->h0, key+sizeof(keylen), keylen) % brz->k; + buffer_h0[i] = h0; + buffer_merge[i] = (cmph_uint8 *)key; + key = NULL; //transfer memory ownership + } + e = 0; + keys_vd = (cmph_uint8 **)calloc((size_t)MAX_BUCKET_SIZE, sizeof(cmph_uint8 *)); + nkeys_vd = 0; + error = 0; + while(e < brz->m) + { + i = brz_min_index(buffer_h0, nflushes); + cur_bucket = buffer_h0[i]; + key = (char *)buffer_manager_read_key(buff_manager, i, &keylen); + if(key) + { + while(key) + { + //keylen = strlen(key); + h0 = hash(brz->h0, key+sizeof(keylen), keylen) % brz->k; + if (h0 != buffer_h0[i]) break; + keys_vd[nkeys_vd++] = (cmph_uint8 *)key; + key = NULL; //transfer memory ownership + e++; + key = (char *)buffer_manager_read_key(buff_manager, i, &keylen); + } + if (key) + { + assert(nkeys_vd < brz->size[cur_bucket]); + keys_vd[nkeys_vd++] = buffer_merge[i]; + buffer_merge[i] = NULL; //transfer memory ownership + e++; + buffer_h0[i] = h0; + buffer_merge[i] = (cmph_uint8 *)key; + } + } + if(!key) + { + assert(nkeys_vd < brz->size[cur_bucket]); + keys_vd[nkeys_vd++] = buffer_merge[i]; + buffer_merge[i] = NULL; //transfer memory ownership + e++; + buffer_h0[i] = UINT_MAX; + } + + if(nkeys_vd == brz->size[cur_bucket]) // Generating mphf for each bucket. + { + cmph_io_adapter_t *source = NULL; + cmph_config_t *config = NULL; + cmph_t *mphf_tmp = NULL; + char *bufmphf = NULL; + cmph_uint32 buflenmphf = 0; + // Source of keys + source = cmph_io_byte_vector_adapter(keys_vd, (cmph_uint32)nkeys_vd); + config = cmph_config_new(source); + cmph_config_set_algo(config, brz->algo); + //cmph_config_set_algo(config, CMPH_BMZ8); + cmph_config_set_graphsize(config, brz->c); + mphf_tmp = cmph_new(config); + if (mphf_tmp == NULL) + { + if(mph->verbosity) fprintf(stderr, "ERROR: Can't generate MPHF for bucket %u out of %u\n", cur_bucket + 1, brz->k); + error = 1; + cmph_config_destroy(config); + brz_destroy_keys_vd(keys_vd, nkeys_vd); + cmph_io_byte_vector_adapter_destroy(source); + break; + } + if(mph->verbosity) + { + if (cur_bucket % 1000 == 0) + { + fprintf(stderr, "MPHF for bucket %u out of %u was generated.\n", cur_bucket + 1, brz->k); + } + } + switch(brz->algo) + { + case CMPH_FCH: + { + fch_data_t * fchf = NULL; + fchf = (fch_data_t *)mphf_tmp->data; + bufmphf = brz_copy_partial_fch_mphf(brz, fchf, cur_bucket, &buflenmphf); + } + break; + case CMPH_BMZ8: + { + bmz8_data_t * bmzf = NULL; + bmzf = (bmz8_data_t *)mphf_tmp->data; + bufmphf = brz_copy_partial_bmz8_mphf(brz, bmzf, cur_bucket, &buflenmphf); + } + break; + default: assert(0); + } + nbytes = fwrite(bufmphf, (size_t)buflenmphf, (size_t)1, brz->mphf_fd); + free(bufmphf); + bufmphf = NULL; + cmph_config_destroy(config); + brz_destroy_keys_vd(keys_vd, nkeys_vd); + cmph_destroy(mphf_tmp); + cmph_io_byte_vector_adapter_destroy(source); + nkeys_vd = 0; + } + } + buffer_manager_destroy(buff_manager); + free(keys_vd); + free(buffer_merge); + free(buffer_h0); + if (error) return 0; + return 1; +} + +static cmph_uint32 brz_min_index(cmph_uint32 * vector, cmph_uint32 n) +{ + cmph_uint32 i, min_index = 0; + for(i = 1; i < n; i++) + { + if(vector[i] < vector[min_index]) min_index = i; + } + return min_index; +} + +static void brz_destroy_keys_vd(cmph_uint8 ** keys_vd, cmph_uint32 nkeys) +{ + cmph_uint8 i; + for(i = 0; i < nkeys; i++) { free(keys_vd[i]); keys_vd[i] = NULL;} +} + +static char * brz_copy_partial_fch_mphf(brz_config_data_t *brz, fch_data_t * fchf, cmph_uint32 index, cmph_uint32 *buflen) +{ + cmph_uint32 i = 0; + cmph_uint32 buflenh1 = 0; + cmph_uint32 buflenh2 = 0; + char * bufh1 = NULL; + char * bufh2 = NULL; + char * buf = NULL; + cmph_uint32 n = fchf->b;//brz->size[index]; + hash_state_dump(fchf->h1, &bufh1, &buflenh1); + hash_state_dump(fchf->h2, &bufh2, &buflenh2); + *buflen = buflenh1 + buflenh2 + n + 2U * (cmph_uint32)sizeof(cmph_uint32); + buf = (char *)malloc((size_t)(*buflen)); + memcpy(buf, &buflenh1, sizeof(cmph_uint32)); + memcpy(buf+sizeof(cmph_uint32), bufh1, (size_t)buflenh1); + memcpy(buf+sizeof(cmph_uint32)+buflenh1, &buflenh2, sizeof(cmph_uint32)); + memcpy(buf+2*sizeof(cmph_uint32)+buflenh1, bufh2, (size_t)buflenh2); + for (i = 0; i < n; i++) memcpy(buf+2*sizeof(cmph_uint32)+buflenh1+buflenh2+i,(fchf->g + i), (size_t)1); + free(bufh1); + free(bufh2); + return buf; +} +static char * brz_copy_partial_bmz8_mphf(brz_config_data_t *brz, bmz8_data_t * bmzf, cmph_uint32 index, cmph_uint32 *buflen) +{ + cmph_uint32 buflenh1 = 0; + cmph_uint32 buflenh2 = 0; + char * bufh1 = NULL; + char * bufh2 = NULL; + char * buf = NULL; + cmph_uint32 n = (cmph_uint32)ceil(brz->c * brz->size[index]); + hash_state_dump(bmzf->hashes[0], &bufh1, &buflenh1); + hash_state_dump(bmzf->hashes[1], &bufh2, &buflenh2); + *buflen = buflenh1 + buflenh2 + n + 2U * (cmph_uint32)sizeof(cmph_uint32); + buf = (char *)malloc((size_t)(*buflen)); + memcpy(buf, &buflenh1, sizeof(cmph_uint32)); + memcpy(buf+sizeof(cmph_uint32), bufh1, (size_t)buflenh1); + memcpy(buf+sizeof(cmph_uint32)+buflenh1, &buflenh2, sizeof(cmph_uint32)); + memcpy(buf+2*sizeof(cmph_uint32)+buflenh1, bufh2, (size_t)buflenh2); + memcpy(buf+2*sizeof(cmph_uint32)+buflenh1+buflenh2,bmzf->g, (size_t)n); + free(bufh1); + free(bufh2); + return buf; +} + + +int brz_dump(cmph_t *mphf, FILE *fd) +{ + brz_data_t *data = (brz_data_t *)mphf->data; + char *buf = NULL; + cmph_uint32 buflen; + register size_t nbytes; + DEBUGP("Dumping brzf\n"); + // The initial part of the MPHF have already been dumped to disk during construction + // Dumping h0 + hash_state_dump(data->h0, &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + // Dumping m and the vector offset. + nbytes = fwrite(&(data->m), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(data->offset, sizeof(cmph_uint32)*(data->k), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + return 1; +} + +void brz_load(FILE *f, cmph_t *mphf) +{ + char *buf = NULL; + cmph_uint32 buflen; + register size_t nbytes; + cmph_uint32 i, n; + brz_data_t *brz = (brz_data_t *)malloc(sizeof(brz_data_t)); + + DEBUGP("Loading brz mphf\n"); + mphf->data = brz; + nbytes = fread(&(brz->c), sizeof(double), (size_t)1, f); + nbytes = fread(&(brz->algo), sizeof(brz->algo), (size_t)1, f); // Reading algo. + nbytes = fread(&(brz->k), sizeof(cmph_uint32), (size_t)1, f); + brz->size = (cmph_uint8 *) malloc(sizeof(cmph_uint8)*brz->k); + nbytes = fread(brz->size, sizeof(cmph_uint8)*(brz->k), (size_t)1, f); + brz->h1 = (hash_state_t **)malloc(sizeof(hash_state_t *)*brz->k); + brz->h2 = (hash_state_t **)malloc(sizeof(hash_state_t *)*brz->k); + brz->g = (cmph_uint8 **) calloc((size_t)brz->k, sizeof(cmph_uint8 *)); + DEBUGP("Reading c = %f k = %u algo = %u \n", brz->c, brz->k, brz->algo); + //loading h_i1, h_i2 and g_i. + for(i = 0; i < brz->k; i++) + { + // h1 + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state 1 has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + brz->h1[i] = hash_state_load(buf, buflen); + free(buf); + //h2 + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state 2 has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + brz->h2[i] = hash_state_load(buf, buflen); + free(buf); + switch(brz->algo) + { + case CMPH_FCH: + n = fch_calc_b(brz->c, brz->size[i]); + break; + case CMPH_BMZ8: + n = (cmph_uint32)ceil(brz->c * brz->size[i]); + break; + default: assert(0); + } + DEBUGP("g_i has %u bytes\n", n); + brz->g[i] = (cmph_uint8 *)calloc((size_t)n, sizeof(cmph_uint8)); + nbytes = fread(brz->g[i], sizeof(cmph_uint8)*n, (size_t)1, f); + } + //loading h0 + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + brz->h0 = hash_state_load(buf, buflen); + free(buf); + + //loading c, m, and the vector offset. + nbytes = fread(&(brz->m), sizeof(cmph_uint32), (size_t)1, f); + brz->offset = (cmph_uint32 *)malloc(sizeof(cmph_uint32)*brz->k); + nbytes = fread(brz->offset, sizeof(cmph_uint32)*(brz->k), (size_t)1, f); + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + } + + return; +} + +static cmph_uint32 brz_bmz8_search(brz_data_t *brz, const char *key, cmph_uint32 keylen, cmph_uint32 * fingerprint) +{ + register cmph_uint32 h0; + register cmph_uint32 m, n, h1, h2; + register cmph_uint8 mphf_bucket; + + hash_vector(brz->h0, key, keylen, fingerprint); + h0 = fingerprint[2] % brz->k; + + m = brz->size[h0]; + n = (cmph_uint32)ceil(brz->c * m); + h1 = hash(brz->h1[h0], key, keylen) % n; + h2 = hash(brz->h2[h0], key, keylen) % n; + + if (h1 == h2 && ++h2 >= n) h2 = 0; + mphf_bucket = (cmph_uint8)(brz->g[h0][h1] + brz->g[h0][h2]); + DEBUGP("key: %s h1: %u h2: %u h0: %u\n", key, h1, h2, h0); + DEBUGP("key: %s g[h1]: %u g[h2]: %u offset[h0]: %u edges: %u\n", key, brz->g[h0][h1], brz->g[h0][h2], brz->offset[h0], brz->m); + DEBUGP("Address: %u\n", mphf_bucket + brz->offset[h0]); + return (mphf_bucket + brz->offset[h0]); +} + +static cmph_uint32 brz_fch_search(brz_data_t *brz, const char *key, cmph_uint32 keylen, cmph_uint32 * fingerprint) +{ + register cmph_uint32 h0; + register cmph_uint32 m, b, h1, h2; + register double p1, p2; + register cmph_uint8 mphf_bucket; + + hash_vector(brz->h0, key, keylen, fingerprint); + h0 = fingerprint[2] % brz->k; + + m = brz->size[h0]; + b = fch_calc_b(brz->c, m); + p1 = fch_calc_p1(m); + p2 = fch_calc_p2(b); + h1 = hash(brz->h1[h0], key, keylen) % m; + h2 = hash(brz->h2[h0], key, keylen) % m; + mphf_bucket = 0; + h1 = mixh10h11h12(b, p1, p2, h1); + mphf_bucket = (cmph_uint8)((h2 + brz->g[h0][h1]) % m); + return (mphf_bucket + brz->offset[h0]); +} + +cmph_uint32 brz_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + brz_data_t *brz = mphf->data; + cmph_uint32 fingerprint[3]; + switch(brz->algo) + { + case CMPH_FCH: + return brz_fch_search(brz, key, keylen, fingerprint); + case CMPH_BMZ8: + return brz_bmz8_search(brz, key, keylen, fingerprint); + default: assert(0); + } + return 0; +} +void brz_destroy(cmph_t *mphf) +{ + cmph_uint32 i; + brz_data_t *data = (brz_data_t *)mphf->data; + if(data->g) + { + for(i = 0; i < data->k; i++) + { + free(data->g[i]); + hash_state_destroy(data->h1[i]); + hash_state_destroy(data->h2[i]); + } + free(data->g); + free(data->h1); + free(data->h2); + } + hash_state_destroy(data->h0); + free(data->size); + free(data->offset); + free(data); + free(mphf); +} + +/** \fn void brz_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void brz_pack(cmph_t *mphf, void *packed_mphf) +{ + brz_data_t *data = (brz_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + cmph_uint32 i,n; + CMPH_HASH h0_type, h1_type, h2_type; +#ifdef __brz_use_64bit__ + cmph_uint64 * g_is_ptr; +#else + cmph_uint32 * g_is_ptr; +#endif + cmph_uint8 * g_i; + + // packing internal algo type + memcpy(ptr, &(data->algo), sizeof(data->algo)); + ptr += sizeof(data->algo); + + // packing h0 type + h0_type = hash_get_type(data->h0); + memcpy(ptr, &h0_type, sizeof(h0_type)); + ptr += sizeof(h0_type); + + // packing h0 + hash_state_pack(data->h0, ptr); + ptr += hash_state_packed_size(h0_type); + + // packing k + memcpy(ptr, &(data->k), sizeof(data->k)); + ptr += sizeof(data->k); + + // packing c + *((cmph_uint64 *)ptr) = (cmph_uint64)data->c; + ptr += sizeof(data->c); + + // packing h1 type + h1_type = hash_get_type(data->h1[0]); + memcpy(ptr, &h1_type, sizeof(h1_type)); + ptr += sizeof(h1_type); + + // packing h2 type + h2_type = hash_get_type(data->h2[0]); + memcpy(ptr, &h2_type, sizeof(h2_type)); + ptr += sizeof(h2_type); + + // packing size + memcpy(ptr, data->size, sizeof(cmph_uint8)*data->k); + ptr += data->k; + + // packing offset + memcpy(ptr, data->offset, sizeof(cmph_uint32)*data->k); + ptr += sizeof(cmph_uint32)*data->k; + + #ifdef __brz_use_64bit__ + g_is_ptr = (cmph_uint64 *)ptr; + #else + g_is_ptr = (cmph_uint32 *)ptr; + #endif + + g_i = (cmph_uint8 *) (g_is_ptr + data->k); + + for(i = 0; i < data->k; i++) + { + #ifdef __brz_use_64bit__ + *g_is_ptr++ = (cmph_uint64)g_i; + #else + *g_is_ptr++ = (cmph_uint32)g_i; + #endif + // packing h1[i] + hash_state_pack(data->h1[i], g_i); + g_i += hash_state_packed_size(h1_type); + + // packing h2[i] + hash_state_pack(data->h2[i], g_i); + g_i += hash_state_packed_size(h2_type); + + // packing g_i + switch(data->algo) + { + case CMPH_FCH: + n = fch_calc_b(data->c, data->size[i]); + break; + case CMPH_BMZ8: + n = (cmph_uint32)ceil(data->c * data->size[i]); + break; + default: assert(0); + } + memcpy(g_i, data->g[i], sizeof(cmph_uint8)*n); + g_i += n; + + } + +} + +/** \fn cmph_uint32 brz_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 brz_packed_size(cmph_t *mphf) +{ + cmph_uint32 i; + cmph_uint32 size = 0; + brz_data_t *data = (brz_data_t *)mphf->data; + CMPH_HASH h0_type = hash_get_type(data->h0); + CMPH_HASH h1_type = hash_get_type(data->h1[0]); + CMPH_HASH h2_type = hash_get_type(data->h2[0]); + cmph_uint32 n; + size = (cmph_uint32)(2*sizeof(CMPH_ALGO) + 3*sizeof(CMPH_HASH) + hash_state_packed_size(h0_type) + sizeof(cmph_uint32) + + sizeof(double) + sizeof(cmph_uint8)*data->k + sizeof(cmph_uint32)*data->k); + // pointers to g_is + #ifdef __brz_use_64bit__ + size += (cmph_uint32) sizeof(cmph_uint64)*data->k; + #else + size += (cmph_uint32) sizeof(cmph_uint32)*data->k; + #endif + + size += hash_state_packed_size(h1_type) * data->k; + size += hash_state_packed_size(h2_type) * data->k; + + n = 0; + for(i = 0; i < data->k; i++) + { + switch(data->algo) + { + case CMPH_FCH: + n = fch_calc_b(data->c, data->size[i]); + break; + case CMPH_BMZ8: + n = (cmph_uint32)ceil(data->c * data->size[i]); + break; + default: assert(0); + } + size += n; + } + return size; +} + + + +static cmph_uint32 brz_bmz8_search_packed(cmph_uint32 *packed_mphf, const char *key, cmph_uint32 keylen, cmph_uint32 * fingerprint) +{ + register CMPH_HASH h0_type = *packed_mphf++; + register cmph_uint32 *h0_ptr = packed_mphf; + register cmph_uint32 k, h0, m, n, h1, h2; + register cmph_uint32 *offset; + register double c; + register CMPH_HASH h1_type, h2_type; + register cmph_uint8 * size; +#ifdef __brz_use_64bit__ + register cmph_uint64 * g_is_ptr; +#else + register cmph_uint32 * g_is_ptr; +#endif + register cmph_uint8 *h1_ptr, *h2_ptr, *g; + register cmph_uint8 mphf_bucket; + + packed_mphf = (cmph_uint32 *)(((cmph_uint8 *)packed_mphf) + hash_state_packed_size(h0_type)); + + k = *packed_mphf++; + + c = (double)(*((cmph_uint64*)packed_mphf)); + packed_mphf += 2; + + h1_type = *packed_mphf++; + + h2_type = *packed_mphf++; + + size = (cmph_uint8 *) packed_mphf; + packed_mphf = (cmph_uint32 *)(size + k); + + offset = packed_mphf; + packed_mphf += k; + + + hash_vector_packed(h0_ptr, h0_type, key, keylen, fingerprint); + h0 = fingerprint[2] % k; + + m = size[h0]; + n = (cmph_uint32)ceil(c * m); + + #ifdef __brz_use_64bit__ + g_is_ptr = (cmph_uint64 *)packed_mphf; + #else + g_is_ptr = packed_mphf; + #endif + + h1_ptr = (cmph_uint8 *) g_is_ptr[h0]; + + h2_ptr = h1_ptr + hash_state_packed_size(h1_type); + + g = h2_ptr + hash_state_packed_size(h2_type); + + h1 = hash_packed(h1_ptr, h1_type, key, keylen) % n; + h2 = hash_packed(h2_ptr, h2_type, key, keylen) % n; + + if (h1 == h2 && ++h2 >= n) h2 = 0; + mphf_bucket = (cmph_uint8)(g[h1] + g[h2]); + DEBUGP("key: %s h1: %u h2: %u h0: %u\n", key, h1, h2, h0); + DEBUGP("Address: %u\n", mphf_bucket + offset[h0]); + return (mphf_bucket + offset[h0]); +} + +static cmph_uint32 brz_fch_search_packed(cmph_uint32 *packed_mphf, const char *key, cmph_uint32 keylen, cmph_uint32 * fingerprint) +{ + register CMPH_HASH h0_type = *packed_mphf++; + + register cmph_uint32 *h0_ptr = packed_mphf; + register cmph_uint32 k, h0, m, b, h1, h2; + register double c, p1, p2; + register CMPH_HASH h1_type, h2_type; + register cmph_uint8 *size, *h1_ptr, *h2_ptr, *g; + register cmph_uint32 *offset; +#ifdef __brz_use_64bit__ + register cmph_uint64 * g_is_ptr; +#else + register cmph_uint32 * g_is_ptr; +#endif + register cmph_uint8 mphf_bucket; + + packed_mphf = (cmph_uint32 *)(((cmph_uint8 *)packed_mphf) + hash_state_packed_size(h0_type)); + + k = *packed_mphf++; + + c = (double)(*((cmph_uint64*)packed_mphf)); + packed_mphf += 2; + + h1_type = *packed_mphf++; + + h2_type = *packed_mphf++; + + size = (cmph_uint8 *) packed_mphf; + packed_mphf = (cmph_uint32 *)(size + k); + + offset = packed_mphf; + packed_mphf += k; + + hash_vector_packed(h0_ptr, h0_type, key, keylen, fingerprint); + h0 = fingerprint[2] % k; + + m = size[h0]; + b = fch_calc_b(c, m); + p1 = fch_calc_p1(m); + p2 = fch_calc_p2(b); + + #ifdef __brz_use_64bit__ + g_is_ptr = (cmph_uint64 *)packed_mphf; + #else + g_is_ptr = packed_mphf; + #endif + + h1_ptr = (cmph_uint8 *) g_is_ptr[h0]; + + h2_ptr = h1_ptr + hash_state_packed_size(h1_type); + + g = h2_ptr + hash_state_packed_size(h2_type); + + h1 = hash_packed(h1_ptr, h1_type, key, keylen) % m; + h2 = hash_packed(h2_ptr, h2_type, key, keylen) % m; + + mphf_bucket = 0; + h1 = mixh10h11h12(b, p1, p2, h1); + mphf_bucket = (cmph_uint8)((h2 + g[h1]) % m); + return (mphf_bucket + offset[h0]); +} + +/** cmph_uint32 brz_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 brz_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + register cmph_uint32 *ptr = (cmph_uint32 *)packed_mphf; + register CMPH_ALGO algo = *ptr++; + cmph_uint32 fingerprint[3]; + switch(algo) + { + case CMPH_FCH: + return brz_fch_search_packed(ptr, key, keylen, fingerprint); + case CMPH_BMZ8: + return brz_bmz8_search_packed(ptr, key, keylen, fingerprint); + default: + assert(0); + return 0; /* To avoid warnings that value must be returned */ + } +} + diff --git a/girepository/cmph/brz.h b/girepository/cmph/brz.h new file mode 100644 index 000000000..648f174b5 --- /dev/null +++ b/girepository/cmph/brz.h @@ -0,0 +1,47 @@ +#ifndef __CMPH_BRZ_H__ +#define __CMPH_BRZ_H__ + +#include "cmph.h" + +typedef struct __brz_data_t brz_data_t; +typedef struct __brz_config_data_t brz_config_data_t; + +brz_config_data_t *brz_config_new(void); +void brz_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void brz_config_set_tmp_dir(cmph_config_t *mph, cmph_uint8 *tmp_dir); +void brz_config_set_mphf_fd(cmph_config_t *mph, FILE *mphf_fd); +void brz_config_set_b(cmph_config_t *mph, cmph_uint32 b); +void brz_config_set_algo(cmph_config_t *mph, CMPH_ALGO algo); +void brz_config_set_memory_availability(cmph_config_t *mph, cmph_uint32 memory_availability); +void brz_config_destroy(cmph_config_t *mph); +cmph_t *brz_new(cmph_config_t *mph, double c); + +void brz_load(FILE *f, cmph_t *mphf); +int brz_dump(cmph_t *mphf, FILE *f); +void brz_destroy(cmph_t *mphf); +cmph_uint32 brz_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void brz_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void brz_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 brz_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 brz_packed_size(cmph_t *mphf); + +/** cmph_uint32 brz_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 brz_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/brz_structs.h b/girepository/cmph/brz_structs.h new file mode 100644 index 000000000..b781107f9 --- /dev/null +++ b/girepository/cmph/brz_structs.h @@ -0,0 +1,39 @@ +#ifndef __CMPH_BRZ_STRUCTS_H__ +#define __CMPH_BRZ_STRUCTS_H__ + +#include "hash_state.h" + +struct __brz_data_t +{ + CMPH_ALGO algo; // CMPH algo for generating the MPHFs for the buckets (Just CMPH_FCH and CMPH_BMZ8) + cmph_uint32 m; // edges (words) count + double c; // constant c + cmph_uint8 *size; // size[i] stores the number of edges represented by g[i][...]. + cmph_uint32 *offset; // offset[i] stores the sum: size[0] + size[1] + ... size[i-1]. + cmph_uint8 **g; // g function. + cmph_uint32 k; // number of components + hash_state_t **h1; + hash_state_t **h2; + hash_state_t * h0; +}; + +struct __brz_config_data_t +{ + CMPH_HASH hashfuncs[3]; + CMPH_ALGO algo; // CMPH algo for generating the MPHFs for the buckets (Just CMPH_FCH and CMPH_BMZ8) + double c; // constant c + cmph_uint32 m; // edges (words) count + cmph_uint8 *size; // size[i] stores the number of edges represented by g[i][...]. + cmph_uint32 *offset; // offset[i] stores the sum: size[0] + size[1] + ... size[i-1]. + cmph_uint8 **g; // g function. + cmph_uint8 b; // parameter b. + cmph_uint32 k; // number of components + hash_state_t **h1; + hash_state_t **h2; + hash_state_t * h0; + cmph_uint32 memory_availability; + cmph_uint8 * tmp_dir; // temporary directory + FILE * mphf_fd; // mphf file +}; + +#endif diff --git a/girepository/cmph/buffer_entry.c b/girepository/cmph/buffer_entry.c new file mode 100644 index 000000000..5dcc4d577 --- /dev/null +++ b/girepository/cmph/buffer_entry.c @@ -0,0 +1,103 @@ +#include "buffer_entry.h" +#include +#include +#include +#include + +struct __buffer_entry_t +{ + FILE *fd; + cmph_uint8 * buff; + cmph_uint32 capacity, // buffer entry capacity + nbytes, // buffer entry used bytes + pos; // current read position in buffer entry + cmph_uint8 eof; // flag to indicate end of file +}; + +buffer_entry_t * buffer_entry_new(cmph_uint32 capacity) +{ + buffer_entry_t *buff_entry = (buffer_entry_t *)malloc(sizeof(buffer_entry_t)); + assert(buff_entry); + buff_entry->fd = NULL; + buff_entry->buff = NULL; + buff_entry->capacity = capacity; + buff_entry->nbytes = capacity; + buff_entry->pos = capacity; + buff_entry->eof = 0; + return buff_entry; +} + +void buffer_entry_open(buffer_entry_t * buffer_entry, char * filename) +{ + buffer_entry->fd = fopen(filename, "rb"); +} + +void buffer_entry_set_capacity(buffer_entry_t * buffer_entry, cmph_uint32 capacity) +{ + buffer_entry->capacity = capacity; +} + + +cmph_uint32 buffer_entry_get_capacity(buffer_entry_t * buffer_entry) +{ + return buffer_entry->capacity; +} + +static void buffer_entry_load(buffer_entry_t * buffer_entry) +{ + free(buffer_entry->buff); + buffer_entry->buff = (cmph_uint8 *)calloc((size_t)buffer_entry->capacity, sizeof(cmph_uint8)); + buffer_entry->nbytes = (cmph_uint32)fread(buffer_entry->buff, (size_t)1, (size_t)buffer_entry->capacity, buffer_entry->fd); + if (buffer_entry->nbytes != buffer_entry->capacity) buffer_entry->eof = 1; + buffer_entry->pos = 0; +} + +cmph_uint8 * buffer_entry_read_key(buffer_entry_t * buffer_entry, cmph_uint32 * keylen) +{ + cmph_uint8 * buf = NULL; + cmph_uint32 lacked_bytes = sizeof(*keylen); + cmph_uint32 copied_bytes = 0; + if(buffer_entry->eof && (buffer_entry->pos == buffer_entry->nbytes)) // end + { + free(buf); + return NULL; + } + if((buffer_entry->pos + lacked_bytes) > buffer_entry->nbytes) + { + copied_bytes = buffer_entry->nbytes - buffer_entry->pos; + lacked_bytes = (buffer_entry->pos + lacked_bytes) - buffer_entry->nbytes; + if (copied_bytes != 0) memcpy(keylen, buffer_entry->buff + buffer_entry->pos, (size_t)copied_bytes); + buffer_entry_load(buffer_entry); + } + memcpy(keylen + copied_bytes, buffer_entry->buff + buffer_entry->pos, (size_t)lacked_bytes); + buffer_entry->pos += lacked_bytes; + + lacked_bytes = *keylen; + copied_bytes = 0; + buf = (cmph_uint8 *)malloc(*keylen + sizeof(*keylen)); + memcpy(buf, keylen, sizeof(*keylen)); + if((buffer_entry->pos + lacked_bytes) > buffer_entry->nbytes) { + copied_bytes = buffer_entry->nbytes - buffer_entry->pos; + lacked_bytes = (buffer_entry->pos + lacked_bytes) - buffer_entry->nbytes; + if (copied_bytes != 0) { + memcpy(buf + sizeof(*keylen), buffer_entry->buff + buffer_entry->pos, (size_t)copied_bytes); + } + buffer_entry_load(buffer_entry); + } + memcpy(buf+sizeof(*keylen)+copied_bytes, buffer_entry->buff + buffer_entry->pos, (size_t)lacked_bytes); + buffer_entry->pos += lacked_bytes; + return buf; +} + +void buffer_entry_destroy(buffer_entry_t * buffer_entry) +{ + fclose(buffer_entry->fd); + buffer_entry->fd = NULL; + free(buffer_entry->buff); + buffer_entry->buff = NULL; + buffer_entry->capacity = 0; + buffer_entry->nbytes = 0; + buffer_entry->pos = 0; + buffer_entry->eof = 0; + free(buffer_entry); +} diff --git a/girepository/cmph/buffer_entry.h b/girepository/cmph/buffer_entry.h new file mode 100644 index 000000000..62102baba --- /dev/null +++ b/girepository/cmph/buffer_entry.h @@ -0,0 +1,14 @@ +#ifndef __CMPH_BUFFER_ENTRY_H__ +#define __CMPH_BUFFER_ENTRY_H__ + +#include "cmph_types.h" +#include +typedef struct __buffer_entry_t buffer_entry_t; + +buffer_entry_t * buffer_entry_new(cmph_uint32 capacity); +void buffer_entry_set_capacity(buffer_entry_t * buffer_entry, cmph_uint32 capacity); +cmph_uint32 buffer_entry_get_capacity(buffer_entry_t * buffer_entry); +void buffer_entry_open(buffer_entry_t * buffer_entry, char * filename); +cmph_uint8 * buffer_entry_read_key(buffer_entry_t * buffer_entry, cmph_uint32 * keylen); +void buffer_entry_destroy(buffer_entry_t * buffer_entry); +#endif diff --git a/girepository/cmph/buffer_manage.c b/girepository/cmph/buffer_manage.c new file mode 100644 index 000000000..fdefc620d --- /dev/null +++ b/girepository/cmph/buffer_manage.c @@ -0,0 +1,66 @@ +#include "buffer_manage.h" +#include "buffer_entry.h" +#include +#include +#include +struct __buffer_manage_t +{ + cmph_uint32 memory_avail; // memory available + buffer_entry_t ** buffer_entries; // buffer entries to be managed + cmph_uint32 nentries; // number of entries to be managed + cmph_uint32 *memory_avail_list; // memory available list + int pos_avail_list; // current position in memory available list +}; + +buffer_manage_t * buffer_manage_new(cmph_uint32 memory_avail, cmph_uint32 nentries) +{ + cmph_uint32 memory_avail_entry, i; + buffer_manage_t *buff_manage = (buffer_manage_t *)malloc(sizeof(buffer_manage_t)); + assert(buff_manage); + buff_manage->memory_avail = memory_avail; + buff_manage->buffer_entries = (buffer_entry_t **)calloc((size_t)nentries, sizeof(buffer_entry_t *)); + buff_manage->memory_avail_list = (cmph_uint32 *)calloc((size_t)nentries, sizeof(cmph_uint32)); + buff_manage->pos_avail_list = -1; + buff_manage->nentries = nentries; + memory_avail_entry = buff_manage->memory_avail/buff_manage->nentries + 1; + for(i = 0; i < buff_manage->nentries; i++) + { + buff_manage->buffer_entries[i] = buffer_entry_new(memory_avail_entry); + } + return buff_manage; +} + +void buffer_manage_open(buffer_manage_t * buffer_manage, cmph_uint32 index, char * filename) +{ + buffer_entry_open(buffer_manage->buffer_entries[index], filename); +} + +cmph_uint8 * buffer_manage_read_key(buffer_manage_t * buffer_manage, cmph_uint32 index) +{ + cmph_uint8 * key = NULL; + if (buffer_manage->pos_avail_list >= 0 ) // recovering memory + { + cmph_uint32 new_capacity = buffer_entry_get_capacity(buffer_manage->buffer_entries[index]) + buffer_manage->memory_avail_list[(buffer_manage->pos_avail_list)--]; + buffer_entry_set_capacity(buffer_manage->buffer_entries[index], new_capacity); + //fprintf(stderr, "recovering memory\n"); + } + key = buffer_entry_read_key(buffer_manage->buffer_entries[index]); + if (key == NULL) // storing memory to be recovered + { + buffer_manage->memory_avail_list[++(buffer_manage->pos_avail_list)] = buffer_entry_get_capacity(buffer_manage->buffer_entries[index]); + //fprintf(stderr, "storing memory to be recovered\n"); + } + return key; +} + +void buffer_manage_destroy(buffer_manage_t * buffer_manage) +{ + cmph_uint32 i; + for(i = 0; i < buffer_manage->nentries; i++) + { + buffer_entry_destroy(buffer_manage->buffer_entries[i]); + } + free(buffer_manage->memory_avail_list); + free(buffer_manage->buffer_entries); + free(buffer_manage); +} diff --git a/girepository/cmph/buffer_manage.h b/girepository/cmph/buffer_manage.h new file mode 100644 index 000000000..8c66cffc1 --- /dev/null +++ b/girepository/cmph/buffer_manage.h @@ -0,0 +1,12 @@ +#ifndef __CMPH_BUFFER_MANAGE_H__ +#define __CMPH_BUFFER_MANAGE_H__ + +#include "cmph_types.h" +#include +typedef struct __buffer_manage_t buffer_manage_t; + +buffer_manage_t * buffer_manage_new(cmph_uint32 memory_avail, cmph_uint32 nentries); +void buffer_manage_open(buffer_manage_t * buffer_manage, cmph_uint32 index, char * filename); +cmph_uint8 * buffer_manage_read_key(buffer_manage_t * buffer_manage, cmph_uint32 index); +void buffer_manage_destroy(buffer_manage_t * buffer_manage); +#endif diff --git a/girepository/cmph/buffer_manager.c b/girepository/cmph/buffer_manager.c new file mode 100644 index 000000000..5a051e2f0 --- /dev/null +++ b/girepository/cmph/buffer_manager.c @@ -0,0 +1,64 @@ +#include "buffer_manager.h" +#include "buffer_entry.h" +#include +#include +#include +struct __buffer_manager_t +{ + cmph_uint32 memory_avail; // memory available + buffer_entry_t ** buffer_entries; // buffer entries to be managed + cmph_uint32 nentries; // number of entries to be managed + cmph_uint32 *memory_avail_list; // memory available list + int pos_avail_list; // current position in memory available list +}; + +buffer_manager_t * buffer_manager_new(cmph_uint32 memory_avail, cmph_uint32 nentries) +{ + cmph_uint32 memory_avail_entry, i; + buffer_manager_t *buff_manager = (buffer_manager_t *)malloc(sizeof(buffer_manager_t)); + assert(buff_manager); + buff_manager->memory_avail = memory_avail; + buff_manager->buffer_entries = (buffer_entry_t **)calloc((size_t)nentries, sizeof(buffer_entry_t *)); + buff_manager->memory_avail_list = (cmph_uint32 *)calloc((size_t)nentries, sizeof(cmph_uint32)); + buff_manager->pos_avail_list = -1; + buff_manager->nentries = nentries; + memory_avail_entry = buff_manager->memory_avail/buff_manager->nentries + 1; + for(i = 0; i < buff_manager->nentries; i++) + { + buff_manager->buffer_entries[i] = buffer_entry_new(memory_avail_entry); + } + return buff_manager; +} + +void buffer_manager_open(buffer_manager_t * buffer_manager, cmph_uint32 index, char * filename) +{ + buffer_entry_open(buffer_manager->buffer_entries[index], filename); +} + +cmph_uint8 * buffer_manager_read_key(buffer_manager_t * buffer_manager, cmph_uint32 index, cmph_uint32 * keylen) +{ + cmph_uint8 * key = NULL; + if (buffer_manager->pos_avail_list >= 0 ) // recovering memory + { + cmph_uint32 new_capacity = buffer_entry_get_capacity(buffer_manager->buffer_entries[index]) + buffer_manager->memory_avail_list[(buffer_manager->pos_avail_list)--]; + buffer_entry_set_capacity(buffer_manager->buffer_entries[index], new_capacity); + } + key = buffer_entry_read_key(buffer_manager->buffer_entries[index], keylen); + if (key == NULL) // storing memory to be recovered + { + buffer_manager->memory_avail_list[++(buffer_manager->pos_avail_list)] = buffer_entry_get_capacity(buffer_manager->buffer_entries[index]); + } + return key; +} + +void buffer_manager_destroy(buffer_manager_t * buffer_manager) +{ + cmph_uint32 i; + for(i = 0; i < buffer_manager->nentries; i++) + { + buffer_entry_destroy(buffer_manager->buffer_entries[i]); + } + free(buffer_manager->memory_avail_list); + free(buffer_manager->buffer_entries); + free(buffer_manager); +} diff --git a/girepository/cmph/buffer_manager.h b/girepository/cmph/buffer_manager.h new file mode 100644 index 000000000..af99c20f6 --- /dev/null +++ b/girepository/cmph/buffer_manager.h @@ -0,0 +1,12 @@ +#ifndef __CMPH_BUFFER_MANAGE_H__ +#define __CMPH_BUFFER_MANAGE_H__ + +#include "cmph_types.h" +#include +typedef struct __buffer_manager_t buffer_manager_t; + +buffer_manager_t * buffer_manager_new(cmph_uint32 memory_avail, cmph_uint32 nentries); +void buffer_manager_open(buffer_manager_t * buffer_manager, cmph_uint32 index, char * filename); +cmph_uint8 * buffer_manager_read_key(buffer_manager_t * buffer_manager, cmph_uint32 index, cmph_uint32 * keylen); +void buffer_manager_destroy(buffer_manager_t * buffer_manager); +#endif diff --git a/girepository/cmph/chd.c b/girepository/cmph/chd.c new file mode 100644 index 000000000..46aec52db --- /dev/null +++ b/girepository/cmph/chd.c @@ -0,0 +1,280 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmph_structs.h" +#include "chd_structs.h" +#include "chd.h" +#include "bitbool.h" +//#define DEBUG +#include "debug.h" + +chd_config_data_t *chd_config_new(cmph_config_t *mph) +{ + cmph_io_adapter_t *key_source = mph->key_source; + chd_config_data_t *chd; + chd = (chd_config_data_t *)malloc(sizeof(chd_config_data_t)); + assert(chd); + memset(chd, 0, sizeof(chd_config_data_t)); + + chd->chd_ph = cmph_config_new(key_source); + cmph_config_set_algo(chd->chd_ph, CMPH_CHD_PH); + + return chd; +} + +void chd_config_destroy(cmph_config_t *mph) +{ + chd_config_data_t *data = (chd_config_data_t *) mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + if(data->chd_ph) + { + cmph_config_destroy(data->chd_ph); + data->chd_ph = NULL; + } + free(data); +} + + +void chd_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + chd_config_data_t *data = (chd_config_data_t *) mph->data; + cmph_config_set_hashfuncs(data->chd_ph, hashfuncs); +} + + +void chd_config_set_b(cmph_config_t *mph, cmph_uint32 keys_per_bucket) +{ + chd_config_data_t *data = (chd_config_data_t *) mph->data; + cmph_config_set_b(data->chd_ph, keys_per_bucket); +} + + +void chd_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin) +{ + chd_config_data_t *data = (chd_config_data_t *) mph->data; + cmph_config_set_keys_per_bin(data->chd_ph, keys_per_bin); +} + + +cmph_t *chd_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + chd_data_t *chdf = NULL; + chd_config_data_t *chd = (chd_config_data_t *)mph->data; + chd_ph_config_data_t * chd_ph = (chd_ph_config_data_t *)chd->chd_ph->data; + compressed_rank_t cr; + + register cmph_t * chd_phf = NULL; + register cmph_uint32 packed_chd_phf_size = 0; + cmph_uint8 * packed_chd_phf = NULL; + + register cmph_uint32 packed_cr_size = 0; + cmph_uint8 * packed_cr = NULL; + + register cmph_uint32 i, idx, nkeys, nvals, nbins; + cmph_uint32 * vals_table = NULL; + register cmph_uint32 * occup_table = NULL; + #ifdef CMPH_TIMING + double construction_time_begin = 0.0; + double construction_time = 0.0; + ELAPSED_TIME_IN_SECONDS(&construction_time_begin); + #endif + + cmph_config_set_verbosity(chd->chd_ph, mph->verbosity); + cmph_config_set_graphsize(chd->chd_ph, c); + + if (mph->verbosity) + { + fprintf(stderr, "Generating a CHD_PH perfect hash function with a load factor equal to %.3f\n", c); + } + + chd_phf = cmph_new(chd->chd_ph); + + if(chd_phf == NULL) + { + return NULL; + } + + packed_chd_phf_size = cmph_packed_size(chd_phf); + DEBUGP("packed_chd_phf_size = %u\n", packed_chd_phf_size); + + /* Make sure that we have enough space to pack the mphf. */ + packed_chd_phf = calloc((size_t)packed_chd_phf_size,(size_t)1); + + /* Pack the mphf. */ + cmph_pack(chd_phf, packed_chd_phf); + + cmph_destroy(chd_phf); + + + if (mph->verbosity) + { + fprintf(stderr, "Compressing the range of the resulting CHD_PH perfect hash function\n"); + } + + compressed_rank_init(&cr); + nbins = chd_ph->n; + nkeys = chd_ph->m; + nvals = nbins - nkeys; + + vals_table = (cmph_uint32 *)calloc(nvals, sizeof(cmph_uint32)); + occup_table = (cmph_uint32 *)chd_ph->occup_table; + + for(i = 0, idx = 0; i < nbins; i++) + { + if(!GETBIT32(occup_table, i)) + { + vals_table[idx++] = i; + } + } + + compressed_rank_generate(&cr, vals_table, nvals); + free(vals_table); + + packed_cr_size = compressed_rank_packed_size(&cr); + packed_cr = (cmph_uint8 *) calloc(packed_cr_size, sizeof(cmph_uint8)); + compressed_rank_pack(&cr, packed_cr); + compressed_rank_destroy(&cr); + + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + chdf = (chd_data_t *)malloc(sizeof(chd_data_t)); + + chdf->packed_cr = packed_cr; + packed_cr = NULL; //transfer memory ownership + + chdf->packed_chd_phf = packed_chd_phf; + packed_chd_phf = NULL; //transfer memory ownership + + chdf->packed_chd_phf_size = packed_chd_phf_size; + chdf->packed_cr_size = packed_cr_size; + + mphf->data = chdf; + mphf->size = nkeys; + + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + #ifdef CMPH_TIMING + ELAPSED_TIME_IN_SECONDS(&construction_time); + register cmph_uint32 space_usage = chd_packed_size(mphf)*8; + construction_time = construction_time - construction_time_begin; + fprintf(stdout, "%u\t%.2f\t%u\t%.4f\t%.4f\n", nkeys, c, chd_ph->keys_per_bucket, construction_time, space_usage/(double)nkeys); + #endif + + return mphf; +} + +void chd_load(FILE *fd, cmph_t *mphf) +{ + register size_t nbytes; + chd_data_t *chd = (chd_data_t *)malloc(sizeof(chd_data_t)); + + DEBUGP("Loading chd mphf\n"); + mphf->data = chd; + + nbytes = fread(&chd->packed_chd_phf_size, sizeof(cmph_uint32), (size_t)1, fd); + DEBUGP("Loading CHD_PH perfect hash function with %u bytes to disk\n", chd->packed_chd_phf_size); + chd->packed_chd_phf = (cmph_uint8 *) calloc((size_t)chd->packed_chd_phf_size,(size_t)1); + nbytes = fread(chd->packed_chd_phf, chd->packed_chd_phf_size, (size_t)1, fd); + + nbytes = fread(&chd->packed_cr_size, sizeof(cmph_uint32), (size_t)1, fd); + DEBUGP("Loading Compressed rank structure, which has %u bytes\n", chd->packed_cr_size); + chd->packed_cr = (cmph_uint8 *) calloc((size_t)chd->packed_cr_size, (size_t)1); + nbytes = fread(chd->packed_cr, chd->packed_cr_size, (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + } + +} + +int chd_dump(cmph_t *mphf, FILE *fd) +{ + register size_t nbytes; + chd_data_t *data = (chd_data_t *)mphf->data; + + __cmph_dump(mphf, fd); + // Dumping CHD_PH perfect hash function + + DEBUGP("Dumping CHD_PH perfect hash function with %u bytes to disk\n", data->packed_chd_phf_size); + nbytes = fwrite(&data->packed_chd_phf_size, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(data->packed_chd_phf, data->packed_chd_phf_size, (size_t)1, fd); + + DEBUGP("Dumping compressed rank structure with %u bytes to disk\n", data->packed_cr_size); + nbytes = fwrite(&data->packed_cr_size, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(data->packed_cr, data->packed_cr_size, (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + + return 1; +} + +void chd_destroy(cmph_t *mphf) +{ + chd_data_t *data = (chd_data_t *)mphf->data; + free(data->packed_chd_phf); + free(data->packed_cr); + free(data); + free(mphf); +} + +static inline cmph_uint32 _chd_search(void * packed_chd_phf, void * packed_cr, const char *key, cmph_uint32 keylen) +{ + register cmph_uint32 bin_idx = cmph_search_packed(packed_chd_phf, key, keylen); + register cmph_uint32 rank = compressed_rank_query_packed(packed_cr, bin_idx); + return bin_idx - rank; +} + +cmph_uint32 chd_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + register chd_data_t * chd = mphf->data; + return _chd_search(chd->packed_chd_phf, chd->packed_cr, key, keylen); +} + +void chd_pack(cmph_t *mphf, void *packed_mphf) +{ + chd_data_t *data = (chd_data_t *)mphf->data; + cmph_uint32 * ptr = packed_mphf; + cmph_uint8 * ptr8; + + // packing packed_cr_size and packed_cr + *ptr = data->packed_cr_size; + ptr8 = (cmph_uint8 *) (ptr + 1); + + memcpy(ptr8, data->packed_cr, data->packed_cr_size); + ptr8 += data->packed_cr_size; + + ptr = (cmph_uint32 *) ptr8; + *ptr = data->packed_chd_phf_size; + + ptr8 = (cmph_uint8 *) (ptr + 1); + memcpy(ptr8, data->packed_chd_phf, data->packed_chd_phf_size); +} + +cmph_uint32 chd_packed_size(cmph_t *mphf) +{ + register chd_data_t *data = (chd_data_t *)mphf->data; + return (cmph_uint32)(sizeof(CMPH_ALGO) + 2*sizeof(cmph_uint32) + data->packed_cr_size + data->packed_chd_phf_size); + +} + +cmph_uint32 chd_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + + register cmph_uint32 * ptr = packed_mphf; + register cmph_uint32 packed_cr_size = *ptr++; + register cmph_uint8 * packed_chd_phf = ((cmph_uint8 *) ptr) + packed_cr_size + sizeof(cmph_uint32); + return _chd_search(packed_chd_phf, ptr, key, keylen); +} + + diff --git a/girepository/cmph/chd.h b/girepository/cmph/chd.h new file mode 100644 index 000000000..e829df816 --- /dev/null +++ b/girepository/cmph/chd.h @@ -0,0 +1,59 @@ +#ifndef _CMPH_CHD_H__ +#define _CMPH_CHD_H__ + +#include "cmph.h" + +typedef struct __chd_data_t chd_data_t; +typedef struct __chd_config_data_t chd_config_data_t; + +/* Config API */ +chd_config_data_t *chd_config_new(cmph_config_t * mph); +void chd_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); + +/** \fn void chd_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin); + * \brief Allows to set the number of keys per bin. + * \param mph pointer to the configuration structure + * \param keys_per_bin value for the number of keys per bin + */ +void chd_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin); + +/** \fn void chd_config_set_b(cmph_config_t *mph, cmph_uint32 keys_per_bucket); + * \brief Allows to set the number of keys per bucket. + * \param mph pointer to the configuration structure + * \param keys_per_bucket value for the number of keys per bucket + */ +void chd_config_set_b(cmph_config_t *mph, cmph_uint32 keys_per_bucket); +void chd_config_destroy(cmph_config_t *mph); + + +/* Chd algorithm API */ +cmph_t *chd_new(cmph_config_t *mph, double c); +void chd_load(FILE *fd, cmph_t *mphf); +int chd_dump(cmph_t *mphf, FILE *fd); +void chd_destroy(cmph_t *mphf); +cmph_uint32 chd_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void chd_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void chd_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 chd_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 chd_packed_size(cmph_t *mphf); + +/** cmph_uint32 chd_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 chd_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/chd_ph.c b/girepository/cmph/chd_ph.c new file mode 100644 index 000000000..ae986b425 --- /dev/null +++ b/girepository/cmph/chd_ph.c @@ -0,0 +1,1001 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmph_structs.h" +#include "chd_structs_ph.h" +#include "chd_ph.h" +#include"miller_rabin.h" +#include"bitbool.h" + + +//#define DEBUG +#include "debug.h" + +// NO_ELEMENT is equivalent to null pointer +#ifndef NO_ELEMENT +#define NO_ELEMENT UINT_MAX +#endif + +// struct used to represent items at mapping, ordering and searching phases +struct _chd_ph_item_t +{ + cmph_uint32 f; + cmph_uint32 h; +}; +typedef struct _chd_ph_item_t chd_ph_item_t; + +// struct to represent the items at mapping phase only. +struct _chd_ph_map_item_t +{ + cmph_uint32 f; + cmph_uint32 h; + cmph_uint32 bucket_num; +}; +typedef struct _chd_ph_map_item_t chd_ph_map_item_t; + +// struct to represent a bucket +struct _chd_ph_bucket_t +{ + cmph_uint32 items_list; // offset + union + { + cmph_uint32 size; + cmph_uint32 bucket_id; + }; +}; + +typedef struct _chd_ph_bucket_t chd_ph_bucket_t; + +struct _chd_ph_sorted_list_t +{ + cmph_uint32 buckets_list; + cmph_uint32 size; +}; + +typedef struct _chd_ph_sorted_list_t chd_ph_sorted_list_t; + + +static inline chd_ph_bucket_t * chd_ph_bucket_new(cmph_uint32 nbuckets); +static inline void chd_ph_bucket_clean(chd_ph_bucket_t * buckets, cmph_uint32 nbuckets); +static inline void chd_ph_bucket_destroy(chd_ph_bucket_t * buckets); + +chd_ph_bucket_t * chd_ph_bucket_new(cmph_uint32 nbuckets) +{ + chd_ph_bucket_t * buckets = (chd_ph_bucket_t *) calloc(nbuckets, sizeof(chd_ph_bucket_t)); + return buckets; +} + +void chd_ph_bucket_clean(chd_ph_bucket_t * buckets, cmph_uint32 nbuckets) +{ + register cmph_uint32 i = 0; + assert(buckets); + for(i = 0; i < nbuckets; i++) + buckets[i].size = 0; +} +static cmph_uint8 chd_ph_bucket_insert(chd_ph_bucket_t * buckets,chd_ph_map_item_t * map_items, chd_ph_item_t * items, + cmph_uint32 nbuckets,cmph_uint32 item_idx) +{ + register cmph_uint32 i = 0; + register chd_ph_item_t * tmp_item; + register chd_ph_map_item_t * tmp_map_item = map_items + item_idx; + register chd_ph_bucket_t * bucket = buckets + tmp_map_item->bucket_num; + tmp_item = items + bucket->items_list; + + for(i = 0; i < bucket->size; i++) + { + if(tmp_item->f == tmp_map_item->f && tmp_item->h == tmp_map_item->h) + { + DEBUGP("Item not added\n"); + return 0; + }; + tmp_item++; + }; + tmp_item->f = tmp_map_item->f; + tmp_item->h = tmp_map_item->h; + bucket->size++; + return 1; +}; +void chd_ph_bucket_destroy(chd_ph_bucket_t * buckets) +{ + free(buckets); +} + +static inline cmph_uint8 chd_ph_mapping(cmph_config_t *mph, chd_ph_bucket_t * buckets, chd_ph_item_t * items, + cmph_uint32 *max_bucket_size); + +static chd_ph_sorted_list_t * chd_ph_ordering(chd_ph_bucket_t ** _buckets,chd_ph_item_t ** items, + cmph_uint32 nbuckets,cmph_uint32 nitems, cmph_uint32 max_bucket_size); + +static cmph_uint8 chd_ph_searching(chd_ph_config_data_t *chd_ph, chd_ph_bucket_t *buckets, chd_ph_item_t *items , + cmph_uint32 max_bucket_size, chd_ph_sorted_list_t *sorted_lists, cmph_uint32 max_probes, cmph_uint32 * disp_table); + +static inline double chd_ph_space_lower_bound(cmph_uint32 _n, cmph_uint32 _r) +{ + double r = _r, n = _n; + return (1 + (r/n - 1.0 + 1.0/(2.0*n))*log(1 - n/r))/log(2); +}; + +/* computes the entropy of non empty buckets.*/ +static inline double chd_ph_get_entropy(cmph_uint32 * disp_table, cmph_uint32 n, cmph_uint32 max_probes) +{ + register cmph_uint32 * probe_counts = (cmph_uint32 *) calloc(max_probes, sizeof(cmph_uint32)); + register cmph_uint32 i; + register double entropy = 0; + + for(i = 0; i < n; i++) + { + probe_counts[disp_table[i]]++; + }; + + for(i = 0; i < max_probes; i++) + { + if(probe_counts[i] > 0) + entropy -= probe_counts[i]*log((double)probe_counts[i]/(double)n)/log(2); + }; + free(probe_counts); + return entropy; +}; + +chd_ph_config_data_t *chd_ph_config_new(void) +{ + chd_ph_config_data_t *chd_ph; + chd_ph = (chd_ph_config_data_t *)malloc(sizeof(chd_ph_config_data_t)); + assert(chd_ph); + memset(chd_ph, 0, sizeof(chd_ph_config_data_t)); + + chd_ph->hashfunc = CMPH_HASH_JENKINS; + chd_ph->cs = NULL; + chd_ph->nbuckets = 0; + chd_ph->n = 0; + chd_ph->hl = NULL; + + chd_ph->m = 0; + chd_ph->use_h = 1; + chd_ph->keys_per_bin = 1; + chd_ph->keys_per_bucket = 4; + chd_ph->occup_table = 0; + + return chd_ph; +} + +void chd_ph_config_destroy(cmph_config_t *mph) +{ + chd_ph_config_data_t *data = (chd_ph_config_data_t *) mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + if(data->occup_table) + { + free(data->occup_table); + data->occup_table = NULL; + } + free(data); +} + + +void chd_ph_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + chd_ph_config_data_t *chd_ph = (chd_ph_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 1) break; //chd_ph only uses one linear hash function + chd_ph->hashfunc = *hashptr; + ++i, ++hashptr; + } +} + + +void chd_ph_config_set_b(cmph_config_t *mph, cmph_uint32 keys_per_bucket) +{ + chd_ph_config_data_t *chd_ph; + assert(mph); + chd_ph = (chd_ph_config_data_t *)mph->data; + if(keys_per_bucket < 1 || keys_per_bucket >= 15) + { + keys_per_bucket = 4; + } + chd_ph->keys_per_bucket = keys_per_bucket; +} + + +void chd_ph_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin) +{ + chd_ph_config_data_t *chd_ph; + assert(mph); + chd_ph = (chd_ph_config_data_t *)mph->data; + if(keys_per_bin <= 1 || keys_per_bin >= 128) + { + keys_per_bin = 1; + } + chd_ph->keys_per_bin = keys_per_bin; +} + +cmph_uint8 chd_ph_mapping(cmph_config_t *mph, chd_ph_bucket_t * buckets, chd_ph_item_t * items, cmph_uint32 *max_bucket_size) +{ + register cmph_uint32 i = 0, g = 0; + cmph_uint32 hl[3]; + chd_ph_config_data_t *chd_ph = (chd_ph_config_data_t *)mph->data; + char * key = NULL; + cmph_uint32 keylen = 0; + chd_ph_map_item_t * map_item; + chd_ph_map_item_t * map_items = malloc(chd_ph->m*sizeof(chd_ph_map_item_t)); + register cmph_uint32 mapping_iterations = 1000; + *max_bucket_size = 0; + while(1) + { + mapping_iterations--; + if (chd_ph->hl) hash_state_destroy(chd_ph->hl); + chd_ph->hl = hash_state_new(chd_ph->hashfunc, chd_ph->m); + + chd_ph_bucket_clean(buckets, chd_ph->nbuckets); + + mph->key_source->rewind(mph->key_source->data); + + for(i = 0; i < chd_ph->m; i++) + { + mph->key_source->read(mph->key_source->data, &key, &keylen); + hash_vector(chd_ph->hl, key, keylen, hl); + + map_item = (map_items + i); + + g = hl[0] % chd_ph->nbuckets; + map_item->f = hl[1] % chd_ph->n; + map_item->h = hl[2] % (chd_ph->n - 1) + 1; + map_item->bucket_num=g; + mph->key_source->dispose(mph->key_source->data, key, keylen); +// if(buckets[g].size == (chd_ph->keys_per_bucket << 2)) +// { +// DEBUGP("BUCKET = %u -- SIZE = %u -- MAXIMUM SIZE = %u\n", g, buckets[g].size, (chd_ph->keys_per_bucket << 2)); +// goto error; +// } + buckets[g].size++; + if(buckets[g].size > *max_bucket_size) + { + *max_bucket_size = buckets[g].size; + } + } + buckets[0].items_list = 0; + for(i = 1; i < chd_ph->nbuckets; i++) + { + buckets[i].items_list = buckets[i-1].items_list + buckets[i - 1].size; + buckets[i - 1].size = 0; + }; + buckets[i - 1].size = 0; + for(i = 0; i < chd_ph->m; i++) + { + map_item = (map_items + i); + if(!chd_ph_bucket_insert(buckets, map_items, items, chd_ph->nbuckets, i)) + break; + } + if(i == chd_ph->m) + { + free(map_items); + return 1; // SUCCESS + } + + if(mapping_iterations == 0) + { + goto error; + } + } +error: + free(map_items); + hash_state_destroy(chd_ph->hl); + chd_ph->hl = NULL; + return 0; // FAILURE +} + +chd_ph_sorted_list_t * chd_ph_ordering(chd_ph_bucket_t ** _buckets, chd_ph_item_t ** _items, + cmph_uint32 nbuckets, cmph_uint32 nitems, cmph_uint32 max_bucket_size) +{ + chd_ph_sorted_list_t * sorted_lists = (chd_ph_sorted_list_t *) calloc(max_bucket_size + 1, sizeof(chd_ph_sorted_list_t)); + + chd_ph_bucket_t * input_buckets = (*_buckets); + chd_ph_bucket_t * output_buckets; + chd_ph_item_t * input_items = (*_items); + chd_ph_item_t * output_items; + register cmph_uint32 i, j, bucket_size, position, position2; +// cmph_uint32 non_empty_buckets; + DEBUGP("MAX BUCKET SIZE = %u\n", max_bucket_size); + // Determine size of each list of buckets + for(i = 0; i < nbuckets; i++) + { + bucket_size = input_buckets[i].size; + if(bucket_size == 0) + continue; + sorted_lists[bucket_size].size++; + }; + sorted_lists[1].buckets_list = 0; + // Determine final position of list of buckets into the contiguous array that will store all the buckets + for(i = 2; i <= max_bucket_size; i++) + { + sorted_lists[i].buckets_list = sorted_lists[i-1].buckets_list + sorted_lists[i-1].size; + sorted_lists[i-1].size = 0; + }; + sorted_lists[i-1].size = 0; + // Store the buckets in a new array which is sorted by bucket sizes + output_buckets = calloc(nbuckets, sizeof(chd_ph_bucket_t)); // everything is initialized with zero +// non_empty_buckets = nbuckets; + + for(i = 0; i < nbuckets; i++) + { + bucket_size = input_buckets[i].size; + if(bucket_size == 0) + { +// non_empty_buckets--; + continue; + }; + position = sorted_lists[bucket_size].buckets_list + sorted_lists[bucket_size].size; + output_buckets[position].bucket_id = i; + output_buckets[position].items_list = input_buckets[i].items_list; + sorted_lists[bucket_size].size++; + }; +/* for(i = non_empty_buckets; i < nbuckets; i++) + output_buckets[i].size=0;*/ + // Return the buckets sorted in new order and free the old buckets sorted in old order + free(input_buckets); + (*_buckets) = output_buckets; + + + // Store the items according to the new order of buckets. + output_items = (chd_ph_item_t*)calloc(nitems, sizeof(chd_ph_item_t)); + position = 0; + i = 0; + for(bucket_size = 1; bucket_size <= max_bucket_size; bucket_size++) + { + for(i = sorted_lists[bucket_size].buckets_list; i < sorted_lists[bucket_size].size + sorted_lists[bucket_size].buckets_list; i++) + { + position2 = output_buckets[i].items_list; + output_buckets[i].items_list = position; + for(j = 0; j < bucket_size; j++) + { + output_items[position].f = input_items[position2].f; + output_items[position].h = input_items[position2].h; + position++; + position2++; + }; + }; + }; + //Return the items sorted in new order and free the old items sorted in old order + free(input_items); + (*_items) = output_items; + return sorted_lists; +}; + +static inline cmph_uint8 place_bucket_probe(chd_ph_config_data_t *chd_ph, chd_ph_bucket_t *buckets, + chd_ph_item_t *items, cmph_uint32 probe0_num, cmph_uint32 probe1_num, + cmph_uint32 bucket_num, cmph_uint32 size) +{ + register cmph_uint32 i; + register chd_ph_item_t * item; + register cmph_uint32 position; + + item = items + buckets[bucket_num].items_list; + // try place bucket with probe_num + if(chd_ph->keys_per_bin > 1) + { + for(i = 0; i < size; i++) // placement + { + position = (cmph_uint32)((item->f + ((cmph_uint64)item->h)*probe0_num + probe1_num) % chd_ph->n); + if(chd_ph->occup_table[position] >= chd_ph->keys_per_bin) + { + break; + } + (chd_ph->occup_table[position])++; + item++; + }; + } else + { + for(i = 0; i < size; i++) // placement + { + position = (cmph_uint32)((item->f + ((cmph_uint64)item->h)*probe0_num + probe1_num) % chd_ph->n); + if(GETBIT32(((cmph_uint32 *)chd_ph->occup_table), position)) + { + break; + } + SETBIT32(((cmph_uint32*)chd_ph->occup_table), position); + item++; + }; + }; + if(i != size) // Undo the placement + { + item = items + buckets[bucket_num].items_list; + if(chd_ph->keys_per_bin > 1) + { + while(1) + { + if(i == 0) + { + break; + } + position = (cmph_uint32)((item->f + ((cmph_uint64 )item->h) * probe0_num + probe1_num) % chd_ph->n); + (chd_ph->occup_table[position])--; + item++; + i--; + }; + } else + { + while(1) + { + if(i == 0) + { + break; + } + position = (cmph_uint32)((item->f + ((cmph_uint64 )item->h) * probe0_num + probe1_num) % chd_ph->n); + UNSETBIT32(((cmph_uint32*)chd_ph->occup_table), position); + +// ([position/32]^=(1<<(position%32)); + item++; + i--; + }; + }; + return 0; + } + return 1; +}; + +static inline cmph_uint8 place_bucket(chd_ph_config_data_t *chd_ph, chd_ph_bucket_t *buckets, chd_ph_item_t * items, cmph_uint32 max_probes, + cmph_uint32 * disp_table, cmph_uint32 bucket_num, cmph_uint32 size) + +{ + register cmph_uint32 probe0_num, probe1_num, probe_num; + probe0_num = 0; + probe1_num = 0; + probe_num = 0; + + while(1) + { + if(place_bucket_probe(chd_ph, buckets, items, probe0_num, probe1_num, bucket_num,size)) + { + disp_table[buckets[bucket_num].bucket_id] = probe0_num + probe1_num * chd_ph->n; + return 1; + } + probe0_num++; + if(probe0_num >= chd_ph->n) + { + probe0_num -= chd_ph->n; + probe1_num++; + }; + probe_num++; + if(probe_num >= max_probes || probe1_num >= chd_ph->n) + { + return 0; + }; + }; + return 0; +}; + +static inline cmph_uint8 place_buckets1(chd_ph_config_data_t *chd_ph, chd_ph_bucket_t * buckets, chd_ph_item_t *items, + cmph_uint32 max_bucket_size, chd_ph_sorted_list_t *sorted_lists, cmph_uint32 max_probes, + cmph_uint32 * disp_table) +{ + register cmph_uint32 i = 0; + register cmph_uint32 curr_bucket = 0; + + for(i = max_bucket_size; i > 0; i--) + { + curr_bucket = sorted_lists[i].buckets_list; + while(curr_bucket < sorted_lists[i].size + sorted_lists[i].buckets_list) + { + if(!place_bucket(chd_ph, buckets, items, max_probes, disp_table, curr_bucket, i)) + { + return 0; + } + curr_bucket++; + }; + }; + return 1; +}; + +static inline cmph_uint8 place_buckets2(chd_ph_config_data_t *chd_ph, chd_ph_bucket_t *buckets, chd_ph_item_t * items, + cmph_uint32 max_bucket_size, chd_ph_sorted_list_t *sorted_lists, cmph_uint32 max_probes, + cmph_uint32 * disp_table) +{ + register cmph_uint32 i,j, non_placed_bucket; + register cmph_uint32 curr_bucket; + register cmph_uint32 probe_num, probe0_num, probe1_num; + cmph_uint32 sorted_list_size; +#ifdef DEBUG + cmph_uint32 items_list; + cmph_uint32 bucket_id; +#endif + DEBUGP("USING HEURISTIC TO PLACE BUCKETS\n"); + for(i = max_bucket_size; i > 0; i--) + { + probe_num = 0; + probe0_num = 0; + probe1_num = 0; + sorted_list_size = sorted_lists[i].size; + while(sorted_lists[i].size != 0) + { + curr_bucket = sorted_lists[i].buckets_list; + for(j = 0, non_placed_bucket = 0; j < sorted_lists[i].size; j++) + { + // if bucket is successfully placed remove it from list + if(place_bucket_probe(chd_ph, buckets, items, probe0_num, probe1_num, curr_bucket, i)) + { + disp_table[buckets[curr_bucket].bucket_id] = probe0_num + probe1_num * chd_ph->n; +// DEBUGP("BUCKET %u PLACED --- DISPLACEMENT = %u\n", curr_bucket, disp_table[curr_bucket]); + } + else + { +// DEBUGP("BUCKET %u NOT PLACED\n", curr_bucket); +#ifdef DEBUG + items_list = buckets[non_placed_bucket + sorted_lists[i].buckets_list].items_list; + bucket_id = buckets[non_placed_bucket + sorted_lists[i].buckets_list].bucket_id; +#endif + buckets[non_placed_bucket + sorted_lists[i].buckets_list].items_list = buckets[curr_bucket].items_list; + buckets[non_placed_bucket + sorted_lists[i].buckets_list].bucket_id = buckets[curr_bucket].bucket_id; +#ifdef DEBUG + buckets[curr_bucket].items_list=items_list; + buckets[curr_bucket].bucket_id=bucket_id; +#endif + non_placed_bucket++; + } + curr_bucket++; + }; + sorted_lists[i].size = non_placed_bucket; + probe0_num++; + if(probe0_num >= chd_ph->n) + { + probe0_num -= chd_ph->n; + probe1_num++; + }; + probe_num++; + if(probe_num >= max_probes || probe1_num >= chd_ph->n) + { + sorted_lists[i].size = sorted_list_size; + return 0; + }; + }; + sorted_lists[i].size = sorted_list_size; + }; + return 1; +}; + +cmph_uint8 chd_ph_searching(chd_ph_config_data_t *chd_ph, chd_ph_bucket_t *buckets, chd_ph_item_t *items , + cmph_uint32 max_bucket_size, chd_ph_sorted_list_t *sorted_lists, cmph_uint32 max_probes, + cmph_uint32 * disp_table) +{ + if(chd_ph->use_h) + { + return place_buckets2(chd_ph, buckets, items, max_bucket_size, sorted_lists, max_probes, disp_table); + } + else + { + return place_buckets1(chd_ph, buckets, items, max_bucket_size, sorted_lists, max_probes, disp_table); + } + +} + +static inline cmph_uint8 chd_ph_check_bin_hashing(chd_ph_config_data_t *chd_ph, chd_ph_bucket_t *buckets, chd_ph_item_t *items, + cmph_uint32 * disp_table, chd_ph_sorted_list_t * sorted_lists,cmph_uint32 max_bucket_size) +{ + register cmph_uint32 bucket_size, i, j; + register cmph_uint32 position, probe0_num, probe1_num; + G_GNUC_UNUSED register cmph_uint32 m = 0; + register chd_ph_item_t * item; + if(chd_ph->keys_per_bin > 1) + memset(chd_ph->occup_table, 0, chd_ph->n); + else + memset(chd_ph->occup_table, 0, ((chd_ph->n + 31)/32) * sizeof(cmph_uint32)); + + for(bucket_size = 1; bucket_size <= max_bucket_size; bucket_size++) + for(i = sorted_lists[bucket_size].buckets_list; i < sorted_lists[bucket_size].size + + sorted_lists[bucket_size].buckets_list; i++) + { + j = bucket_size; + item = items + buckets[i].items_list; + probe0_num = disp_table[buckets[i].bucket_id] % chd_ph->n; + probe1_num = disp_table[buckets[i].bucket_id] / chd_ph->n; + for(; j > 0; j--) + { +#ifdef DEBUG + m++; +#endif + position = (cmph_uint32)((item->f + ((cmph_uint64 )item->h) * probe0_num + probe1_num) % chd_ph->n); + if(chd_ph->keys_per_bin > 1) + { + if(chd_ph->occup_table[position] >= chd_ph->keys_per_bin) + { + return 0; + } + (chd_ph->occup_table[position])++; + } + else + { + if(GETBIT32(((cmph_uint32*)chd_ph->occup_table), position)) + { + return 0; + } + SETBIT32(((cmph_uint32*)chd_ph->occup_table), position); + }; + item++; + }; + }; + DEBUGP("We were able to place m = %u keys\n", m); + return 1; +}; + + +cmph_t *chd_ph_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + chd_ph_data_t *chd_phf = NULL; + chd_ph_config_data_t *chd_ph = (chd_ph_config_data_t *)mph->data; + + register double load_factor = c; + register cmph_uint8 searching_success = 0; + register cmph_uint32 max_probes = 1 << 20; // default value for max_probes + register cmph_uint32 iterations = 100; + chd_ph_bucket_t * buckets = NULL; + chd_ph_item_t * items = NULL; + register cmph_uint8 failure = 0; + cmph_uint32 max_bucket_size = 0; + chd_ph_sorted_list_t * sorted_lists = NULL; + cmph_uint32 * disp_table = NULL; + register double space_lower_bound = 0; + #ifdef CMPH_TIMING + double construction_time_begin = 0.0; + double construction_time = 0.0; + ELAPSED_TIME_IN_SECONDS(&construction_time_begin); + #endif + + + chd_ph->m = mph->key_source->nkeys; + DEBUGP("m = %u\n", chd_ph->m); + + chd_ph->nbuckets = (cmph_uint32)(chd_ph->m/chd_ph->keys_per_bucket) + 1; + DEBUGP("nbuckets = %u\n", chd_ph->nbuckets); + + if(load_factor < 0.5 ) + { + load_factor = 0.5; + } + + if(load_factor >= 0.99) + { + load_factor = 0.99; + } + + DEBUGP("load_factor = %.3f\n", load_factor); + + chd_ph->n = (cmph_uint32)(chd_ph->m/(chd_ph->keys_per_bin * load_factor)) + 1; + + //Round the number of bins to the prime immediately above + if(chd_ph->n % 2 == 0) chd_ph->n++; + for(;;) + { + if(check_primality(chd_ph->n) == 1) + break; + chd_ph->n += 2; // just odd numbers can be primes for n > 2 + + }; + + DEBUGP("n = %u \n", chd_ph->n); + if(chd_ph->keys_per_bin == 1) + { + space_lower_bound = chd_ph_space_lower_bound(chd_ph->m, chd_ph->n); + } + + if(mph->verbosity) + { + fprintf(stderr, "space lower bound is %.3f bits per key\n", space_lower_bound); + } + + // We allocate the working tables + buckets = chd_ph_bucket_new(chd_ph->nbuckets); + items = (chd_ph_item_t *) calloc(chd_ph->m, sizeof(chd_ph_item_t)); + + max_probes = (cmph_uint32)(((log(chd_ph->m)/log(2))/20) * max_probes); + + if(chd_ph->keys_per_bin == 1) + chd_ph->occup_table = (cmph_uint8 *) calloc(((chd_ph->n + 31)/32), sizeof(cmph_uint32)); + else + chd_ph->occup_table = (cmph_uint8 *) calloc(chd_ph->n, sizeof(cmph_uint8)); + + disp_table = (cmph_uint32 *) calloc(chd_ph->nbuckets, sizeof(cmph_uint32)); +// +// init_genrand(time(0)); + + while(1) + { + iterations --; + if (mph->verbosity) + { + fprintf(stderr, "Starting mapping step for mph creation of %u keys with %u bins\n", chd_ph->m, chd_ph->n); + } + + if(!chd_ph_mapping(mph, buckets, items, &max_bucket_size)) + { + if (mph->verbosity) + { + fprintf(stderr, "Failure in mapping step\n"); + } + failure = 1; + goto cleanup; + } + + if (mph->verbosity) + { + fprintf(stderr, "Starting ordering step\n"); + } + if(sorted_lists) + { + free(sorted_lists); + } + + sorted_lists = chd_ph_ordering(&buckets, &items, chd_ph->nbuckets, chd_ph->m, max_bucket_size); + + if (mph->verbosity) + { + fprintf(stderr, "Starting searching step\n"); + } + + searching_success = chd_ph_searching(chd_ph, buckets, items, max_bucket_size, sorted_lists, max_probes, disp_table); + if(searching_success) break; + + // reset occup_table + if(chd_ph->keys_per_bin > 1) + memset(chd_ph->occup_table, 0, chd_ph->n); + else + memset(chd_ph->occup_table, 0, ((chd_ph->n + 31)/32) * sizeof(cmph_uint32)); + if(iterations == 0) + { + // Cleanup memory + if (mph->verbosity) + { + fprintf(stderr, "Failure because the max trials was exceeded\n"); + } + failure = 1; + goto cleanup; + }; + } + + #ifdef DEBUG + { + if(!chd_ph_check_bin_hashing(chd_ph, buckets, items, disp_table,sorted_lists,max_bucket_size)) + { + + DEBUGP("Error for bin packing generation"); + failure = 1; + goto cleanup; + } + } + #endif + + if (mph->verbosity) + { + fprintf(stderr, "Starting compressing step\n"); + } + + if(chd_ph->cs) + { + free(chd_ph->cs); + } + chd_ph->cs = (compressed_seq_t *) calloc(1, sizeof(compressed_seq_t)); + compressed_seq_init(chd_ph->cs); + compressed_seq_generate(chd_ph->cs, disp_table, chd_ph->nbuckets); + + #ifdef CMPH_TIMING + ELAPSED_TIME_IN_SECONDS(&construction_time); + register double entropy = chd_ph_get_entropy(disp_table, chd_ph->nbuckets, max_probes); + DEBUGP("Entropy = %.4f\n", entropy/chd_ph->m); + #endif + +cleanup: + chd_ph_bucket_destroy(buckets); + free(items); + free(sorted_lists); + free(disp_table); + if(failure) + { + if(chd_ph->hl) + { + hash_state_destroy(chd_ph->hl); + } + chd_ph->hl = NULL; + return NULL; + } + + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + chd_phf = (chd_ph_data_t *)malloc(sizeof(chd_ph_data_t)); + + chd_phf->cs = chd_ph->cs; + chd_ph->cs = NULL; //transfer memory ownership + chd_phf->hl = chd_ph->hl; + chd_ph->hl = NULL; //transfer memory ownership + chd_phf->n = chd_ph->n; + chd_phf->nbuckets = chd_ph->nbuckets; + + mphf->data = chd_phf; + mphf->size = chd_ph->n; + + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + + #ifdef CMPH_TIMING + register cmph_uint32 space_usage = chd_ph_packed_size(mphf)*8; + construction_time = construction_time - construction_time_begin; + fprintf(stdout, "%u\t%.2f\t%u\t%.4f\t%.4f\t%.4f\t%.4f\n", chd_ph->m, load_factor, chd_ph->keys_per_bucket, construction_time, space_usage/(double)chd_ph->m, space_lower_bound, entropy/chd_ph->m); + #endif + + return mphf; +} + + + +void chd_ph_load(FILE *fd, cmph_t *mphf) +{ + char *buf = NULL; + cmph_uint32 buflen; + register size_t nbytes; + chd_ph_data_t *chd_ph = (chd_ph_data_t *)malloc(sizeof(chd_ph_data_t)); + + DEBUGP("Loading chd_ph mphf\n"); + mphf->data = chd_ph; + + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, fd); + chd_ph->hl = hash_state_load(buf, buflen); + free(buf); + + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + DEBUGP("Compressed sequence structure has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, fd); + chd_ph->cs = (compressed_seq_t *) calloc(1, sizeof(compressed_seq_t)); + compressed_seq_load(chd_ph->cs, buf, buflen); + free(buf); + + // loading n and nbuckets + DEBUGP("Reading n and nbuckets\n"); + nbytes = fread(&(chd_ph->n), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fread(&(chd_ph->nbuckets), sizeof(cmph_uint32), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + } + +} + +int chd_ph_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + register size_t nbytes; + chd_ph_data_t *data = (chd_ph_data_t *)mphf->data; + + __cmph_dump(mphf, fd); + + hash_state_dump(data->hl, &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + compressed_seq_dump(data->cs, &buf, &buflen); + DEBUGP("Dumping compressed sequence structure with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + // dumping n and nbuckets + nbytes = fwrite(&(data->n), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->nbuckets), sizeof(cmph_uint32), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + return 1; +} + +void chd_ph_destroy(cmph_t *mphf) +{ + chd_ph_data_t *data = (chd_ph_data_t *)mphf->data; + compressed_seq_destroy(data->cs); + free(data->cs); + hash_state_destroy(data->hl); + free(data); + free(mphf); + +} + +cmph_uint32 chd_ph_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + register chd_ph_data_t * chd_ph = mphf->data; + cmph_uint32 hl[3]; + register cmph_uint32 disp,position; + register cmph_uint32 probe0_num,probe1_num; + register cmph_uint32 f,g,h; + hash_vector(chd_ph->hl, key, keylen, hl); + g = hl[0] % chd_ph->nbuckets; + f = hl[1] % chd_ph->n; + h = hl[2] % (chd_ph->n-1) + 1; + + disp = compressed_seq_query(chd_ph->cs, g); + probe0_num = disp % chd_ph->n; + probe1_num = disp/chd_ph->n; + position = (cmph_uint32)((f + ((cmph_uint64 )h)*probe0_num + probe1_num) % chd_ph->n); + return position; +} + +void chd_ph_pack(cmph_t *mphf, void *packed_mphf) +{ + chd_ph_data_t *data = (chd_ph_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + + // packing hl type + CMPH_HASH hl_type = hash_get_type(data->hl); + *((cmph_uint32 *) ptr) = hl_type; + ptr += sizeof(cmph_uint32); + + // packing hl + hash_state_pack(data->hl, ptr); + ptr += hash_state_packed_size(hl_type); + + // packing n + *((cmph_uint32 *) ptr) = data->n; + ptr += sizeof(data->n); + + // packing nbuckets + *((cmph_uint32 *) ptr) = data->nbuckets; + ptr += sizeof(data->nbuckets); + + // packing cs + compressed_seq_pack(data->cs, ptr); + //ptr += compressed_seq_packed_size(data->cs); + +} + +cmph_uint32 chd_ph_packed_size(cmph_t *mphf) +{ + register chd_ph_data_t *data = (chd_ph_data_t *)mphf->data; + register CMPH_HASH hl_type = hash_get_type(data->hl); + register cmph_uint32 hash_state_pack_size = hash_state_packed_size(hl_type); + register cmph_uint32 cs_pack_size = compressed_seq_packed_size(data->cs); + + return (cmph_uint32)(sizeof(CMPH_ALGO) + hash_state_pack_size + cs_pack_size + 3*sizeof(cmph_uint32)); + +} + +cmph_uint32 chd_ph_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + register CMPH_HASH hl_type = *(cmph_uint32 *)packed_mphf; + register cmph_uint8 *hl_ptr = (cmph_uint8 *)(packed_mphf) + 4; + + register cmph_uint32 * ptr = (cmph_uint32 *)(hl_ptr + hash_state_packed_size(hl_type)); + register cmph_uint32 n = *ptr++; + register cmph_uint32 nbuckets = *ptr++; + cmph_uint32 hl[3]; + + register cmph_uint32 disp,position; + register cmph_uint32 probe0_num,probe1_num; + register cmph_uint32 f,g,h; + + hash_vector_packed(hl_ptr, hl_type, key, keylen, hl); + + g = hl[0] % nbuckets; + f = hl[1] % n; + h = hl[2] % (n-1) + 1; + + disp = compressed_seq_query_packed(ptr, g); + probe0_num = disp % n; + probe1_num = disp/n; + position = (cmph_uint32)((f + ((cmph_uint64 )h)*probe0_num + probe1_num) % n); + return position; +} + + + diff --git a/girepository/cmph/chd_ph.h b/girepository/cmph/chd_ph.h new file mode 100644 index 000000000..03e4087c9 --- /dev/null +++ b/girepository/cmph/chd_ph.h @@ -0,0 +1,59 @@ +#ifndef _CMPH_CHD_PH_H__ +#define _CMPH_CHD_PH_H__ + +#include "cmph.h" + +typedef struct __chd_ph_data_t chd_ph_data_t; +typedef struct __chd_ph_config_data_t chd_ph_config_data_t; + +/* Config API */ +chd_ph_config_data_t *chd_ph_config_new(void); +void chd_ph_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); + +/** \fn void chd_ph_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin); + * \brief Allows to set the number of keys per bin. + * \param mph pointer to the configuration structure + * \param keys_per_bin value for the number of keys per bin + */ +void chd_ph_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin); + +/** \fn void chd_ph_config_set_b(cmph_config_t *mph, cmph_uint32 keys_per_bucket); + * \brief Allows to set the number of keys per bucket. + * \param mph pointer to the configuration structure + * \param keys_per_bucket value for the number of keys per bucket + */ +void chd_ph_config_set_b(cmph_config_t *mph, cmph_uint32 keys_per_bucket); +void chd_ph_config_destroy(cmph_config_t *mph); + + +/* Chd algorithm API */ +cmph_t *chd_ph_new(cmph_config_t *mph, double c); +void chd_ph_load(FILE *fd, cmph_t *mphf); +int chd_ph_dump(cmph_t *mphf, FILE *fd); +void chd_ph_destroy(cmph_t *mphf); +cmph_uint32 chd_ph_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void chd_ph_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void chd_ph_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 chd_ph_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 chd_ph_packed_size(cmph_t *mphf); + +/** cmph_uint32 chd_ph_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 chd_ph_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/chd_structs.h b/girepository/cmph/chd_structs.h new file mode 100644 index 000000000..d62f68268 --- /dev/null +++ b/girepository/cmph/chd_structs.h @@ -0,0 +1,21 @@ +#ifndef __CMPH_CHD_STRUCTS_H__ +#define __CMPH_CHD_STRUCTS_H__ + +#include "chd_structs_ph.h" +#include "chd_ph.h" +#include "compressed_rank.h" + +struct __chd_data_t +{ + cmph_uint32 packed_cr_size; + cmph_uint8 * packed_cr; // packed compressed rank structure to control the number of zeros in a bit vector + + cmph_uint32 packed_chd_phf_size; + cmph_uint8 * packed_chd_phf; +}; + +struct __chd_config_data_t +{ + cmph_config_t *chd_ph; // chd_ph algorithm must be used here +}; +#endif diff --git a/girepository/cmph/chd_structs_ph.h b/girepository/cmph/chd_structs_ph.h new file mode 100644 index 000000000..d86921822 --- /dev/null +++ b/girepository/cmph/chd_structs_ph.h @@ -0,0 +1,29 @@ +#ifndef __CMPH_CHD_PH_STRUCTS_H__ +#define __CMPH_CHD_PH_STRUCTS_H__ + +#include "hash_state.h" +#include "compressed_seq.h" + +struct __chd_ph_data_t +{ + compressed_seq_t * cs; // compressed displacement values + cmph_uint32 nbuckets; // number of buckets + cmph_uint32 n; // number of bins + hash_state_t *hl; // linear hash function +}; + +struct __chd_ph_config_data_t +{ + CMPH_HASH hashfunc; // linear hash function to be used + compressed_seq_t * cs; // compressed displacement values + cmph_uint32 nbuckets; // number of buckets + cmph_uint32 n; // number of bins + hash_state_t *hl; // linear hash function + + cmph_uint32 m; // number of keys + cmph_uint8 use_h; // flag to indicate the of use of a heuristic (use_h = 1) + cmph_uint32 keys_per_bin;//maximum number of keys per bin + cmph_uint32 keys_per_bucket; // average number of keys per bucket + cmph_uint8 *occup_table; // table that indicates occupied positions +}; +#endif diff --git a/girepository/cmph/chm.c b/girepository/cmph/chm.c new file mode 100644 index 000000000..36a07a0d3 --- /dev/null +++ b/girepository/cmph/chm.c @@ -0,0 +1,396 @@ +#include "graph.h" +#include "chm.h" +#include "cmph_structs.h" +#include "chm_structs.h" +#include "hash.h" +#include "bitbool.h" + +#include +#include +#include +#include +#include +#include + +//#define DEBUG +#include "debug.h" + +static int chm_gen_edges(cmph_config_t *mph); +static void chm_traverse(chm_config_data_t *chm, cmph_uint8 *visited, cmph_uint32 v); + +chm_config_data_t *chm_config_new(void) +{ + chm_config_data_t *chm = NULL; + chm = (chm_config_data_t *)malloc(sizeof(chm_config_data_t)); + assert(chm); + memset(chm, 0, sizeof(chm_config_data_t)); + chm->hashfuncs[0] = CMPH_HASH_JENKINS; + chm->hashfuncs[1] = CMPH_HASH_JENKINS; + chm->g = NULL; + chm->graph = NULL; + chm->hashes = NULL; + return chm; +} +void chm_config_destroy(cmph_config_t *mph) +{ + chm_config_data_t *data = (chm_config_data_t *)mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void chm_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + chm_config_data_t *chm = (chm_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 2) break; //chm only uses two hash functions + chm->hashfuncs[i] = *hashptr; + ++i, ++hashptr; + } +} + +cmph_t *chm_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + chm_data_t *chmf = NULL; + + cmph_uint32 i; + cmph_uint32 iterations = 20; + cmph_uint8 *visited = NULL; + chm_config_data_t *chm = (chm_config_data_t *)mph->data; + chm->m = mph->key_source->nkeys; + if (c == 0) c = 2.09; + chm->n = (cmph_uint32)ceil(c * mph->key_source->nkeys); + DEBUGP("m (edges): %u n (vertices): %u c: %f\n", chm->m, chm->n, c); + chm->graph = graph_new(chm->n, chm->m); + DEBUGP("Created graph\n"); + + chm->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*3); + for(i = 0; i < 3; ++i) chm->hashes[i] = NULL; + //Mapping step + if (mph->verbosity) + { + fprintf(stderr, "Entering mapping step for mph creation of %u keys with graph sized %u\n", chm->m, chm->n); + } + while(1) + { + int ok; + chm->hashes[0] = hash_state_new(chm->hashfuncs[0], chm->n); + chm->hashes[1] = hash_state_new(chm->hashfuncs[1], chm->n); + ok = chm_gen_edges(mph); + if (!ok) + { + --iterations; + hash_state_destroy(chm->hashes[0]); + chm->hashes[0] = NULL; + hash_state_destroy(chm->hashes[1]); + chm->hashes[1] = NULL; + DEBUGP("%u iterations remaining\n", iterations); + if (mph->verbosity) + { + fprintf(stderr, "Acyclic graph creation failure - %u iterations remaining\n", iterations); + } + if (iterations == 0) break; + } + else break; + } + if (iterations == 0) + { + graph_destroy(chm->graph); + return NULL; + } + + //Assignment step + if (mph->verbosity) + { + fprintf(stderr, "Starting assignment step\n"); + } + DEBUGP("Assignment step\n"); + visited = (cmph_uint8 *)malloc((size_t)(chm->n/8 + 1)); + memset(visited, 0, (size_t)(chm->n/8 + 1)); + free(chm->g); + chm->g = (cmph_uint32 *)malloc(chm->n * sizeof(cmph_uint32)); + assert(chm->g); + for (i = 0; i < chm->n; ++i) + { + if (!GETBIT(visited,i)) + { + chm->g[i] = 0; + chm_traverse(chm, visited, i); + } + } + graph_destroy(chm->graph); + free(visited); + chm->graph = NULL; + + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + chmf = (chm_data_t *)malloc(sizeof(chm_data_t)); + chmf->g = chm->g; + chm->g = NULL; //transfer memory ownership + chmf->hashes = chm->hashes; + chm->hashes = NULL; //transfer memory ownership + chmf->n = chm->n; + chmf->m = chm->m; + mphf->data = chmf; + mphf->size = chm->m; + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + return mphf; +} + +static void chm_traverse(chm_config_data_t *chm, cmph_uint8 *visited, cmph_uint32 v) +{ + + graph_iterator_t it = graph_neighbors_it(chm->graph, v); + cmph_uint32 neighbor = 0; + SETBIT(visited,v); + + DEBUGP("Visiting vertex %u\n", v); + while((neighbor = graph_next_neighbor(chm->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + DEBUGP("Visiting neighbor %u\n", neighbor); + if(GETBIT(visited,neighbor)) continue; + DEBUGP("Visiting neighbor %u\n", neighbor); + DEBUGP("Visiting edge %u->%u with id %u\n", v, neighbor, graph_edge_id(chm->graph, v, neighbor)); + chm->g[neighbor] = graph_edge_id(chm->graph, v, neighbor) - chm->g[v]; + DEBUGP("g is %u (%u - %u mod %u)\n", chm->g[neighbor], graph_edge_id(chm->graph, v, neighbor), chm->g[v], chm->m); + chm_traverse(chm, visited, neighbor); + } +} + +static int chm_gen_edges(cmph_config_t *mph) +{ + cmph_uint32 e; + chm_config_data_t *chm = (chm_config_data_t *)mph->data; + int cycles = 0; + + DEBUGP("Generating edges for %u vertices with hash functions %s and %s\n", chm->n, cmph_hash_names[chm->hashfuncs[0]], cmph_hash_names[chm->hashfuncs[1]]); + graph_clear_edges(chm->graph); + mph->key_source->rewind(mph->key_source->data); + for (e = 0; e < mph->key_source->nkeys; ++e) + { + cmph_uint32 h1, h2; + cmph_uint32 keylen; + char *key; + mph->key_source->read(mph->key_source->data, &key, &keylen); + h1 = hash(chm->hashes[0], key, keylen) % chm->n; + h2 = hash(chm->hashes[1], key, keylen) % chm->n; + if (h1 == h2) if (++h2 >= chm->n) h2 = 0; + if (h1 == h2) + { + if (mph->verbosity) fprintf(stderr, "Self loop for key %u\n", e); + mph->key_source->dispose(mph->key_source->data, key, keylen); + return 0; + } + DEBUGP("Adding edge: %u -> %u for key %s\n", h1, h2, key); + mph->key_source->dispose(mph->key_source->data, key, keylen); + graph_add_edge(chm->graph, h1, h2); + } + cycles = graph_is_cyclic(chm->graph); + if (mph->verbosity && cycles) fprintf(stderr, "Cyclic graph generated\n"); + DEBUGP("Looking for cycles: %u\n", cycles); + + return ! cycles; +} + +int chm_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 two = 2; //number of hash functions + chm_data_t *data = (chm_data_t *)mphf->data; + register size_t nbytes; + + __cmph_dump(mphf, fd); + + nbytes = fwrite(&two, sizeof(cmph_uint32), (size_t)1, fd); + hash_state_dump(data->hashes[0], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + hash_state_dump(data->hashes[1], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + nbytes = fwrite(&(data->n), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->m), sizeof(cmph_uint32), (size_t)1, fd); + + nbytes = fwrite(data->g, sizeof(cmph_uint32)*data->n, (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } +/* #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < data->n; ++i) fprintf(stderr, "%u ", data->g[i]); + fprintf(stderr, "\n"); + #endif*/ + return 1; +} + +void chm_load(FILE *f, cmph_t *mphf) +{ + cmph_uint32 nhashes; + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 i; + chm_data_t *chm = (chm_data_t *)malloc(sizeof(chm_data_t)); + register size_t nbytes; + DEBUGP("Loading chm mphf\n"); + mphf->data = chm; + nbytes = fread(&nhashes, sizeof(cmph_uint32), (size_t)1, f); + chm->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*(nhashes + 1)); + chm->hashes[nhashes] = NULL; + DEBUGP("Reading %u hashes\n", nhashes); + for (i = 0; i < nhashes; ++i) + { + hash_state_t *state = NULL; + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + state = hash_state_load(buf, buflen); + chm->hashes[i] = state; + free(buf); + } + + DEBUGP("Reading m and n\n"); + nbytes = fread(&(chm->n), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(chm->m), sizeof(cmph_uint32), (size_t)1, f); + + chm->g = (cmph_uint32 *)malloc(sizeof(cmph_uint32)*chm->n); + nbytes = fread(chm->g, chm->n*sizeof(cmph_uint32), (size_t)1, f); + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return; + } + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < chm->n; ++i) fprintf(stderr, "%u ", chm->g[i]); + fprintf(stderr, "\n"); + #endif + return; +} + + +cmph_uint32 chm_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + chm_data_t *chm = mphf->data; + cmph_uint32 h1 = hash(chm->hashes[0], key, keylen) % chm->n; + cmph_uint32 h2 = hash(chm->hashes[1], key, keylen) % chm->n; + DEBUGP("key: %s h1: %u h2: %u\n", key, h1, h2); + if (h1 == h2 && ++h2 >= chm->n) h2 = 0; + DEBUGP("key: %s g[h1]: %u g[h2]: %u edges: %u\n", key, chm->g[h1], chm->g[h2], chm->m); + return (chm->g[h1] + chm->g[h2]) % chm->m; +} +void chm_destroy(cmph_t *mphf) +{ + chm_data_t *data = (chm_data_t *)mphf->data; + free(data->g); + hash_state_destroy(data->hashes[0]); + hash_state_destroy(data->hashes[1]); + free(data->hashes); + free(data); + free(mphf); +} + +/** \fn void chm_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void chm_pack(cmph_t *mphf, void *packed_mphf) +{ + chm_data_t *data = (chm_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + CMPH_HASH h2_type; + + // packing h1 type + CMPH_HASH h1_type = hash_get_type(data->hashes[0]); + *((cmph_uint32 *) ptr) = h1_type; + ptr += sizeof(cmph_uint32); + + // packing h1 + hash_state_pack(data->hashes[0], ptr); + ptr += hash_state_packed_size(h1_type); + + // packing h2 type + h2_type = hash_get_type(data->hashes[1]); + *((cmph_uint32 *) ptr) = h2_type; + ptr += sizeof(cmph_uint32); + + // packing h2 + hash_state_pack(data->hashes[1], ptr); + ptr += hash_state_packed_size(h2_type); + + // packing n + *((cmph_uint32 *) ptr) = data->n; + ptr += sizeof(data->n); + + // packing m + *((cmph_uint32 *) ptr) = data->m; + ptr += sizeof(data->m); + + // packing g + memcpy(ptr, data->g, sizeof(cmph_uint32)*data->n); +} + +/** \fn cmph_uint32 chm_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 chm_packed_size(cmph_t *mphf) +{ + chm_data_t *data = (chm_data_t *)mphf->data; + CMPH_HASH h1_type = hash_get_type(data->hashes[0]); + CMPH_HASH h2_type = hash_get_type(data->hashes[1]); + + return (cmph_uint32)(sizeof(CMPH_ALGO) + hash_state_packed_size(h1_type) + hash_state_packed_size(h2_type) + + 4*sizeof(cmph_uint32) + sizeof(cmph_uint32)*data->n); +} + +/** cmph_uint32 chm_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 chm_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + register cmph_uint8 *h1_ptr = packed_mphf; + register CMPH_HASH h1_type = *((cmph_uint32 *)h1_ptr); + register cmph_uint8 *h2_ptr; + register CMPH_HASH h2_type; + register cmph_uint32 *g_ptr; + register cmph_uint32 n, m, h1, h2; + + h1_ptr += 4; + + h2_ptr = h1_ptr + hash_state_packed_size(h1_type); + h2_type = *((cmph_uint32 *)h2_ptr); + h2_ptr += 4; + + g_ptr = (cmph_uint32 *)(h2_ptr + hash_state_packed_size(h2_type)); + + n = *g_ptr++; + m = *g_ptr++; + + h1 = hash_packed(h1_ptr, h1_type, key, keylen) % n; + h2 = hash_packed(h2_ptr, h2_type, key, keylen) % n; + DEBUGP("key: %s h1: %u h2: %u\n", key, h1, h2); + if (h1 == h2 && ++h2 >= n) h2 = 0; + DEBUGP("key: %s g[h1]: %u g[h2]: %u edges: %u\n", key, g_ptr[h1], g_ptr[h2], m); + return (g_ptr[h1] + g_ptr[h2]) % m; +} diff --git a/girepository/cmph/chm.h b/girepository/cmph/chm.h new file mode 100644 index 000000000..392d23aaf --- /dev/null +++ b/girepository/cmph/chm.h @@ -0,0 +1,42 @@ +#ifndef __CMPH_CHM_H__ +#define __CMPH_CHM_H__ + +#include "cmph.h" + +typedef struct __chm_data_t chm_data_t; +typedef struct __chm_config_data_t chm_config_data_t; + +chm_config_data_t *chm_config_new(void); +void chm_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void chm_config_destroy(cmph_config_t *mph); +cmph_t *chm_new(cmph_config_t *mph, double c); + +void chm_load(FILE *f, cmph_t *mphf); +int chm_dump(cmph_t *mphf, FILE *f); +void chm_destroy(cmph_t *mphf); +cmph_uint32 chm_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void chm_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void chm_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 chm_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 chm_packed_size(cmph_t *mphf); + +/** cmph_uint32 chm_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 chm_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/chm_structs.h b/girepository/cmph/chm_structs.h new file mode 100644 index 000000000..fcad1bc3d --- /dev/null +++ b/girepository/cmph/chm_structs.h @@ -0,0 +1,24 @@ +#ifndef __CMPH_CHM_STRUCTS_H__ +#define __CMPH_CHM_STRUCTS_H__ + +#include "hash_state.h" + +struct __chm_data_t +{ + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + cmph_uint32 *g; + hash_state_t **hashes; +}; + +struct __chm_config_data_t +{ + CMPH_HASH hashfuncs[2]; + cmph_uint32 m; //edges (words) count + cmph_uint32 n; //vertex count + graph_t *graph; + cmph_uint32 *g; + hash_state_t **hashes; +}; + +#endif diff --git a/girepository/cmph/cmph.c b/girepository/cmph/cmph.c new file mode 100644 index 000000000..3fd40a297 --- /dev/null +++ b/girepository/cmph/cmph.c @@ -0,0 +1,844 @@ +#include "cmph.h" +#include "cmph_structs.h" +#include "chm.h" +#include "bmz.h" +#include "bmz8.h" +#include "brz.h" +#include "fch.h" +#include "bdz.h" +#include "bdz_ph.h" +#include "chd_ph.h" +#include "chd.h" + +#include +#include +#include +//#define DEBUG +#include "debug.h" + +const char *cmph_names[] = {"bmz", "bmz8", "chm", "brz", "fch", "bdz", "bdz_ph", "chd_ph", "chd", NULL }; + +typedef struct +{ + void *vector; + cmph_uint32 position; // access position when data is a vector +} cmph_vector_t; + + + +/** + * Support a vector of struct as the source of keys. + * + * E.g. The keys could be the fieldB's in a vector of struct rec where + * struct rec is defined as: + * struct rec { + * fieldA; + * fieldB; + * fieldC; + * } + */ +typedef struct +{ + void *vector; /* Pointer to the vector of struct */ + cmph_uint32 position; /* current position */ + cmph_uint32 struct_size; /* The size of the struct */ + cmph_uint32 key_offset; /* The byte offset of the key in the struct */ + cmph_uint32 key_len; /* The length of the key */ +} cmph_struct_vector_t; + + +static cmph_io_adapter_t *cmph_io_vector_new(void * vector, cmph_uint32 nkeys); +static void cmph_io_vector_destroy(cmph_io_adapter_t * key_source); + +static cmph_io_adapter_t *cmph_io_struct_vector_new(void * vector, cmph_uint32 struct_size, cmph_uint32 key_offset, cmph_uint32 key_len, cmph_uint32 nkeys); +static void cmph_io_struct_vector_destroy(cmph_io_adapter_t * key_source); + +static int key_nlfile_read(void *data, char **key, cmph_uint32 *keylen) +{ + FILE *fd = (FILE *)data; + *key = NULL; + *keylen = 0; + while(1) + { + char buf[BUFSIZ]; + char *c = fgets(buf, BUFSIZ, fd); + if (c == NULL) return -1; + if (feof(fd)) return -1; + *key = (char *)realloc(*key, *keylen + strlen(buf) + 1); + memcpy(*key + *keylen, buf, strlen(buf)); + *keylen += (cmph_uint32)strlen(buf); + if (buf[strlen(buf) - 1] != '\n') continue; + break; + } + if ((*keylen) && (*key)[*keylen - 1] == '\n') + { + (*key)[(*keylen) - 1] = 0; + --(*keylen); + } + return (int)(*keylen); +} + +static int key_byte_vector_read(void *data, char **key, cmph_uint32 *keylen) +{ + cmph_vector_t *cmph_vector = (cmph_vector_t *)data; + cmph_uint8 **keys_vd = (cmph_uint8 **)cmph_vector->vector; + size_t size; + memcpy(keylen, keys_vd[cmph_vector->position], sizeof(*keylen)); + size = *keylen; + *key = (char *)malloc(size); + memcpy(*key, keys_vd[cmph_vector->position] + sizeof(*keylen), size); + cmph_vector->position = cmph_vector->position + 1; + return (int)(*keylen); + +} + +static int key_struct_vector_read(void *data, char **key, cmph_uint32 *keylen) +{ + cmph_struct_vector_t *cmph_struct_vector = (cmph_struct_vector_t *)data; + char *keys_vd = (char *)cmph_struct_vector->vector; + size_t size; + *keylen = cmph_struct_vector->key_len; + size = *keylen; + *key = (char *)malloc(size); + memcpy(*key, (keys_vd + (cmph_struct_vector->position * cmph_struct_vector->struct_size) + cmph_struct_vector->key_offset), size); + cmph_struct_vector->position = cmph_struct_vector->position + 1; + return (int)(*keylen); +} + +static int key_vector_read(void *data, char **key, cmph_uint32 *keylen) +{ + cmph_vector_t *cmph_vector = (cmph_vector_t *)data; + char **keys_vd = (char **)cmph_vector->vector; + size_t size; + *keylen = (cmph_uint32)strlen(keys_vd[cmph_vector->position]); + size = *keylen; + *key = (char *)malloc(size + 1); + strcpy(*key, keys_vd[cmph_vector->position]); + cmph_vector->position = cmph_vector->position + 1; + return (int)(*keylen); + +} + + +static void key_nlfile_dispose(void *data, char *key, cmph_uint32 keylen) +{ + free(key); +} + +static void key_vector_dispose(void *data, char *key, cmph_uint32 keylen) +{ + free(key); +} + +static void key_nlfile_rewind(void *data) +{ + FILE *fd = (FILE *)data; + rewind(fd); +} + +static void key_struct_vector_rewind(void *data) +{ + cmph_struct_vector_t *cmph_struct_vector = (cmph_struct_vector_t *)data; + cmph_struct_vector->position = 0; +} + +static void key_vector_rewind(void *data) +{ + cmph_vector_t *cmph_vector = (cmph_vector_t *)data; + cmph_vector->position = 0; +} + +static cmph_uint32 count_nlfile_keys(FILE *fd) +{ + cmph_uint32 count = 0; + rewind(fd); + while(1) + { + char buf[BUFSIZ]; + if (fgets(buf, BUFSIZ, fd) == NULL) break; + if (feof(fd)) break; + if (buf[strlen(buf) - 1] != '\n') continue; + ++count; + } + rewind(fd); + return count; +} + +cmph_io_adapter_t *cmph_io_nlfile_adapter(FILE * keys_fd) +{ + cmph_io_adapter_t * key_source = (cmph_io_adapter_t *)malloc(sizeof(cmph_io_adapter_t)); + assert(key_source); + key_source->data = (void *)keys_fd; + key_source->nkeys = count_nlfile_keys(keys_fd); + key_source->read = key_nlfile_read; + key_source->dispose = key_nlfile_dispose; + key_source->rewind = key_nlfile_rewind; + return key_source; +} + +void cmph_io_nlfile_adapter_destroy(cmph_io_adapter_t * key_source) +{ + free(key_source); +} + +cmph_io_adapter_t *cmph_io_nlnkfile_adapter(FILE * keys_fd, cmph_uint32 nkeys) +{ + cmph_io_adapter_t * key_source = (cmph_io_adapter_t *)malloc(sizeof(cmph_io_adapter_t)); + assert(key_source); + key_source->data = (void *)keys_fd; + key_source->nkeys = nkeys; + key_source->read = key_nlfile_read; + key_source->dispose = key_nlfile_dispose; + key_source->rewind = key_nlfile_rewind; + return key_source; +} + +void cmph_io_nlnkfile_adapter_destroy(cmph_io_adapter_t * key_source) +{ + free(key_source); +} + + +static cmph_io_adapter_t *cmph_io_struct_vector_new(void * vector, cmph_uint32 struct_size, cmph_uint32 key_offset, cmph_uint32 key_len, cmph_uint32 nkeys) +{ + cmph_io_adapter_t * key_source = (cmph_io_adapter_t *)malloc(sizeof(cmph_io_adapter_t)); + cmph_struct_vector_t * cmph_struct_vector = (cmph_struct_vector_t *)malloc(sizeof(cmph_struct_vector_t)); + assert(key_source); + assert(cmph_struct_vector); + cmph_struct_vector->vector = vector; + cmph_struct_vector->position = 0; + cmph_struct_vector->struct_size = struct_size; + cmph_struct_vector->key_offset = key_offset; + cmph_struct_vector->key_len = key_len; + key_source->data = (void *)cmph_struct_vector; + key_source->nkeys = nkeys; + return key_source; +} + +static void cmph_io_struct_vector_destroy(cmph_io_adapter_t * key_source) +{ + cmph_struct_vector_t *cmph_struct_vector = (cmph_struct_vector_t *)key_source->data; + cmph_struct_vector->vector = NULL; + free(cmph_struct_vector); + free(key_source); +} + +static cmph_io_adapter_t *cmph_io_vector_new(void * vector, cmph_uint32 nkeys) +{ + cmph_io_adapter_t * key_source = (cmph_io_adapter_t *)malloc(sizeof(cmph_io_adapter_t)); + cmph_vector_t * cmph_vector = (cmph_vector_t *)malloc(sizeof(cmph_vector_t)); + assert(key_source); + assert(cmph_vector); + cmph_vector->vector = vector; + cmph_vector->position = 0; + key_source->data = (void *)cmph_vector; + key_source->nkeys = nkeys; + return key_source; +} + +static void cmph_io_vector_destroy(cmph_io_adapter_t * key_source) +{ + cmph_vector_t *cmph_vector = (cmph_vector_t *)key_source->data; + cmph_vector->vector = NULL; + free(cmph_vector); + free(key_source); +} + +cmph_io_adapter_t *cmph_io_byte_vector_adapter(cmph_uint8 ** vector, cmph_uint32 nkeys) +{ + cmph_io_adapter_t * key_source = cmph_io_vector_new(vector, nkeys); + key_source->read = key_byte_vector_read; + key_source->dispose = key_vector_dispose; + key_source->rewind = key_vector_rewind; + return key_source; +} +void cmph_io_byte_vector_adapter_destroy(cmph_io_adapter_t * key_source) +{ + cmph_io_vector_destroy(key_source); +} + +cmph_io_adapter_t *cmph_io_struct_vector_adapter(void * vector, cmph_uint32 struct_size, cmph_uint32 key_offset, cmph_uint32 key_len, cmph_uint32 nkeys) +{ + cmph_io_adapter_t * key_source = cmph_io_struct_vector_new(vector, struct_size, key_offset, key_len, nkeys); + key_source->read = key_struct_vector_read; + key_source->dispose = key_vector_dispose; + key_source->rewind = key_struct_vector_rewind; + return key_source; +} + +void cmph_io_struct_vector_adapter_destroy(cmph_io_adapter_t * key_source) +{ + cmph_io_struct_vector_destroy(key_source); +} + +cmph_io_adapter_t *cmph_io_vector_adapter(char ** vector, cmph_uint32 nkeys) +{ + cmph_io_adapter_t * key_source = cmph_io_vector_new(vector, nkeys); + key_source->read = key_vector_read; + key_source->dispose = key_vector_dispose; + key_source->rewind = key_vector_rewind; + return key_source; +} + +void cmph_io_vector_adapter_destroy(cmph_io_adapter_t * key_source) +{ + cmph_io_vector_destroy(key_source); +} + +cmph_config_t *cmph_config_new(cmph_io_adapter_t *key_source) +{ + cmph_config_t *mph = NULL; + mph = __config_new(key_source); + assert(mph); + mph->algo = CMPH_CHM; // default value + mph->data = chm_config_new(); + return mph; +} + +void cmph_config_set_algo(cmph_config_t *mph, CMPH_ALGO algo) +{ + if (algo != mph->algo) + { + switch (mph->algo) + { + case CMPH_CHM: + chm_config_destroy(mph); + break; + case CMPH_BMZ: + bmz_config_destroy(mph); + break; + case CMPH_BMZ8: + bmz8_config_destroy(mph); + break; + case CMPH_BRZ: + brz_config_destroy(mph); + break; + case CMPH_FCH: + fch_config_destroy(mph); + break; + case CMPH_BDZ: + bdz_config_destroy(mph); + break; + case CMPH_BDZ_PH: + bdz_ph_config_destroy(mph); + break; + case CMPH_CHD_PH: + chd_ph_config_destroy(mph); + break; + case CMPH_CHD: + chd_config_destroy(mph); + break; + default: + assert(0); + } + switch(algo) + { + case CMPH_CHM: + mph->data = chm_config_new(); + break; + case CMPH_BMZ: + mph->data = bmz_config_new(); + break; + case CMPH_BMZ8: + mph->data = bmz8_config_new(); + break; + case CMPH_BRZ: + mph->data = brz_config_new(); + break; + case CMPH_FCH: + mph->data = fch_config_new(); + break; + case CMPH_BDZ: + mph->data = bdz_config_new(); + break; + case CMPH_BDZ_PH: + mph->data = bdz_ph_config_new(); + break; + case CMPH_CHD_PH: + mph->data = chd_ph_config_new(); + break; + case CMPH_CHD: + mph->data = chd_config_new(mph); + break; + default: + assert(0); + } + } + mph->algo = algo; +} + +void cmph_config_set_tmp_dir(cmph_config_t *mph, cmph_uint8 *tmp_dir) +{ + if (mph->algo == CMPH_BRZ) + { + brz_config_set_tmp_dir(mph, tmp_dir); + } +} + + +void cmph_config_set_mphf_fd(cmph_config_t *mph, FILE *mphf_fd) +{ + if (mph->algo == CMPH_BRZ) + { + brz_config_set_mphf_fd(mph, mphf_fd); + } +} + +void cmph_config_set_b(cmph_config_t *mph, cmph_uint32 b) +{ + if (mph->algo == CMPH_BRZ) + { + brz_config_set_b(mph, b); + } + else if (mph->algo == CMPH_BDZ) + { + bdz_config_set_b(mph, b); + } + else if (mph->algo == CMPH_CHD_PH) + { + chd_ph_config_set_b(mph, b); + } + else if (mph->algo == CMPH_CHD) + { + chd_config_set_b(mph, b); + } +} + +void cmph_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin) +{ + if (mph->algo == CMPH_CHD_PH) + { + chd_ph_config_set_keys_per_bin(mph, keys_per_bin); + } + else if (mph->algo == CMPH_CHD) + { + chd_config_set_keys_per_bin(mph, keys_per_bin); + } +} + +void cmph_config_set_memory_availability(cmph_config_t *mph, cmph_uint32 memory_availability) +{ + if (mph->algo == CMPH_BRZ) + { + brz_config_set_memory_availability(mph, memory_availability); + } +} + +void cmph_config_destroy(cmph_config_t *mph) +{ + if(mph) + { + DEBUGP("Destroying mph with algo %s\n", cmph_names[mph->algo]); + switch (mph->algo) + { + case CMPH_CHM: + chm_config_destroy(mph); + break; + case CMPH_BMZ: /* included -- Fabiano */ + bmz_config_destroy(mph); + break; + case CMPH_BMZ8: /* included -- Fabiano */ + bmz8_config_destroy(mph); + break; + case CMPH_BRZ: /* included -- Fabiano */ + brz_config_destroy(mph); + break; + case CMPH_FCH: /* included -- Fabiano */ + fch_config_destroy(mph); + break; + case CMPH_BDZ: /* included -- Fabiano */ + bdz_config_destroy(mph); + break; + case CMPH_BDZ_PH: /* included -- Fabiano */ + bdz_ph_config_destroy(mph); + break; + case CMPH_CHD_PH: /* included -- Fabiano */ + chd_ph_config_destroy(mph); + break; + case CMPH_CHD: /* included -- Fabiano */ + chd_config_destroy(mph); + break; + default: + assert(0); + } + __config_destroy(mph); + } +} + +void cmph_config_set_verbosity(cmph_config_t *mph, cmph_uint32 verbosity) +{ + mph->verbosity = verbosity; +} + +void cmph_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + switch (mph->algo) + { + case CMPH_CHM: + chm_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_BMZ: /* included -- Fabiano */ + bmz_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_BMZ8: /* included -- Fabiano */ + bmz8_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_BRZ: /* included -- Fabiano */ + brz_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_FCH: /* included -- Fabiano */ + fch_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_BDZ: /* included -- Fabiano */ + bdz_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_BDZ_PH: /* included -- Fabiano */ + bdz_ph_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_CHD_PH: /* included -- Fabiano */ + chd_ph_config_set_hashfuncs(mph, hashfuncs); + break; + case CMPH_CHD: /* included -- Fabiano */ + chd_config_set_hashfuncs(mph, hashfuncs); + break; + default: + break; + } + return; +} +void cmph_config_set_graphsize(cmph_config_t *mph, double c) +{ + mph->c = c; + return; +} + +cmph_t *cmph_new(cmph_config_t *mph) +{ + cmph_t *mphf = NULL; + double c = mph->c; + + DEBUGP("Creating mph with algorithm %s\n", cmph_names[mph->algo]); + switch (mph->algo) + { + case CMPH_CHM: + DEBUGP("Creating chm hash\n"); + mphf = chm_new(mph, c); + break; + case CMPH_BMZ: /* included -- Fabiano */ + DEBUGP("Creating bmz hash\n"); + mphf = bmz_new(mph, c); + break; + case CMPH_BMZ8: /* included -- Fabiano */ + DEBUGP("Creating bmz8 hash\n"); + mphf = bmz8_new(mph, c); + break; + case CMPH_BRZ: /* included -- Fabiano */ + DEBUGP("Creating brz hash\n"); + if (c >= 2.0) brz_config_set_algo(mph, CMPH_FCH); + else brz_config_set_algo(mph, CMPH_BMZ8); + mphf = brz_new(mph, c); + break; + case CMPH_FCH: /* included -- Fabiano */ + DEBUGP("Creating fch hash\n"); + mphf = fch_new(mph, c); + break; + case CMPH_BDZ: /* included -- Fabiano */ + DEBUGP("Creating bdz hash\n"); + mphf = bdz_new(mph, c); + break; + case CMPH_BDZ_PH: /* included -- Fabiano */ + DEBUGP("Creating bdz_ph hash\n"); + mphf = bdz_ph_new(mph, c); + break; + case CMPH_CHD_PH: /* included -- Fabiano */ + DEBUGP("Creating chd_ph hash\n"); + mphf = chd_ph_new(mph, c); + break; + case CMPH_CHD: /* included -- Fabiano */ + DEBUGP("Creating chd hash\n"); + mphf = chd_new(mph, c); + break; + default: + assert(0); + } + return mphf; +} + +int cmph_dump(cmph_t *mphf, FILE *f) +{ + switch (mphf->algo) + { + case CMPH_CHM: + return chm_dump(mphf, f); + case CMPH_BMZ: /* included -- Fabiano */ + return bmz_dump(mphf, f); + case CMPH_BMZ8: /* included -- Fabiano */ + return bmz8_dump(mphf, f); + case CMPH_BRZ: /* included -- Fabiano */ + return brz_dump(mphf, f); + case CMPH_FCH: /* included -- Fabiano */ + return fch_dump(mphf, f); + case CMPH_BDZ: /* included -- Fabiano */ + return bdz_dump(mphf, f); + case CMPH_BDZ_PH: /* included -- Fabiano */ + return bdz_ph_dump(mphf, f); + case CMPH_CHD_PH: /* included -- Fabiano */ + return chd_ph_dump(mphf, f); + case CMPH_CHD: /* included -- Fabiano */ + return chd_dump(mphf, f); + default: + assert(0); + } + assert(0); + return 0; +} +cmph_t *cmph_load(FILE *f) +{ + cmph_t *mphf = NULL; + DEBUGP("Loading mphf generic parts\n"); + mphf = __cmph_load(f); + if (mphf == NULL) return NULL; + DEBUGP("Loading mphf algorithm dependent parts\n"); + + switch (mphf->algo) + { + case CMPH_CHM: + chm_load(f, mphf); + break; + case CMPH_BMZ: /* included -- Fabiano */ + DEBUGP("Loading bmz algorithm dependent parts\n"); + bmz_load(f, mphf); + break; + case CMPH_BMZ8: /* included -- Fabiano */ + DEBUGP("Loading bmz8 algorithm dependent parts\n"); + bmz8_load(f, mphf); + break; + case CMPH_BRZ: /* included -- Fabiano */ + DEBUGP("Loading brz algorithm dependent parts\n"); + brz_load(f, mphf); + break; + case CMPH_FCH: /* included -- Fabiano */ + DEBUGP("Loading fch algorithm dependent parts\n"); + fch_load(f, mphf); + break; + case CMPH_BDZ: /* included -- Fabiano */ + DEBUGP("Loading bdz algorithm dependent parts\n"); + bdz_load(f, mphf); + break; + case CMPH_BDZ_PH: /* included -- Fabiano */ + DEBUGP("Loading bdz_ph algorithm dependent parts\n"); + bdz_ph_load(f, mphf); + break; + case CMPH_CHD_PH: /* included -- Fabiano */ + DEBUGP("Loading chd_ph algorithm dependent parts\n"); + chd_ph_load(f, mphf); + break; + case CMPH_CHD: /* included -- Fabiano */ + DEBUGP("Loading chd algorithm dependent parts\n"); + chd_load(f, mphf); + break; + default: + assert(0); + } + DEBUGP("Loaded mphf\n"); + return mphf; +} + + +cmph_uint32 cmph_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + DEBUGP("mphf algorithm: %u \n", mphf->algo); + switch(mphf->algo) + { + case CMPH_CHM: + return chm_search(mphf, key, keylen); + case CMPH_BMZ: /* included -- Fabiano */ + DEBUGP("bmz algorithm search\n"); + return bmz_search(mphf, key, keylen); + case CMPH_BMZ8: /* included -- Fabiano */ + DEBUGP("bmz8 algorithm search\n"); + return bmz8_search(mphf, key, keylen); + case CMPH_BRZ: /* included -- Fabiano */ + DEBUGP("brz algorithm search\n"); + return brz_search(mphf, key, keylen); + case CMPH_FCH: /* included -- Fabiano */ + DEBUGP("fch algorithm search\n"); + return fch_search(mphf, key, keylen); + case CMPH_BDZ: /* included -- Fabiano */ + DEBUGP("bdz algorithm search\n"); + return bdz_search(mphf, key, keylen); + case CMPH_BDZ_PH: /* included -- Fabiano */ + DEBUGP("bdz_ph algorithm search\n"); + return bdz_ph_search(mphf, key, keylen); + case CMPH_CHD_PH: /* included -- Fabiano */ + DEBUGP("chd_ph algorithm search\n"); + return chd_ph_search(mphf, key, keylen); + case CMPH_CHD: /* included -- Fabiano */ + DEBUGP("chd algorithm search\n"); + return chd_search(mphf, key, keylen); + default: + assert(0); + } + assert(0); + return 0; +} + +cmph_uint32 cmph_size(cmph_t *mphf) +{ + return mphf->size; +} + +void cmph_destroy(cmph_t *mphf) +{ + switch(mphf->algo) + { + case CMPH_CHM: + chm_destroy(mphf); + return; + case CMPH_BMZ: /* included -- Fabiano */ + bmz_destroy(mphf); + return; + case CMPH_BMZ8: /* included -- Fabiano */ + bmz8_destroy(mphf); + return; + case CMPH_BRZ: /* included -- Fabiano */ + brz_destroy(mphf); + return; + case CMPH_FCH: /* included -- Fabiano */ + fch_destroy(mphf); + return; + case CMPH_BDZ: /* included -- Fabiano */ + bdz_destroy(mphf); + return; + case CMPH_BDZ_PH: /* included -- Fabiano */ + bdz_ph_destroy(mphf); + return; + case CMPH_CHD_PH: /* included -- Fabiano */ + chd_ph_destroy(mphf); + return; + case CMPH_CHD: /* included -- Fabiano */ + chd_destroy(mphf); + return; + default: + assert(0); + } + assert(0); + return; +} + + +/** \fn void cmph_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void cmph_pack(cmph_t *mphf, void *packed_mphf) +{ + // packing algorithm type to be used in cmph.c + cmph_uint32 * ptr = (cmph_uint32 *) packed_mphf; + *ptr++ = mphf->algo; + DEBUGP("mphf->algo = %u\n", mphf->algo); + switch(mphf->algo) + { + case CMPH_CHM: + chm_pack(mphf, ptr); + break; + case CMPH_BMZ: /* included -- Fabiano */ + bmz_pack(mphf, ptr); + break; + case CMPH_BMZ8: /* included -- Fabiano */ + bmz8_pack(mphf, ptr); + break; + case CMPH_BRZ: /* included -- Fabiano */ + brz_pack(mphf, ptr); + break; + case CMPH_FCH: /* included -- Fabiano */ + fch_pack(mphf, ptr); + break; + case CMPH_BDZ: /* included -- Fabiano */ + bdz_pack(mphf, ptr); + break; + case CMPH_BDZ_PH: /* included -- Fabiano */ + bdz_ph_pack(mphf, ptr); + break; + case CMPH_CHD_PH: /* included -- Fabiano */ + chd_ph_pack(mphf, ptr); + break; + case CMPH_CHD: /* included -- Fabiano */ + chd_pack(mphf, ptr); + break; + default: + assert(0); + } + return; +} + +/** \fn cmph_uint32 cmph_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 cmph_packed_size(cmph_t *mphf) +{ + switch(mphf->algo) + { + case CMPH_CHM: + return chm_packed_size(mphf); + case CMPH_BMZ: /* included -- Fabiano */ + return bmz_packed_size(mphf); + case CMPH_BMZ8: /* included -- Fabiano */ + return bmz8_packed_size(mphf); + case CMPH_BRZ: /* included -- Fabiano */ + return brz_packed_size(mphf); + case CMPH_FCH: /* included -- Fabiano */ + return fch_packed_size(mphf); + case CMPH_BDZ: /* included -- Fabiano */ + return bdz_packed_size(mphf); + case CMPH_BDZ_PH: /* included -- Fabiano */ + return bdz_ph_packed_size(mphf); + case CMPH_CHD_PH: /* included -- Fabiano */ + return chd_ph_packed_size(mphf); + case CMPH_CHD: /* included -- Fabiano */ + return chd_packed_size(mphf); + default: + assert(0); + } + return 0; // FAILURE +} + +/** cmph_uint32 cmph_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 cmph_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + cmph_uint32 *ptr = (cmph_uint32 *)packed_mphf; +// fprintf(stderr, "algo:%u\n", *ptr); + switch(*ptr) + { + case CMPH_CHM: + return chm_search_packed(++ptr, key, keylen); + case CMPH_BMZ: /* included -- Fabiano */ + return bmz_search_packed(++ptr, key, keylen); + case CMPH_BMZ8: /* included -- Fabiano */ + return bmz8_search_packed(++ptr, key, keylen); + case CMPH_BRZ: /* included -- Fabiano */ + return brz_search_packed(++ptr, key, keylen); + case CMPH_FCH: /* included -- Fabiano */ + return fch_search_packed(++ptr, key, keylen); + case CMPH_BDZ: /* included -- Fabiano */ + return bdz_search_packed(++ptr, key, keylen); + case CMPH_BDZ_PH: /* included -- Fabiano */ + return bdz_ph_search_packed(++ptr, key, keylen); + case CMPH_CHD_PH: /* included -- Fabiano */ + return chd_ph_search_packed(++ptr, key, keylen); + case CMPH_CHD: /* included -- Fabiano */ + return chd_search_packed(++ptr, key, keylen); + default: + assert(0); + } + return 0; // FAILURE +} diff --git a/girepository/cmph/cmph.h b/girepository/cmph/cmph.h new file mode 100644 index 000000000..1bc009e19 --- /dev/null +++ b/girepository/cmph/cmph.h @@ -0,0 +1,112 @@ +#ifndef __CMPH_H__ +#define __CMPH_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "cmph_types.h" + +typedef struct __config_t cmph_config_t; +typedef struct __cmph_t cmph_t; + +typedef struct +{ + void *data; + cmph_uint32 nkeys; + int (*read)(void *, char **, cmph_uint32 *); + void (*dispose)(void *, char *, cmph_uint32); + void (*rewind)(void *); +} cmph_io_adapter_t; + +/** Adapter pattern API **/ +/* please call free() in the created adapters */ +cmph_io_adapter_t *cmph_io_nlfile_adapter(FILE * keys_fd); +void cmph_io_nlfile_adapter_destroy(cmph_io_adapter_t * key_source); + +cmph_io_adapter_t *cmph_io_nlnkfile_adapter(FILE * keys_fd, cmph_uint32 nkeys); +void cmph_io_nlnkfile_adapter_destroy(cmph_io_adapter_t * key_source); + +cmph_io_adapter_t *cmph_io_vector_adapter(char ** vector, cmph_uint32 nkeys); +void cmph_io_vector_adapter_destroy(cmph_io_adapter_t * key_source); + +cmph_io_adapter_t *cmph_io_byte_vector_adapter(cmph_uint8 ** vector, cmph_uint32 nkeys); +void cmph_io_byte_vector_adapter_destroy(cmph_io_adapter_t * key_source); + +cmph_io_adapter_t *cmph_io_struct_vector_adapter(void * vector, + cmph_uint32 struct_size, + cmph_uint32 key_offset, + cmph_uint32 key_len, + cmph_uint32 nkeys); + +void cmph_io_struct_vector_adapter_destroy(cmph_io_adapter_t * key_source); + +/** Hash configuration API **/ +cmph_config_t *cmph_config_new(cmph_io_adapter_t *key_source); +void cmph_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void cmph_config_set_verbosity(cmph_config_t *mph, cmph_uint32 verbosity); +void cmph_config_set_graphsize(cmph_config_t *mph, double c); +void cmph_config_set_algo(cmph_config_t *mph, CMPH_ALGO algo); +void cmph_config_set_tmp_dir(cmph_config_t *mph, cmph_uint8 *tmp_dir); +void cmph_config_set_mphf_fd(cmph_config_t *mph, FILE *mphf_fd); +void cmph_config_set_b(cmph_config_t *mph, cmph_uint32 b); +void cmph_config_set_keys_per_bin(cmph_config_t *mph, cmph_uint32 keys_per_bin); +void cmph_config_set_memory_availability(cmph_config_t *mph, cmph_uint32 memory_availability); +void cmph_config_destroy(cmph_config_t *mph); + +/** Hash API **/ +cmph_t *cmph_new(cmph_config_t *mph); + +/** cmph_uint32 cmph_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + * \brief Computes the mphf value. + * \param mphf pointer to the resulting function + * \param key is the key to be hashed + * \param keylen is the key legth in bytes + * \return The mphf value + */ +cmph_uint32 cmph_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +cmph_uint32 cmph_size(cmph_t *mphf); +void cmph_destroy(cmph_t *mphf); + +/** Hash serialization/deserialization */ +int cmph_dump(cmph_t *mphf, FILE *f); +cmph_t *cmph_load(FILE *f); + +/** \fn void cmph_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the + * \param resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void cmph_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 cmph_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 cmph_packed_size(cmph_t *mphf); + +/** cmph_uint32 cmph_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 cmph_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +// TIMING functions. To use the macro CMPH_TIMING must be defined +#include "cmph_time.h" + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/girepository/cmph/cmph_structs.c b/girepository/cmph/cmph_structs.c new file mode 100644 index 000000000..9ecf5fc0e --- /dev/null +++ b/girepository/cmph/cmph_structs.c @@ -0,0 +1,76 @@ +#include "cmph_structs.h" + +#include +#include + +//#define DEBUG +#include "debug.h" + +cmph_config_t *__config_new(cmph_io_adapter_t *key_source) +{ + cmph_config_t *mph = (cmph_config_t *)malloc(sizeof(cmph_config_t)); + memset(mph, 0, sizeof(cmph_config_t)); + if (mph == NULL) return NULL; + mph->key_source = key_source; + mph->verbosity = 0; + mph->data = NULL; + mph->c = 0; + return mph; +} + +void __config_destroy(cmph_config_t *mph) +{ + free(mph); +} + +void __cmph_dump(cmph_t *mphf, FILE *fd) +{ + register size_t nbytes; + nbytes = fwrite(cmph_names[mphf->algo], (size_t)(strlen(cmph_names[mphf->algo]) + 1), (size_t)1, fd); + nbytes = fwrite(&(mphf->size), sizeof(mphf->size), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + } +} +cmph_t *__cmph_load(FILE *f) +{ + cmph_t *mphf = NULL; + cmph_uint32 i; + char algo_name[BUFSIZ]; + char *ptr = algo_name; + CMPH_ALGO algo = CMPH_COUNT; + register size_t nbytes; + + DEBUGP("Loading mphf\n"); + while(1) + { + size_t c = fread(ptr, (size_t)1, (size_t)1, f); + if (c != 1) return NULL; + if (*ptr == 0) break; + ++ptr; + } + for(i = 0; i < CMPH_COUNT; ++i) + { + if (strcmp(algo_name, cmph_names[i]) == 0) + { + algo = i; + } + } + if (algo == CMPH_COUNT) + { + DEBUGP("Algorithm %s not found\n", algo_name); + return NULL; + } + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = algo; + nbytes = fread(&(mphf->size), sizeof(mphf->size), (size_t)1, f); + mphf->data = NULL; + DEBUGP("Algorithm is %s and mphf is sized %u\n", cmph_names[algo], mphf->size); + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + } + + return mphf; +} + + diff --git a/girepository/cmph/cmph_structs.h b/girepository/cmph/cmph_structs.h new file mode 100644 index 000000000..88fafb6ce --- /dev/null +++ b/girepository/cmph/cmph_structs.h @@ -0,0 +1,33 @@ +#ifndef __CMPH_STRUCTS_H__ +#define __CMPH_STRUCTS_H__ + +#include "cmph.h" + +/** Hash generation algorithm data + */ +struct __config_t +{ + CMPH_ALGO algo; + cmph_io_adapter_t *key_source; + cmph_uint32 verbosity; + double c; + void *data; // algorithm dependent data +}; + +/** Hash querying algorithm data + */ +struct __cmph_t +{ + CMPH_ALGO algo; + cmph_uint32 size; + cmph_io_adapter_t *key_source; + void *data; // algorithm dependent data +}; + +cmph_config_t *__config_new(cmph_io_adapter_t *key_source); +void __config_destroy(cmph_config_t*); +void __cmph_dump(cmph_t *mphf, FILE *); +cmph_t *__cmph_load(FILE *f); + + +#endif diff --git a/girepository/cmph/cmph_time.h b/girepository/cmph/cmph_time.h new file mode 100644 index 000000000..5d5d89330 --- /dev/null +++ b/girepository/cmph/cmph_time.h @@ -0,0 +1,62 @@ +#ifdef ELAPSED_TIME_IN_SECONDS +#undef ELAPSED_TIME_IN_SECONDS +#endif + +#ifdef ELAPSED_TIME_IN_uSECONDS +#undef ELAPSED_TIME_IN_uSECONDS +#endif + +#ifdef __GNUC__ + #include + #ifndef WIN32 + #include + #endif +#endif + +#ifdef __GNUC__ + #ifndef __CMPH_TIME_H__ + #define __CMPH_TIME_H__ + static inline void elapsed_time_in_seconds(double * elapsed_time) + { + struct timeval e_time; + if (gettimeofday(&e_time, NULL) < 0) { + return; + } + *elapsed_time = (double)e_time.tv_sec + ((double)e_time.tv_usec/1000000.0); + } + static inline void dummy_elapsed_time_in_seconds(double * elapsed_time) + { + (void) elapsed_time; + } + static inline void elapsed_time_in_useconds(cmph_uint64 * elapsed_time) + { + struct timeval e_time; + if (gettimeofday(&e_time, NULL) < 0) { + return; + } + *elapsed_time = (cmph_uint64)(e_time.tv_sec*1000000 + e_time.tv_usec); + } + static inline void dummy_elapsed_time_in_useconds(cmph_uint64 * elapsed_time) + { + (void) elapsed_time; + } + #endif +#endif + +#ifdef CMPH_TIMING + #ifdef __GNUC__ + #define ELAPSED_TIME_IN_SECONDS elapsed_time_in_seconds + #define ELAPSED_TIME_IN_uSECONDS elapsed_time_in_useconds + #else + #define ELAPSED_TIME_IN_SECONDS dummy_elapsed_time_in_seconds + #define ELAPSED_TIME_IN_uSECONDS dummy_elapsed_time_in_useconds + #endif +#else + #ifdef __GNUC__ + #define ELAPSED_TIME_IN_SECONDS + #define ELAPSED_TIME_IN_uSECONDS + #else + #define ELAPSED_TIME_IN_SECONDS dummy_elapsed_time_in_seconds + #define ELAPSED_TIME_IN_uSECONDS dummy_elapsed_time_in_useconds + #endif +#endif diff --git a/girepository/cmph/cmph_types.h b/girepository/cmph/cmph_types.h new file mode 100644 index 000000000..288323587 --- /dev/null +++ b/girepository/cmph/cmph_types.h @@ -0,0 +1,25 @@ +#include + +#ifndef __CMPH_TYPES_H__ +#define __CMPH_TYPES_H__ + +typedef gint8 cmph_int8; +typedef guint8 cmph_uint8; + +typedef gint16 cmph_int16; +typedef guint16 cmph_uint16; + +typedef gint32 cmph_int32; +typedef guint32 cmph_uint32; + +typedef gint64 cmph_int64; +typedef guint64 cmph_uint64; + +typedef enum { CMPH_HASH_JENKINS, CMPH_HASH_COUNT } CMPH_HASH; +extern const char *cmph_hash_names[]; +typedef enum { CMPH_BMZ, CMPH_BMZ8, CMPH_CHM, CMPH_BRZ, CMPH_FCH, + CMPH_BDZ, CMPH_BDZ_PH, + CMPH_CHD_PH, CMPH_CHD, CMPH_COUNT } CMPH_ALGO; +extern const char *cmph_names[]; + +#endif diff --git a/girepository/cmph/compressed_rank.c b/girepository/cmph/compressed_rank.c new file mode 100644 index 000000000..8019dbe5c --- /dev/null +++ b/girepository/cmph/compressed_rank.c @@ -0,0 +1,327 @@ +#include +#include +#include +#include +#include"compressed_rank.h" +#include"bitbool.h" +// #define DEBUG +#include"debug.h" +static inline cmph_uint32 compressed_rank_i_log2(cmph_uint32 x) +{ + register cmph_uint32 res = 0; + + while(x > 1) + { + x >>= 1; + res++; + } + return res; +}; + +void compressed_rank_init(compressed_rank_t * cr) +{ + cr->max_val = 0; + cr->n = 0; + cr->rem_r = 0; + select_init(&cr->sel); + cr->vals_rems = 0; +} + +void compressed_rank_destroy(compressed_rank_t * cr) +{ + free(cr->vals_rems); + cr->vals_rems = 0; + select_destroy(&cr->sel); +} + +void compressed_rank_generate(compressed_rank_t * cr, cmph_uint32 * vals_table, cmph_uint32 n) +{ + register cmph_uint32 i,j; + register cmph_uint32 rems_mask; + register cmph_uint32 * select_vec = 0; + cr->n = n; + cr->max_val = vals_table[cr->n - 1]; + cr->rem_r = compressed_rank_i_log2(cr->max_val/cr->n); + if(cr->rem_r == 0) + { + cr->rem_r = 1; + } + select_vec = (cmph_uint32 *) calloc(cr->max_val >> cr->rem_r, sizeof(cmph_uint32)); + cr->vals_rems = (cmph_uint32 *) calloc(BITS_TABLE_SIZE(cr->n, cr->rem_r), sizeof(cmph_uint32)); + rems_mask = (1U << cr->rem_r) - 1U; + + for(i = 0; i < cr->n; i++) + { + set_bits_value(cr->vals_rems, i, vals_table[i] & rems_mask, cr->rem_r, rems_mask); + } + + for(i = 1, j = 0; i <= cr->max_val >> cr->rem_r; i++) + { + while(i > (vals_table[j] >> cr->rem_r)) + { + j++; + } + select_vec[i - 1] = j; + }; + + + // FABIANO: before it was (cr->total_length >> cr->rem_r) + 1. But I wiped out the + 1 because + // I changed the select structure to work up to m, instead of up to m - 1. + select_generate(&cr->sel, select_vec, cr->max_val >> cr->rem_r, cr->n); + + free(select_vec); +} + +cmph_uint32 compressed_rank_query(compressed_rank_t * cr, cmph_uint32 idx) +{ + register cmph_uint32 rems_mask; + register cmph_uint32 val_quot, val_rem; + register cmph_uint32 sel_res, rank; + + if(idx > cr->max_val) + { + return cr->n; + } + + val_quot = idx >> cr->rem_r; + rems_mask = (1U << cr->rem_r) - 1U; + val_rem = idx & rems_mask; + if(val_quot == 0) + { + rank = sel_res = 0; + } + else + { + sel_res = select_query(&cr->sel, val_quot - 1) + 1; + rank = sel_res - val_quot; + } + + do + { + if(GETBIT32(cr->sel.bits_vec, sel_res)) + { + break; + } + if(get_bits_value(cr->vals_rems, rank, cr->rem_r, rems_mask) >= val_rem) + { + break; + } + sel_res++; + rank++; + } while(1); + + return rank; +} + +cmph_uint32 compressed_rank_get_space_usage(compressed_rank_t * cr) +{ + register cmph_uint32 space_usage = select_get_space_usage(&cr->sel); + space_usage += BITS_TABLE_SIZE(cr->n, cr->rem_r)*(cmph_uint32)sizeof(cmph_uint32)*8; + space_usage += 3*(cmph_uint32)sizeof(cmph_uint32)*8; + return space_usage; +} + +void compressed_rank_dump(compressed_rank_t * cr, char **buf, cmph_uint32 *buflen) +{ + register cmph_uint32 sel_size = select_packed_size(&(cr->sel)); + register cmph_uint32 vals_rems_size = BITS_TABLE_SIZE(cr->n, cr->rem_r) * (cmph_uint32)sizeof(cmph_uint32); + register cmph_uint32 pos = 0; + char * buf_sel = 0; + cmph_uint32 buflen_sel = 0; +#ifdef DEBUG + cmph_uint32 i; +#endif + + *buflen = 4*(cmph_uint32)sizeof(cmph_uint32) + sel_size + vals_rems_size; + + DEBUGP("sel_size = %u\n", sel_size); + DEBUGP("vals_rems_size = %u\n", vals_rems_size); + + *buf = (char *)calloc(*buflen, sizeof(char)); + + if (!*buf) + { + *buflen = UINT_MAX; + return; + } + + // dumping max_val, n and rem_r + memcpy(*buf, &(cr->max_val), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("max_val = %u\n", cr->max_val); + + memcpy(*buf + pos, &(cr->n), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("n = %u\n", cr->n); + + memcpy(*buf + pos, &(cr->rem_r), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("rem_r = %u\n", cr->rem_r); + + // dumping sel + select_dump(&cr->sel, &buf_sel, &buflen_sel); + memcpy(*buf + pos, &buflen_sel, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("buflen_sel = %u\n", buflen_sel); + + memcpy(*buf + pos, buf_sel, buflen_sel); + + #ifdef DEBUG + i = 0; + for(i = 0; i < buflen_sel; i++) + { + DEBUGP("pos = %u -- buf_sel[%u] = %u\n", pos, i, *(*buf + pos + i)); + } + #endif + pos += buflen_sel; + + free(buf_sel); + + // dumping vals_rems + memcpy(*buf + pos, cr->vals_rems, vals_rems_size); + #ifdef DEBUG + for(i = 0; i < vals_rems_size; i++) + { + DEBUGP("pos = %u -- vals_rems_size = %u -- vals_rems[%u] = %u\n", pos, vals_rems_size, i, *(*buf + pos + i)); + } + #endif + pos += vals_rems_size; + + DEBUGP("Dumped compressed rank structure with size %u bytes\n", *buflen); +} + +void compressed_rank_load(compressed_rank_t * cr, const char *buf, cmph_uint32 buflen) +{ + register cmph_uint32 pos = 0; + cmph_uint32 buflen_sel = 0; + register cmph_uint32 vals_rems_size = 0; +#ifdef DEBUG + cmph_uint32 i; +#endif + + // loading max_val, n, and rem_r + memcpy(&(cr->max_val), buf, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("max_val = %u\n", cr->max_val); + + memcpy(&(cr->n), buf + pos, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("n = %u\n", cr->n); + + memcpy(&(cr->rem_r), buf + pos, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("rem_r = %u\n", cr->rem_r); + + // loading sel + memcpy(&buflen_sel, buf + pos, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("buflen_sel = %u\n", buflen_sel); + + select_load(&cr->sel, buf + pos, buflen_sel); + #ifdef DEBUG + i = 0; + for(i = 0; i < buflen_sel; i++) + { + DEBUGP("pos = %u -- buf_sel[%u] = %u\n", pos, i, *(buf + pos + i)); + } + #endif + pos += buflen_sel; + + // loading vals_rems + if(cr->vals_rems) + { + free(cr->vals_rems); + } + vals_rems_size = BITS_TABLE_SIZE(cr->n, cr->rem_r); + cr->vals_rems = (cmph_uint32 *) calloc(vals_rems_size, sizeof(cmph_uint32)); + vals_rems_size *= 4; + memcpy(cr->vals_rems, buf + pos, vals_rems_size); + + #ifdef DEBUG + for(i = 0; i < vals_rems_size; i++) + { + DEBUGP("pos = %u -- vals_rems_size = %u -- vals_rems[%u] = %u\n", pos, vals_rems_size, i, *(buf + pos + i)); + } + #endif + pos += vals_rems_size; + + DEBUGP("Loaded compressed rank structure with size %u bytes\n", buflen); +} + + + +void compressed_rank_pack(compressed_rank_t *cr, void *cr_packed) +{ + if (cr && cr_packed) + { + char *buf = NULL; + cmph_uint32 buflen = 0; + compressed_rank_dump(cr, &buf, &buflen); + memcpy(cr_packed, buf, buflen); + free(buf); + } +} + +cmph_uint32 compressed_rank_packed_size(compressed_rank_t *cr) +{ + register cmph_uint32 sel_size = select_packed_size(&cr->sel); + register cmph_uint32 vals_rems_size = BITS_TABLE_SIZE(cr->n, cr->rem_r) * (cmph_uint32)sizeof(cmph_uint32); + return 4 * (cmph_uint32)sizeof(cmph_uint32) + sel_size + vals_rems_size; +} + +cmph_uint32 compressed_rank_query_packed(void * cr_packed, cmph_uint32 idx) +{ + // unpacking cr_packed + register cmph_uint32 *ptr = (cmph_uint32 *)cr_packed; + register cmph_uint32 max_val = *ptr++; + register cmph_uint32 n = *ptr++; + register cmph_uint32 rem_r = *ptr++; + register cmph_uint32 buflen_sel = *ptr++; + register cmph_uint32 * sel_packed = ptr; + + register cmph_uint32 * bits_vec = sel_packed + 2; // skipping n and m + + register cmph_uint32 * vals_rems = (ptr += (buflen_sel >> 2)); + + // compressed sequence query computation + register cmph_uint32 rems_mask; + register cmph_uint32 val_quot, val_rem; + register cmph_uint32 sel_res, rank; + + if(idx > max_val) + { + return n; + } + + val_quot = idx >> rem_r; + rems_mask = (1U << rem_r) - 1U; + val_rem = idx & rems_mask; + if(val_quot == 0) + { + rank = sel_res = 0; + } + else + { + sel_res = select_query_packed(sel_packed, val_quot - 1) + 1; + rank = sel_res - val_quot; + } + + do + { + if(GETBIT32(bits_vec, sel_res)) + { + break; + } + if(get_bits_value(vals_rems, rank, rem_r, rems_mask) >= val_rem) + { + break; + } + sel_res++; + rank++; + } while(1); + + return rank; +} + + + diff --git a/girepository/cmph/compressed_rank.h b/girepository/cmph/compressed_rank.h new file mode 100644 index 000000000..bfe930dd3 --- /dev/null +++ b/girepository/cmph/compressed_rank.h @@ -0,0 +1,55 @@ +#ifndef __CMPH_COMPRESSED_RANK_H__ +#define __CMPH_COMPRESSED_RANK_H__ + +#include "select.h" + +struct _compressed_rank_t +{ + cmph_uint32 max_val; + cmph_uint32 n; // number of values stored in vals_rems + // The length in bits of each value is decomposed into two compnents: the lg(n) MSBs are stored in rank_select data structure + // the remaining LSBs are stored in a table of n cells, each one of rem_r bits. + cmph_uint32 rem_r; + select_t sel; + cmph_uint32 * vals_rems; +}; + +typedef struct _compressed_rank_t compressed_rank_t; + +void compressed_rank_init(compressed_rank_t * cr); + +void compressed_rank_destroy(compressed_rank_t * cr); + +void compressed_rank_generate(compressed_rank_t * cr, cmph_uint32 * vals_table, cmph_uint32 n); + +cmph_uint32 compressed_rank_query(compressed_rank_t * cr, cmph_uint32 idx); + +cmph_uint32 compressed_rank_get_space_usage(compressed_rank_t * cr); + +void compressed_rank_dump(compressed_rank_t * cr, char **buf, cmph_uint32 *buflen); + +void compressed_rank_load(compressed_rank_t * cr, const char *buf, cmph_uint32 buflen); + + +/** \fn void compressed_rank_pack(compressed_rank_t *cr, void *cr_packed); + * \brief Support the ability to pack a compressed_rank structure into a preallocated contiguous memory space pointed by cr_packed. + * \param cr points to the compressed_rank structure + * \param cr_packed pointer to the contiguous memory area used to store the compressed_rank structure. The size of cr_packed must be at least @see compressed_rank_packed_size + */ +void compressed_rank_pack(compressed_rank_t *cr, void *cr_packed); + +/** \fn cmph_uint32 compressed_rank_packed_size(compressed_rank_t *cr); + * \brief Return the amount of space needed to pack a compressed_rank structure. + * \return the size of the packed compressed_rank structure or zero for failures + */ +cmph_uint32 compressed_rank_packed_size(compressed_rank_t *cr); + + +/** \fn cmph_uint32 compressed_rank_query_packed(void * cr_packed, cmph_uint32 idx); + * \param cr_packed is a pointer to a contiguous memory area + * \param idx is an index to compute the rank + * \return an integer that represents the compressed_rank value. + */ +cmph_uint32 compressed_rank_query_packed(void * cr_packed, cmph_uint32 idx); + +#endif diff --git a/girepository/cmph/compressed_seq.c b/girepository/cmph/compressed_seq.c new file mode 100644 index 000000000..e5191fd5a --- /dev/null +++ b/girepository/cmph/compressed_seq.c @@ -0,0 +1,384 @@ +#include "compressed_seq.h" +#include +#include +#include +#include +#include + +#include "bitbool.h" + +// #define DEBUG +#include "debug.h" + +static inline cmph_uint32 compressed_seq_i_log2(cmph_uint32 x) +{ + register cmph_uint32 res = 0; + + while(x > 1) + { + x >>= 1; + res++; + } + return res; +}; + +void compressed_seq_init(compressed_seq_t * cs) +{ + select_init(&cs->sel); + cs->n = 0; + cs->rem_r = 0; + cs->length_rems = 0; + cs->total_length = 0; + cs->store_table = 0; +} + +void compressed_seq_destroy(compressed_seq_t * cs) +{ + free(cs->store_table); + cs->store_table = 0; + free(cs->length_rems); + cs->length_rems = 0; + select_destroy(&cs->sel); +}; + + +void compressed_seq_generate(compressed_seq_t * cs, cmph_uint32 * vals_table, cmph_uint32 n) +{ + register cmph_uint32 i; + // lengths: represents lengths of encoded values + register cmph_uint32 * lengths = (cmph_uint32 *)calloc(n, sizeof(cmph_uint32)); + register cmph_uint32 rems_mask; + register cmph_uint32 stored_value; + + cs->n = n; + cs->total_length = 0; + + for(i = 0; i < cs->n; i++) + { + if(vals_table[i] == 0) + { + lengths[i] = 0; + } + else + { + lengths[i] = compressed_seq_i_log2(vals_table[i] + 1); + cs->total_length += lengths[i]; + }; + }; + + if(cs->store_table) + { + free(cs->store_table); + } + cs->store_table = (cmph_uint32 *) calloc(((cs->total_length + 31) >> 5), sizeof(cmph_uint32)); + cs->total_length = 0; + + for(i = 0; i < cs->n; i++) + { + if(vals_table[i] == 0) + continue; + stored_value = vals_table[i] - ((1U << lengths[i]) - 1U); + set_bits_at_pos(cs->store_table, cs->total_length, stored_value, lengths[i]); + cs->total_length += lengths[i]; + }; + + cs->rem_r = compressed_seq_i_log2(cs->total_length/cs->n); + + if(cs->rem_r == 0) + { + cs->rem_r = 1; + } + + if(cs->length_rems) + { + free(cs->length_rems); + } + + cs->length_rems = (cmph_uint32 *) calloc(BITS_TABLE_SIZE(cs->n, cs->rem_r), sizeof(cmph_uint32)); + + rems_mask = (1U << cs->rem_r) - 1U; + cs->total_length = 0; + + for(i = 0; i < cs->n; i++) + { + cs->total_length += lengths[i]; + set_bits_value(cs->length_rems, i, cs->total_length & rems_mask, cs->rem_r, rems_mask); + lengths[i] = cs->total_length >> cs->rem_r; + }; + + select_init(&cs->sel); + + // FABIANO: before it was (cs->total_length >> cs->rem_r) + 1. But I wiped out the + 1 because + // I changed the select structure to work up to m, instead of up to m - 1. + select_generate(&cs->sel, lengths, cs->n, (cs->total_length >> cs->rem_r)); + + free(lengths); +}; + +cmph_uint32 compressed_seq_get_space_usage(compressed_seq_t * cs) +{ + register cmph_uint32 space_usage = select_get_space_usage(&cs->sel); + space_usage += ((cs->total_length + 31) >> 5) * (cmph_uint32)sizeof(cmph_uint32) * 8; + space_usage += BITS_TABLE_SIZE(cs->n, cs->rem_r) * (cmph_uint32)sizeof(cmph_uint32) * 8; + return 4 * (cmph_uint32)sizeof(cmph_uint32) * 8 + space_usage; +} + +cmph_uint32 compressed_seq_query(compressed_seq_t * cs, cmph_uint32 idx) +{ + register cmph_uint32 enc_idx, enc_length; + register cmph_uint32 rems_mask; + register cmph_uint32 stored_value; + register cmph_uint32 sel_res; + + assert(idx < cs->n); // FABIANO ADDED + + rems_mask = (1U << cs->rem_r) - 1U; + + if(idx == 0) + { + enc_idx = 0; + sel_res = select_query(&cs->sel, idx); + } + else + { + sel_res = select_query(&cs->sel, idx - 1); + + enc_idx = (sel_res - (idx - 1)) << cs->rem_r; + enc_idx += get_bits_value(cs->length_rems, idx-1, cs->rem_r, rems_mask); + + sel_res = select_next_query(&cs->sel, sel_res); + }; + + enc_length = (sel_res - idx) << cs->rem_r; + enc_length += get_bits_value(cs->length_rems, idx, cs->rem_r, rems_mask); + enc_length -= enc_idx; + if(enc_length == 0) + return 0; + + stored_value = get_bits_at_pos(cs->store_table, enc_idx, enc_length); + return stored_value + ((1U << enc_length) - 1U); +}; + +void compressed_seq_dump(compressed_seq_t * cs, char ** buf, cmph_uint32 * buflen) +{ + register cmph_uint32 sel_size = select_packed_size(&(cs->sel)); + register cmph_uint32 length_rems_size = BITS_TABLE_SIZE(cs->n, cs->rem_r) * 4; + register cmph_uint32 store_table_size = ((cs->total_length + 31) >> 5) * 4; + register cmph_uint32 pos = 0; + char * buf_sel = 0; + cmph_uint32 buflen_sel = 0; +#ifdef DEBUG + cmph_uint32 i; +#endif + + *buflen = 4*(cmph_uint32)sizeof(cmph_uint32) + sel_size + length_rems_size + store_table_size; + + DEBUGP("sel_size = %u\n", sel_size); + DEBUGP("length_rems_size = %u\n", length_rems_size); + DEBUGP("store_table_size = %u\n", store_table_size); + *buf = (char *)calloc(*buflen, sizeof(char)); + + if (!*buf) + { + *buflen = UINT_MAX; + return; + } + + // dumping n, rem_r and total_length + memcpy(*buf, &(cs->n), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("n = %u\n", cs->n); + + memcpy(*buf + pos, &(cs->rem_r), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("rem_r = %u\n", cs->rem_r); + + memcpy(*buf + pos, &(cs->total_length), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("total_length = %u\n", cs->total_length); + + + // dumping sel + select_dump(&cs->sel, &buf_sel, &buflen_sel); + memcpy(*buf + pos, &buflen_sel, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("buflen_sel = %u\n", buflen_sel); + + memcpy(*buf + pos, buf_sel, buflen_sel); + #ifdef DEBUG + i = 0; + for(i = 0; i < buflen_sel; i++) + { + DEBUGP("pos = %u -- buf_sel[%u] = %u\n", pos, i, *(*buf + pos + i)); + } + #endif + pos += buflen_sel; + + free(buf_sel); + + // dumping length_rems + memcpy(*buf + pos, cs->length_rems, length_rems_size); + #ifdef DEBUG + for(i = 0; i < length_rems_size; i++) + { + DEBUGP("pos = %u -- length_rems_size = %u -- length_rems[%u] = %u\n", pos, length_rems_size, i, *(*buf + pos + i)); + } + #endif + pos += length_rems_size; + + // dumping store_table + memcpy(*buf + pos, cs->store_table, store_table_size); + + #ifdef DEBUG + for(i = 0; i < store_table_size; i++) + { + DEBUGP("pos = %u -- store_table_size = %u -- store_table[%u] = %u\n", pos, store_table_size, i, *(*buf + pos + i)); + } + #endif + DEBUGP("Dumped compressed sequence structure with size %u bytes\n", *buflen); +} + +void compressed_seq_load(compressed_seq_t * cs, const char * buf, cmph_uint32 buflen) +{ + register cmph_uint32 pos = 0; + cmph_uint32 buflen_sel = 0; + register cmph_uint32 length_rems_size = 0; + register cmph_uint32 store_table_size = 0; +#ifdef DEBUG + cmph_uint32 i; +#endif + + // loading n, rem_r and total_length + memcpy(&(cs->n), buf, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("n = %u\n", cs->n); + + memcpy(&(cs->rem_r), buf + pos, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("rem_r = %u\n", cs->rem_r); + + memcpy(&(cs->total_length), buf + pos, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("total_length = %u\n", cs->total_length); + + // loading sel + memcpy(&buflen_sel, buf + pos, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + DEBUGP("buflen_sel = %u\n", buflen_sel); + + select_load(&cs->sel, buf + pos, buflen_sel); + #ifdef DEBUG + i = 0; + for(i = 0; i < buflen_sel; i++) + { + DEBUGP("pos = %u -- buf_sel[%u] = %u\n", pos, i, *(buf + pos + i)); + } + #endif + pos += buflen_sel; + + // loading length_rems + if(cs->length_rems) + { + free(cs->length_rems); + } + length_rems_size = BITS_TABLE_SIZE(cs->n, cs->rem_r); + cs->length_rems = (cmph_uint32 *) calloc(length_rems_size, sizeof(cmph_uint32)); + length_rems_size *= 4; + memcpy(cs->length_rems, buf + pos, length_rems_size); + + #ifdef DEBUG + for(i = 0; i < length_rems_size; i++) + { + DEBUGP("pos = %u -- length_rems_size = %u -- length_rems[%u] = %u\n", pos, length_rems_size, i, *(buf + pos + i)); + } + #endif + pos += length_rems_size; + + // loading store_table + store_table_size = ((cs->total_length + 31) >> 5); + if(cs->store_table) + { + free(cs->store_table); + } + cs->store_table = (cmph_uint32 *) calloc(store_table_size, sizeof(cmph_uint32)); + store_table_size *= 4; + memcpy(cs->store_table, buf + pos, store_table_size); + + #ifdef DEBUG + for(i = 0; i < store_table_size; i++) + { + DEBUGP("pos = %u -- store_table_size = %u -- store_table[%u] = %u\n", pos, store_table_size, i, *(buf + pos + i)); + } + #endif + + DEBUGP("Loaded compressed sequence structure with size %u bytes\n", buflen); +} + +void compressed_seq_pack(compressed_seq_t *cs, void *cs_packed) +{ + if (cs && cs_packed) + { + char *buf = NULL; + cmph_uint32 buflen = 0; + compressed_seq_dump(cs, &buf, &buflen); + memcpy(cs_packed, buf, buflen); + free(buf); + } + +} + +cmph_uint32 compressed_seq_packed_size(compressed_seq_t *cs) +{ + register cmph_uint32 sel_size = select_packed_size(&cs->sel); + register cmph_uint32 store_table_size = ((cs->total_length + 31) >> 5) * (cmph_uint32)sizeof(cmph_uint32); + register cmph_uint32 length_rems_size = BITS_TABLE_SIZE(cs->n, cs->rem_r) * (cmph_uint32)sizeof(cmph_uint32); + return 4 * (cmph_uint32)sizeof(cmph_uint32) + sel_size + store_table_size + length_rems_size; +} + + +cmph_uint32 compressed_seq_query_packed(void * cs_packed, cmph_uint32 idx) +{ + // unpacking cs_packed + register cmph_uint32 *ptr = (cmph_uint32 *)cs_packed; + register cmph_uint32 n = *ptr++; + register cmph_uint32 rem_r = *ptr++; + register cmph_uint32 buflen_sel, length_rems_size, enc_idx, enc_length; + // compressed sequence query computation + register cmph_uint32 rems_mask, stored_value, sel_res; + register cmph_uint32 *sel_packed, *length_rems, *store_table; + + ptr++; // skipping total_length +// register cmph_uint32 total_length = *ptr++; + buflen_sel = *ptr++; + sel_packed = ptr; + length_rems = (ptr += (buflen_sel >> 2)); + length_rems_size = BITS_TABLE_SIZE(n, rem_r); + store_table = (ptr += length_rems_size); + + + rems_mask = (1U << rem_r) - 1U; + + if(idx == 0) + { + enc_idx = 0; + sel_res = select_query_packed(sel_packed, idx); + } + else + { + sel_res = select_query_packed(sel_packed, idx - 1); + + enc_idx = (sel_res - (idx - 1)) << rem_r; + enc_idx += get_bits_value(length_rems, idx-1, rem_r, rems_mask); + + sel_res = select_next_query_packed(sel_packed, sel_res); + }; + + enc_length = (sel_res - idx) << rem_r; + enc_length += get_bits_value(length_rems, idx, rem_r, rems_mask); + enc_length -= enc_idx; + if(enc_length == 0) + return 0; + + stored_value = get_bits_at_pos(store_table, enc_idx, enc_length); + return stored_value + ((1U << enc_length) - 1U); +} diff --git a/girepository/cmph/compressed_seq.h b/girepository/cmph/compressed_seq.h new file mode 100644 index 000000000..8d87fc700 --- /dev/null +++ b/girepository/cmph/compressed_seq.h @@ -0,0 +1,84 @@ +#ifndef __CMPH_COMPRESSED_SEQ_H__ +#define __CMPH_COMPRESSED_SEQ_H__ + +#include"select.h" + +struct _compressed_seq_t +{ + cmph_uint32 n; // number of values stored in store_table + // The length in bits of each value is decomposed into two compnents: the lg(n) MSBs are stored in rank_select data structure + // the remaining LSBs are stored in a table of n cells, each one of rem_r bits. + cmph_uint32 rem_r; + cmph_uint32 total_length; // total length in bits of stored_table + select_t sel; + cmph_uint32 * length_rems; + cmph_uint32 * store_table; +}; + +typedef struct _compressed_seq_t compressed_seq_t; + +/** \fn void compressed_seq_init(compressed_seq_t * cs); + * \brief Initialize a compressed sequence structure. + * \param cs points to the compressed sequence structure to be initialized + */ +void compressed_seq_init(compressed_seq_t * cs); + +/** \fn void compressed_seq_destroy(compressed_seq_t * cs); + * \brief Destroy a compressed sequence given as input. + * \param cs points to the compressed sequence structure to be destroyed + */ +void compressed_seq_destroy(compressed_seq_t * cs); + +/** \fn void compressed_seq_generate(compressed_seq_t * cs, cmph_uint32 * vals_table, cmph_uint32 n); + * \brief Generate a compressed sequence from an input array with n values. + * \param cs points to the compressed sequence structure + * \param vals_table poiter to the array given as input + * \param n number of values in @see vals_table + */ +void compressed_seq_generate(compressed_seq_t * cs, cmph_uint32 * vals_table, cmph_uint32 n); + + +/** \fn cmph_uint32 compressed_seq_query(compressed_seq_t * cs, cmph_uint32 idx); + * \brief Returns the value stored at index @see idx of the compressed sequence structure. + * \param cs points to the compressed sequence structure + * \param idx index to retrieve the value from + * \return the value stored at index @see idx of the compressed sequence structure + */ +cmph_uint32 compressed_seq_query(compressed_seq_t * cs, cmph_uint32 idx); + + +/** \fn cmph_uint32 compressed_seq_get_space_usage(compressed_seq_t * cs); + * \brief Returns amount of space (in bits) to store the compressed sequence. + * \param cs points to the compressed sequence structure + * \return the amount of space (in bits) to store @see cs + */ +cmph_uint32 compressed_seq_get_space_usage(compressed_seq_t * cs); + +void compressed_seq_dump(compressed_seq_t * cs, char ** buf, cmph_uint32 * buflen); + +void compressed_seq_load(compressed_seq_t * cs, const char * buf, cmph_uint32 buflen); + + +/** \fn void compressed_seq_pack(compressed_seq_t *cs, void *cs_packed); + * \brief Support the ability to pack a compressed sequence structure into a preallocated contiguous memory space pointed by cs_packed. + * \param cs points to the compressed sequence structure + * \param cs_packed pointer to the contiguous memory area used to store the compressed sequence structure. The size of cs_packed must be at least @see compressed_seq_packed_size + */ +void compressed_seq_pack(compressed_seq_t *cs, void *cs_packed); + +/** \fn cmph_uint32 compressed_seq_packed_size(compressed_seq_t *cs); + * \brief Return the amount of space needed to pack a compressed sequence structure. + * \return the size of the packed compressed sequence structure or zero for failures + */ +cmph_uint32 compressed_seq_packed_size(compressed_seq_t *cs); + + +/** \fn cmph_uint32 compressed_seq_query_packed(void * cs_packed, cmph_uint32 idx); + * \brief Returns the value stored at index @see idx of the packed compressed sequence structure. + * \param cs_packed is a pointer to a contiguous memory area + * \param idx is the index to retrieve the value from + * \return the value stored at index @see idx of the packed compressed sequence structure + */ +cmph_uint32 compressed_seq_query_packed(void * cs_packed, cmph_uint32 idx); + +#endif diff --git a/girepository/cmph/debug.h b/girepository/cmph/debug.h new file mode 100644 index 000000000..0f7ddb139 --- /dev/null +++ b/girepository/cmph/debug.h @@ -0,0 +1,53 @@ +#ifdef DEBUGP +#undef DEBUGP +#endif + +#ifdef __cplusplus +#include +#ifdef WIN32 +#include +#endif +#else +#include +#ifdef WIN32 +#include +#endif +#endif + +#ifndef __GNUC__ +#ifndef __DEBUG_H__ +#define __DEBUG_H__ +#include +static void debugprintf(const char *format, ...) +{ + va_list ap; + char *f = NULL; + const char *p="%s:%d "; + size_t plen = strlen(p); + va_start(ap, format); + f = (char *)malloc(plen + strlen(format) + 1); + if (!f) return; + memcpy(f, p, plen); + memcpy(f + plen, format, strlen(format) + 1); + vfprintf(stderr, f, ap); + va_end(ap); + free(f); +} +static void dummyprintf(const char *format, ...) +{} +#endif +#endif + +#ifdef DEBUG +#ifndef __GNUC__ +#define DEBUGP debugprintf +#else +#define DEBUGP(args...) do { fprintf(stderr, "%s:%d ", __FILE__, __LINE__); fprintf(stderr, ## args); } while(0) +#endif +#else +#ifndef __GNUC__ +#define DEBUGP dummyprintf +#else +#define DEBUGP(args...) +#endif +#endif diff --git a/girepository/cmph/djb2_hash.c b/girepository/cmph/djb2_hash.c new file mode 100644 index 000000000..d3b4330ab --- /dev/null +++ b/girepository/cmph/djb2_hash.c @@ -0,0 +1,49 @@ +#include "djb2_hash.h" +#include + +djb2_state_t *djb2_state_new() +{ + djb2_state_t *state = (djb2_state_t *)malloc(sizeof(djb2_state_t)); + state->hashfunc = CMPH_HASH_DJB2; + return state; +} + +void djb2_state_destroy(djb2_state_t *state) +{ + free(state); +} + +cmph_uint32 djb2_hash(djb2_state_t *state, const char *k, cmph_uint32 keylen) +{ + register cmph_uint32 hash = 5381; + const unsigned char *ptr = (unsigned char *)k; + cmph_uint32 i = 0; + while (i < keylen) + { + hash = hash*33 ^ *ptr; + ++ptr, ++i; + } + return hash; +} + + +void djb2_state_dump(djb2_state_t *state, char **buf, cmph_uint32 *buflen) +{ + *buf = NULL; + *buflen = 0; + return; +} + +djb2_state_t *djb2_state_copy(djb2_state_t *src_state) +{ + djb2_state_t *dest_state = (djb2_state_t *)malloc(sizeof(djb2_state_t)); + dest_state->hashfunc = src_state->hashfunc; + return dest_state; +} + +djb2_state_t *djb2_state_load(const char *buf, cmph_uint32 buflen) +{ + djb2_state_t *state = (djb2_state_t *)malloc(sizeof(djb2_state_t)); + state->hashfunc = CMPH_HASH_DJB2; + return state; +} diff --git a/girepository/cmph/djb2_hash.h b/girepository/cmph/djb2_hash.h new file mode 100644 index 000000000..dda97e317 --- /dev/null +++ b/girepository/cmph/djb2_hash.h @@ -0,0 +1,18 @@ +#ifndef __DJB2_HASH_H__ +#define __DJB2_HASH_H__ + +#include "hash.h" + +typedef struct __djb2_state_t +{ + CMPH_HASH hashfunc; +} djb2_state_t; + +djb2_state_t *djb2_state_new(); +cmph_uint32 djb2_hash(djb2_state_t *state, const char *k, cmph_uint32 keylen); +void djb2_state_dump(djb2_state_t *state, char **buf, cmph_uint32 *buflen); +djb2_state_t *djb2_state_copy(djb2_state_t *src_state); +djb2_state_t *djb2_state_load(const char *buf, cmph_uint32 buflen); +void djb2_state_destroy(djb2_state_t *state); + +#endif diff --git a/girepository/cmph/fch.c b/girepository/cmph/fch.c new file mode 100644 index 000000000..f12b6fcd2 --- /dev/null +++ b/girepository/cmph/fch.c @@ -0,0 +1,539 @@ +#include "fch.h" +#include "cmph_structs.h" +#include "fch_structs.h" +#include "hash.h" +#include "bitbool.h" +#include "fch_buckets.h" +#include +#include +#include +#include +#include +#include + +#define INDEX 0 /* alignment index within a bucket */ +//#define DEBUG +#include "debug.h" + +static fch_buckets_t * mapping(cmph_config_t *mph); +static cmph_uint32 * ordering(fch_buckets_t * buckets); +static cmph_uint8 check_for_collisions_h2(fch_config_data_t *fch, fch_buckets_t * buckets, cmph_uint32 *sorted_indexes); +static void permut(cmph_uint32 * vector, cmph_uint32 n); +static cmph_uint8 searching(fch_config_data_t *fch, fch_buckets_t *buckets, cmph_uint32 *sorted_indexes); + +fch_config_data_t *fch_config_new(void) +{ + fch_config_data_t *fch; + fch = (fch_config_data_t *)malloc(sizeof(fch_config_data_t)); + assert(fch); + memset(fch, 0, sizeof(fch_config_data_t)); + fch->hashfuncs[0] = CMPH_HASH_JENKINS; + fch->hashfuncs[1] = CMPH_HASH_JENKINS; + fch->m = fch->b = 0; + fch->c = fch->p1 = fch->p2 = 0.0; + fch->g = NULL; + fch->h1 = NULL; + fch->h2 = NULL; + return fch; +} + +void fch_config_destroy(cmph_config_t *mph) +{ + fch_config_data_t *data = (fch_config_data_t *)mph->data; + //DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void fch_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + fch_config_data_t *fch = (fch_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 2) break; //fch only uses two hash functions + fch->hashfuncs[i] = *hashptr; + ++i, ++hashptr; + } +} + +cmph_uint32 mixh10h11h12(cmph_uint32 b, double p1, double p2, cmph_uint32 initial_index) +{ + register cmph_uint32 int_p2 = (cmph_uint32)p2; + if (initial_index < p1) initial_index %= int_p2; /* h11 o h10 */ + else { /* h12 o h10 */ + initial_index %= b; + if(initial_index < p2) initial_index += int_p2; + } + return initial_index; +} + + +cmph_uint32 fch_calc_b(double c, cmph_uint32 m) +{ + return (cmph_uint32)ceil((c*m)/(log((double)m)/log(2.0) + 1)); +} + +double fch_calc_p1(cmph_uint32 m) +{ + return ceil(0.55*m); +} + +double fch_calc_p2(cmph_uint32 b) +{ + return ceil(0.3*b); +} + +static fch_buckets_t * mapping(cmph_config_t *mph) +{ + cmph_uint32 i = 0; + fch_buckets_t *buckets = NULL; + fch_config_data_t *fch = (fch_config_data_t *)mph->data; + if (fch->h1) hash_state_destroy(fch->h1); + fch->h1 = hash_state_new(fch->hashfuncs[0], fch->m); + fch->b = fch_calc_b(fch->c, fch->m); + fch->p1 = fch_calc_p1(fch->m); + fch->p2 = fch_calc_p2(fch->b); + //DEBUGP("b:%u p1:%f p2:%f\n", fch->b, fch->p1, fch->p2); + buckets = fch_buckets_new(fch->b); + + mph->key_source->rewind(mph->key_source->data); + for(i = 0; i < fch->m; i++) + { + cmph_uint32 h1, keylen; + char *key = NULL; + mph->key_source->read(mph->key_source->data, &key, &keylen); + h1 = hash(fch->h1, key, keylen) % fch->m; + h1 = mixh10h11h12 (fch->b, fch->p1, fch->p2, h1); + fch_buckets_insert(buckets, h1, key, keylen); + key = NULL; // transger memory ownership + + } + return buckets; +} + + +// returns the buckets indexes sorted by their sizes. +static cmph_uint32 * ordering(fch_buckets_t * buckets) +{ + return fch_buckets_get_indexes_sorted_by_size(buckets); +} + +/* Check whether function h2 causes collisions among the keys of each bucket */ +static cmph_uint8 check_for_collisions_h2(fch_config_data_t *fch, fch_buckets_t * buckets, cmph_uint32 *sorted_indexes) +{ + //cmph_uint32 max_size = fch_buckets_get_max_size(buckets); + cmph_uint8 * hashtable = (cmph_uint8 *)calloc((size_t)fch->m, sizeof(cmph_uint8)); + cmph_uint32 nbuckets = fch_buckets_get_nbuckets(buckets); + cmph_uint32 i = 0, index = 0, j =0; + for (i = 0; i < nbuckets; i++) + { + cmph_uint32 nkeys = fch_buckets_get_size(buckets, sorted_indexes[i]); + memset(hashtable, 0, (size_t)fch->m); + //DEBUGP("bucket %u -- nkeys: %u\n", i, nkeys); + for (j = 0; j < nkeys; j++) + { + char * key = fch_buckets_get_key(buckets, sorted_indexes[i], j); + cmph_uint32 keylen = fch_buckets_get_keylength(buckets, sorted_indexes[i], j); + index = hash(fch->h2, key, keylen) % fch->m; + if(hashtable[index]) { // collision detected + free(hashtable); + return 1; + } + hashtable[index] = 1; + } + } + free(hashtable); + return 0; +} + +static void permut(cmph_uint32 * vector, cmph_uint32 n) +{ + cmph_uint32 i, j, b; + for (i = 0; i < n; i++) { + j = (cmph_uint32) rand() % n; + b = vector[i]; + vector[i] = vector[j]; + vector[j] = b; + } +} + +static cmph_uint8 searching(fch_config_data_t *fch, fch_buckets_t *buckets, cmph_uint32 *sorted_indexes) +{ + cmph_uint32 * random_table = (cmph_uint32 *) calloc((size_t)fch->m, sizeof(cmph_uint32)); + cmph_uint32 * map_table = (cmph_uint32 *) calloc((size_t)fch->m, sizeof(cmph_uint32)); + cmph_uint32 iteration_to_generate_h2 = 0; + cmph_uint32 searching_iterations = 0; + cmph_uint8 restart = 0; + cmph_uint32 nbuckets = fch_buckets_get_nbuckets(buckets); + cmph_uint32 i, j, z, counter = 0, filled_count = 0; + if (fch->g) free (fch->g); + fch->g = (cmph_uint32 *) calloc((size_t)fch->b, sizeof(cmph_uint32)); + + //DEBUGP("max bucket size: %u\n", fch_buckets_get_max_size(buckets)); + + for(i = 0; i < fch->m; i++) + { + random_table[i] = i; + } + permut(random_table, fch->m); + for(i = 0; i < fch->m; i++) + { + map_table[random_table[i]] = i; + } + do { + if (fch->h2) hash_state_destroy(fch->h2); + fch->h2 = hash_state_new(fch->hashfuncs[1], fch->m); + restart = check_for_collisions_h2(fch, buckets, sorted_indexes); + filled_count = 0; + if (!restart) + { + searching_iterations++; iteration_to_generate_h2 = 0; + //DEBUGP("searching_iterations: %u\n", searching_iterations); + } + else { + iteration_to_generate_h2++; + //DEBUGP("iteration_to_generate_h2: %u\n", iteration_to_generate_h2); + } + for(i = 0; (i < nbuckets) && !restart; i++) { + cmph_uint32 bucketsize = fch_buckets_get_size(buckets, sorted_indexes[i]); + if (bucketsize == 0) + { + restart = 0; // false + break; + } + else restart = 1; // true + for(z = 0; (z < (fch->m - filled_count)) && restart; z++) { + char * key = fch_buckets_get_key(buckets, sorted_indexes[i], INDEX); + cmph_uint32 keylen = fch_buckets_get_keylength(buckets, sorted_indexes[i], INDEX); + cmph_uint32 h2 = hash(fch->h2, key, keylen) % fch->m; + counter = 0; + restart = 0; // false + fch->g[sorted_indexes[i]] = (fch->m + random_table[filled_count + z] - h2) % fch->m; + //DEBUGP("g[%u]: %u\n", sorted_indexes[i], fch->g[sorted_indexes[i]]); + j = INDEX; + do { + cmph_uint32 index = 0; + key = fch_buckets_get_key(buckets, sorted_indexes[i], j); + keylen = fch_buckets_get_keylength(buckets, sorted_indexes[i], j); + h2 = hash(fch->h2, key, keylen) % fch->m; + index = (h2 + fch->g[sorted_indexes[i]]) % fch->m; + //DEBUGP("key:%s keylen:%u index: %u h2:%u bucketsize:%u\n", key, keylen, index, h2, bucketsize); + if (map_table[index] >= filled_count) { + cmph_uint32 y = map_table[index]; + cmph_uint32 ry = random_table[y]; + random_table[y] = random_table[filled_count]; + random_table[filled_count] = ry; + map_table[random_table[y]] = y; + map_table[random_table[filled_count]] = filled_count; + filled_count++; + counter ++; + } + else { + restart = 1; // true + filled_count = filled_count - counter; + counter = 0; + break; + } + j = (j + 1) % bucketsize; + } while(j % bucketsize != INDEX); + } + //getchar(); + } + } while(restart && (searching_iterations < 10) && (iteration_to_generate_h2 < 1000)); + free(map_table); + free(random_table); + return restart; +} + + + +cmph_t *fch_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + fch_data_t *fchf = NULL; + cmph_uint32 iterations = 100; + cmph_uint8 restart_mapping = 0; + fch_buckets_t * buckets = NULL; + cmph_uint32 * sorted_indexes = NULL; + fch_config_data_t *fch = (fch_config_data_t *)mph->data; + fch->m = mph->key_source->nkeys; + //DEBUGP("m: %f\n", fch->m); + if (c <= 2) c = 2.6; // validating restrictions over parameter c. + fch->c = c; + //DEBUGP("c: %f\n", fch->c); + fch->h1 = NULL; + fch->h2 = NULL; + fch->g = NULL; + do + { + if (mph->verbosity) + { + fprintf(stderr, "Entering mapping step for mph creation of %u keys\n", fch->m); + } + if (buckets) fch_buckets_destroy(buckets); + buckets = mapping(mph); + if (mph->verbosity) + { + fprintf(stderr, "Starting ordering step\n"); + } + if (sorted_indexes) free (sorted_indexes); + sorted_indexes = ordering(buckets); + if (mph->verbosity) + { + fprintf(stderr, "Starting searching step.\n"); + } + restart_mapping = searching(fch, buckets, sorted_indexes); + iterations--; + + } while(restart_mapping && iterations > 0); + if (buckets) fch_buckets_destroy(buckets); + if (sorted_indexes) free (sorted_indexes); + if (iterations == 0) return NULL; + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + fchf = (fch_data_t *)malloc(sizeof(fch_data_t)); + fchf->g = fch->g; + fch->g = NULL; //transfer memory ownership + fchf->h1 = fch->h1; + fch->h1 = NULL; //transfer memory ownership + fchf->h2 = fch->h2; + fch->h2 = NULL; //transfer memory ownership + fchf->p2 = fch->p2; + fchf->p1 = fch->p1; + fchf->b = fch->b; + fchf->c = fch->c; + fchf->m = fch->m; + mphf->data = fchf; + mphf->size = fch->m; + //DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + return mphf; +} + +int fch_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + register size_t nbytes; + + fch_data_t *data = (fch_data_t *)mphf->data; + +#ifdef DEBUG + cmph_uint32 i; +#endif + __cmph_dump(mphf, fd); + + hash_state_dump(data->h1, &buf, &buflen); + //DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + hash_state_dump(data->h2, &buf, &buflen); + //DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + nbytes = fwrite(&buflen, sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(buf, (size_t)buflen, (size_t)1, fd); + free(buf); + + nbytes = fwrite(&(data->m), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->c), sizeof(double), (size_t)1, fd); + nbytes = fwrite(&(data->b), sizeof(cmph_uint32), (size_t)1, fd); + nbytes = fwrite(&(data->p1), sizeof(double), (size_t)1, fd); + nbytes = fwrite(&(data->p2), sizeof(double), (size_t)1, fd); + nbytes = fwrite(data->g, sizeof(cmph_uint32)*(data->b), (size_t)1, fd); + if (nbytes == 0 && ferror(fd)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return 0; + } + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < data->b; ++i) fprintf(stderr, "%u ", data->g[i]); + fprintf(stderr, "\n"); + #endif + return 1; +} + +void fch_load(FILE *f, cmph_t *mphf) +{ + char *buf = NULL; + cmph_uint32 buflen; + register size_t nbytes; + fch_data_t *fch = (fch_data_t *)malloc(sizeof(fch_data_t)); +#ifdef DEBUG + cmph_uint32 i; +#endif + + //DEBUGP("Loading fch mphf\n"); + mphf->data = fch; + //DEBUGP("Reading h1\n"); + fch->h1 = NULL; + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + //DEBUGP("Hash state of h1 has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + fch->h1 = hash_state_load(buf, buflen); + free(buf); + + //DEBUGP("Loading fch mphf\n"); + mphf->data = fch; + //DEBUGP("Reading h2\n"); + fch->h2 = NULL; + nbytes = fread(&buflen, sizeof(cmph_uint32), (size_t)1, f); + //DEBUGP("Hash state of h2 has %u bytes\n", buflen); + buf = (char *)malloc((size_t)buflen); + nbytes = fread(buf, (size_t)buflen, (size_t)1, f); + fch->h2 = hash_state_load(buf, buflen); + free(buf); + + + //DEBUGP("Reading m and n\n"); + nbytes = fread(&(fch->m), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(fch->c), sizeof(double), (size_t)1, f); + nbytes = fread(&(fch->b), sizeof(cmph_uint32), (size_t)1, f); + nbytes = fread(&(fch->p1), sizeof(double), (size_t)1, f); + nbytes = fread(&(fch->p2), sizeof(double), (size_t)1, f); + + fch->g = (cmph_uint32 *)malloc(sizeof(cmph_uint32)*fch->b); + nbytes = fread(fch->g, fch->b*sizeof(cmph_uint32), (size_t)1, f); + if (nbytes == 0 && ferror(f)) { + fprintf(stderr, "ERROR: %s\n", strerror(errno)); + return; + } + + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < fch->b; ++i) fprintf(stderr, "%u ", fch->g[i]); + fprintf(stderr, "\n"); + #endif + return; +} + +cmph_uint32 fch_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + fch_data_t *fch = mphf->data; + cmph_uint32 h1 = hash(fch->h1, key, keylen) % fch->m; + cmph_uint32 h2 = hash(fch->h2, key, keylen) % fch->m; + h1 = mixh10h11h12 (fch->b, fch->p1, fch->p2, h1); + //DEBUGP("key: %s h1: %u h2: %u g[h1]: %u\n", key, h1, h2, fch->g[h1]); + return (h2 + fch->g[h1]) % fch->m; +} +void fch_destroy(cmph_t *mphf) +{ + fch_data_t *data = (fch_data_t *)mphf->data; + free(data->g); + hash_state_destroy(data->h1); + hash_state_destroy(data->h2); + free(data); + free(mphf); +} + +/** \fn void fch_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void fch_pack(cmph_t *mphf, void *packed_mphf) +{ + fch_data_t *data = (fch_data_t *)mphf->data; + cmph_uint8 * ptr = packed_mphf; + + // packing h1 type + CMPH_HASH h1_type = hash_get_type(data->h1); + CMPH_HASH h2_type; + *((cmph_uint32 *) ptr) = h1_type; + ptr += sizeof(cmph_uint32); + + // packing h1 + hash_state_pack(data->h1, ptr); + ptr += hash_state_packed_size(h1_type); + + // packing h2 type + h2_type = hash_get_type(data->h2); + *((cmph_uint32 *) ptr) = h2_type; + ptr += sizeof(cmph_uint32); + + // packing h2 + hash_state_pack(data->h2, ptr); + ptr += hash_state_packed_size(h2_type); + + // packing m + *((cmph_uint32 *) ptr) = data->m; + ptr += sizeof(data->m); + + // packing b + *((cmph_uint32 *) ptr) = data->b; + ptr += sizeof(data->b); + + // packing p1 + *((cmph_uint64 *)ptr) = (cmph_uint64)data->p1; + ptr += sizeof(data->p1); + + // packing p2 + *((cmph_uint64 *)ptr) = (cmph_uint64)data->p2; + ptr += sizeof(data->p2); + + // packing g + memcpy(ptr, data->g, sizeof(cmph_uint32)*(data->b)); +} + +/** \fn cmph_uint32 fch_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 fch_packed_size(cmph_t *mphf) +{ + fch_data_t *data = (fch_data_t *)mphf->data; + CMPH_HASH h1_type = hash_get_type(data->h1); + CMPH_HASH h2_type = hash_get_type(data->h2); + + return (cmph_uint32)(sizeof(CMPH_ALGO) + hash_state_packed_size(h1_type) + hash_state_packed_size(h2_type) + + 4*sizeof(cmph_uint32) + 2*sizeof(double) + sizeof(cmph_uint32)*(data->b)); +} + + +/** cmph_uint32 fch_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 fch_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen) +{ + register cmph_uint8 *h1_ptr = packed_mphf; + register CMPH_HASH h1_type = *((cmph_uint32 *)h1_ptr); + register cmph_uint8 *h2_ptr; + register CMPH_HASH h2_type; + register cmph_uint32 *g_ptr; + register cmph_uint32 m, b, h1, h2; + register double p1, p2; + + h1_ptr += 4; + + h2_ptr = h1_ptr + hash_state_packed_size(h1_type); + h2_type = *((cmph_uint32 *)h2_ptr); + h2_ptr += 4; + + g_ptr = (cmph_uint32 *)(h2_ptr + hash_state_packed_size(h2_type)); + + m = *g_ptr++; + + b = *g_ptr++; + + p1 = (double)(*((cmph_uint64 *)g_ptr)); + g_ptr += 2; + + p2 = (double)(*((cmph_uint64 *)g_ptr)); + g_ptr += 2; + + h1 = hash_packed(h1_ptr, h1_type, key, keylen) % m; + h2 = hash_packed(h2_ptr, h2_type, key, keylen) % m; + h1 = mixh10h11h12 (b, p1, p2, h1); + return (h2 + g_ptr[h1]) % m; +} + diff --git a/girepository/cmph/fch.h b/girepository/cmph/fch.h new file mode 100644 index 000000000..9d13a1c1b --- /dev/null +++ b/girepository/cmph/fch.h @@ -0,0 +1,48 @@ +#ifndef __CMPH_FCH_H__ +#define __CMPH_FCH_H__ + +#include "cmph.h" + +typedef struct __fch_data_t fch_data_t; +typedef struct __fch_config_data_t fch_config_data_t; + +/* Parameters calculation */ +cmph_uint32 fch_calc_b(double c, cmph_uint32 m); +double fch_calc_p1(cmph_uint32 m); +double fch_calc_p2(cmph_uint32 b); +cmph_uint32 mixh10h11h12(cmph_uint32 b, double p1, double p2, cmph_uint32 initial_index); + +fch_config_data_t *fch_config_new(void); +void fch_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void fch_config_destroy(cmph_config_t *mph); +cmph_t *fch_new(cmph_config_t *mph, double c); + +void fch_load(FILE *f, cmph_t *mphf); +int fch_dump(cmph_t *mphf, FILE *f); +void fch_destroy(cmph_t *mphf); +cmph_uint32 fch_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); + +/** \fn void fch_pack(cmph_t *mphf, void *packed_mphf); + * \brief Support the ability to pack a perfect hash function into a preallocated contiguous memory space pointed by packed_mphf. + * \param mphf pointer to the resulting mphf + * \param packed_mphf pointer to the contiguous memory area used to store the resulting mphf. The size of packed_mphf must be at least cmph_packed_size() + */ +void fch_pack(cmph_t *mphf, void *packed_mphf); + +/** \fn cmph_uint32 fch_packed_size(cmph_t *mphf); + * \brief Return the amount of space needed to pack mphf. + * \param mphf pointer to a mphf + * \return the size of the packed function or zero for failures + */ +cmph_uint32 fch_packed_size(cmph_t *mphf); + +/** cmph_uint32 fch_search(void *packed_mphf, const char *key, cmph_uint32 keylen); + * \brief Use the packed mphf to do a search. + * \param packed_mphf pointer to the packed mphf + * \param key key to be hashed + * \param keylen key legth in bytes + * \return The mphf value + */ +cmph_uint32 fch_search_packed(void *packed_mphf, const char *key, cmph_uint32 keylen); + +#endif diff --git a/girepository/cmph/fch_buckets.c b/girepository/cmph/fch_buckets.c new file mode 100644 index 000000000..a588f147e --- /dev/null +++ b/girepository/cmph/fch_buckets.c @@ -0,0 +1,214 @@ +#include "vqueue.h" +#include "fch_buckets.h" +#include +#include +#include +//#define DEBUG +#include "debug.h" + +typedef struct __fch_bucket_entry_t +{ + char * value; + cmph_uint32 length; +} fch_bucket_entry_t; + +typedef struct __fch_bucket_t +{ + fch_bucket_entry_t * entries; + cmph_uint32 capacity, size; +} fch_bucket_t; + + + +static void fch_bucket_new(fch_bucket_t *bucket) +{ + assert(bucket); + bucket->size = 0; + bucket->entries = NULL; + bucket->capacity = 0; +} + +static void fch_bucket_destroy(fch_bucket_t *bucket) +{ + cmph_uint32 i; + assert(bucket); + for (i = 0; i < bucket->size; i++) + { + free((bucket->entries + i)->value); + } + free(bucket->entries); +} + + +static void fch_bucket_reserve(fch_bucket_t *bucket, cmph_uint32 size) +{ + assert(bucket); + if (bucket->capacity < size) + { + cmph_uint32 new_capacity = bucket->capacity + 1; + DEBUGP("Increasing current capacity %u to %u\n", bucket->capacity, size); + while (new_capacity < size) + { + new_capacity *= 2; + } + bucket->entries = (fch_bucket_entry_t *)realloc(bucket->entries, sizeof(fch_bucket_entry_t)*new_capacity); + assert(bucket->entries); + bucket->capacity = new_capacity; + DEBUGP("Increased\n"); + } +} + +static void fch_bucket_insert(fch_bucket_t *bucket, char *val, cmph_uint32 val_length) +{ + assert(bucket); + fch_bucket_reserve(bucket, bucket->size + 1); + (bucket->entries + bucket->size)->value = val; + (bucket->entries + bucket->size)->length = val_length; + ++(bucket->size); +} + + +static cmph_uint8 fch_bucket_is_empty(fch_bucket_t *bucket) +{ + assert(bucket); + return (cmph_uint8)(bucket->size == 0); +} + +static cmph_uint32 fch_bucket_size(fch_bucket_t *bucket) +{ + assert(bucket); + return bucket->size; +} + +static char * fch_bucket_get_key(fch_bucket_t *bucket, cmph_uint32 index_key) +{ + assert(bucket); assert(index_key < bucket->size); + return (bucket->entries + index_key)->value; +} + +static cmph_uint32 fch_bucket_get_length(fch_bucket_t *bucket, cmph_uint32 index_key) +{ + assert(bucket); assert(index_key < bucket->size); + return (bucket->entries + index_key)->length; +} + +static void fch_bucket_print(fch_bucket_t * bucket, cmph_uint32 index) +{ + cmph_uint32 i; + assert(bucket); + fprintf(stderr, "Printing bucket %u ...\n", index); + for (i = 0; i < bucket->size; i++) + { + fprintf(stderr, " key: %s\n", (bucket->entries + i)->value); + } +} + +////////////////////////////////////////////////////////////////////////////////////// + +struct __fch_buckets_t +{ + fch_bucket_t * values; + cmph_uint32 nbuckets, max_size; + +}; + +fch_buckets_t * fch_buckets_new(cmph_uint32 nbuckets) +{ + cmph_uint32 i; + fch_buckets_t *buckets = (fch_buckets_t *)malloc(sizeof(fch_buckets_t)); + assert(buckets); + buckets->values = (fch_bucket_t *)calloc((size_t)nbuckets, sizeof(fch_bucket_t)); + for (i = 0; i < nbuckets; i++) fch_bucket_new(buckets->values + i); + assert(buckets->values); + buckets->nbuckets = nbuckets; + buckets->max_size = 0; + return buckets; +} + +cmph_uint8 fch_buckets_is_empty(fch_buckets_t * buckets, cmph_uint32 index) +{ + assert(index < buckets->nbuckets); + return fch_bucket_is_empty(buckets->values + index); +} + +void fch_buckets_insert(fch_buckets_t * buckets, cmph_uint32 index, char * key, cmph_uint32 length) +{ + assert(index < buckets->nbuckets); + fch_bucket_insert(buckets->values + index, key, length); + if (fch_bucket_size(buckets->values + index) > buckets->max_size) + { + buckets->max_size = fch_bucket_size(buckets->values + index); + } +} + +cmph_uint32 fch_buckets_get_size(fch_buckets_t * buckets, cmph_uint32 index) +{ + assert(index < buckets->nbuckets); + return fch_bucket_size(buckets->values + index); +} + + +char * fch_buckets_get_key(fch_buckets_t * buckets, cmph_uint32 index, cmph_uint32 index_key) +{ + assert(index < buckets->nbuckets); + return fch_bucket_get_key(buckets->values + index, index_key); +} + +cmph_uint32 fch_buckets_get_keylength(fch_buckets_t * buckets, cmph_uint32 index, cmph_uint32 index_key) +{ + assert(index < buckets->nbuckets); + return fch_bucket_get_length(buckets->values + index, index_key); +} + +cmph_uint32 fch_buckets_get_max_size(fch_buckets_t * buckets) +{ + return buckets->max_size; +} + +cmph_uint32 fch_buckets_get_nbuckets(fch_buckets_t * buckets) +{ + return buckets->nbuckets; +} + +cmph_uint32 * fch_buckets_get_indexes_sorted_by_size(fch_buckets_t * buckets) +{ + cmph_uint32 i = 0; + cmph_uint32 sum = 0, value; + cmph_uint32 *nbuckets_size = (cmph_uint32 *) calloc((size_t)buckets->max_size + 1, sizeof(cmph_uint32)); + cmph_uint32 * sorted_indexes = (cmph_uint32 *) calloc((size_t)buckets->nbuckets, sizeof(cmph_uint32)); + + // collect how many buckets for each size. + for(i = 0; i < buckets->nbuckets; i++) nbuckets_size[fch_bucket_size(buckets->values + i)] ++; + + // calculating offset considering a decreasing order of buckets size. + value = nbuckets_size[buckets->max_size]; + nbuckets_size[buckets->max_size] = sum; + for(i = (int)buckets->max_size - 1; i >= 0; i--) + { + sum += value; + value = nbuckets_size[i]; + nbuckets_size[i] = sum; + + } + for(i = 0; i < buckets->nbuckets; i++) + { + sorted_indexes[nbuckets_size[fch_bucket_size(buckets->values + i)]] = (cmph_uint32)i; + nbuckets_size[fch_bucket_size(buckets->values + i)] ++; + } + free(nbuckets_size); + return sorted_indexes; +} + +void fch_buckets_print(fch_buckets_t * buckets) +{ + cmph_uint32 i; + for (i = 0; i < buckets->nbuckets; i++) fch_bucket_print(buckets->values + i, i); +} + +void fch_buckets_destroy(fch_buckets_t * buckets) +{ + cmph_uint32 i; + for (i = 0; i < buckets->nbuckets; i++) fch_bucket_destroy(buckets->values + i); + free(buckets->values); + free(buckets); +} diff --git a/girepository/cmph/fch_buckets.h b/girepository/cmph/fch_buckets.h new file mode 100644 index 000000000..2a1b8b2a6 --- /dev/null +++ b/girepository/cmph/fch_buckets.h @@ -0,0 +1,30 @@ +#ifndef __CMPH_FCH_BUCKETS_H__ +#define __CMPH_FCH_BUCKETS_H__ + +#include "cmph_types.h" +typedef struct __fch_buckets_t fch_buckets_t; + +fch_buckets_t * fch_buckets_new(cmph_uint32 nbuckets); + +cmph_uint8 fch_buckets_is_empty(fch_buckets_t * buckets, cmph_uint32 index); + +void fch_buckets_insert(fch_buckets_t * buckets, cmph_uint32 index, char * key, cmph_uint32 length); + +cmph_uint32 fch_buckets_get_size(fch_buckets_t * buckets, cmph_uint32 index); + +char * fch_buckets_get_key(fch_buckets_t * buckets, cmph_uint32 index, cmph_uint32 index_key); + +cmph_uint32 fch_buckets_get_keylength(fch_buckets_t * buckets, cmph_uint32 index, cmph_uint32 index_key); + +// returns the size of biggest bucket. +cmph_uint32 fch_buckets_get_max_size(fch_buckets_t * buckets); + +// returns the number of buckets. +cmph_uint32 fch_buckets_get_nbuckets(fch_buckets_t * buckets); + +cmph_uint32 * fch_buckets_get_indexes_sorted_by_size(fch_buckets_t * buckets); + +void fch_buckets_print(fch_buckets_t * buckets); + +void fch_buckets_destroy(fch_buckets_t * buckets); +#endif diff --git a/girepository/cmph/fch_structs.h b/girepository/cmph/fch_structs.h new file mode 100644 index 000000000..fcd1555ea --- /dev/null +++ b/girepository/cmph/fch_structs.h @@ -0,0 +1,30 @@ +#ifndef __CMPH_FCH_STRUCTS_H__ +#define __CMPH_FCH_STRUCTS_H__ + +#include "hash_state.h" + +struct __fch_data_t +{ + cmph_uint32 m; // words count + double c; // constant c + cmph_uint32 b; // parameter b = ceil(c*m/(log(m)/log(2) + 1)). Don't need to be stored + double p1; // constant p1 = ceil(0.6*m). Don't need to be stored + double p2; // constant p2 = ceil(0.3*b). Don't need to be stored + cmph_uint32 *g; // g function. + hash_state_t *h1; // h10 function. + hash_state_t *h2; // h20 function. +}; + +struct __fch_config_data_t +{ + CMPH_HASH hashfuncs[2]; + cmph_uint32 m; // words count + double c; // constant c + cmph_uint32 b; // parameter b = ceil(c*m/(log(m)/log(2) + 1)). Don't need to be stored + double p1; // constant p1 = ceil(0.6*m). Don't need to be stored + double p2; // constant p2 = ceil(0.3*b). Don't need to be stored + cmph_uint32 *g; // g function. + hash_state_t *h1; // h10 function. + hash_state_t *h2; // h20 function. +}; +#endif diff --git a/girepository/cmph/fnv_hash.c b/girepository/cmph/fnv_hash.c new file mode 100644 index 000000000..aeaca8ff4 --- /dev/null +++ b/girepository/cmph/fnv_hash.c @@ -0,0 +1,53 @@ +#include "fnv_hash.h" +#include + +fnv_state_t *fnv_state_new() +{ + fnv_state_t *state = (fnv_state_t *)malloc(sizeof(fnv_state_t)); + state->hashfunc = CMPH_HASH_FNV; + return state; +} + +void fnv_state_destroy(fnv_state_t *state) +{ + free(state); +} + +cmph_uint32 fnv_hash(fnv_state_t *state, const char *k, cmph_uint32 keylen) +{ + const unsigned char *bp = (const unsigned char *)k; + const unsigned char *be = bp + keylen; + static unsigned int hval = 0; + + while (bp < be) + { + + //hval *= 0x01000193; good for non-gcc compiler + hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); //good for gcc + + hval ^= *bp++; + } + return hval; +} + + +void fnv_state_dump(fnv_state_t *state, char **buf, cmph_uint32 *buflen) +{ + *buf = NULL; + *buflen = 0; + return; +} + +fnv_state_t * fnv_state_copy(fnv_state_t *src_state) +{ + fnv_state_t *dest_state = (fnv_state_t *)malloc(sizeof(fnv_state_t)); + dest_state->hashfunc = src_state->hashfunc; + return dest_state; +} + +fnv_state_t *fnv_state_load(const char *buf, cmph_uint32 buflen) +{ + fnv_state_t *state = (fnv_state_t *)malloc(sizeof(fnv_state_t)); + state->hashfunc = CMPH_HASH_FNV; + return state; +} diff --git a/girepository/cmph/fnv_hash.h b/girepository/cmph/fnv_hash.h new file mode 100644 index 000000000..7f5794657 --- /dev/null +++ b/girepository/cmph/fnv_hash.h @@ -0,0 +1,18 @@ +#ifndef __FNV_HASH_H__ +#define __FNV_HASH_H__ + +#include "hash.h" + +typedef struct __fnv_state_t +{ + CMPH_HASH hashfunc; +} fnv_state_t; + +fnv_state_t *fnv_state_new(); +cmph_uint32 fnv_hash(fnv_state_t *state, const char *k, cmph_uint32 keylen); +void fnv_state_dump(fnv_state_t *state, char **buf, cmph_uint32 *buflen); +fnv_state_t *fnv_state_copy(fnv_state_t *src_state); +fnv_state_t *fnv_state_load(const char *buf, cmph_uint32 buflen); +void fnv_state_destroy(fnv_state_t *state); + +#endif diff --git a/girepository/cmph/graph.c b/girepository/cmph/graph.c new file mode 100644 index 000000000..c29fd8b9c --- /dev/null +++ b/girepository/cmph/graph.c @@ -0,0 +1,338 @@ +#include "graph.h" + +#include +#include +#include +#include +#include +#include "vstack.h" +#include "bitbool.h" + +//#define DEBUG +#include "debug.h" + +/* static const cmph_uint8 bitmask[8] = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7 }; */ +/* #define GETBIT(array, i) (array[(i) / 8] & bitmask[(i) % 8]) */ +/* #define SETBIT(array, i) (array[(i) / 8] |= bitmask[(i) % 8]) */ +/* #define UNSETBIT(array, i) (array[(i) / 8] &= (~(bitmask[(i) % 8]))) */ + +#define abs_edge(e, i) (e % g->nedges + i * g->nedges) + +struct __graph_t +{ + cmph_uint32 nnodes; + cmph_uint32 nedges; + cmph_uint32 *edges; + cmph_uint32 *first; + cmph_uint32 *next; + cmph_uint8 *critical_nodes; /* included -- Fabiano*/ + cmph_uint32 ncritical_nodes; /* included -- Fabiano*/ + cmph_uint32 cedges; + int shrinking; +}; + +static cmph_uint32 EMPTY = UINT_MAX; + +graph_t *graph_new(cmph_uint32 nnodes, cmph_uint32 nedges) +{ + graph_t *graph = (graph_t *)malloc(sizeof(graph_t)); + if (!graph) return NULL; + + graph->edges = (cmph_uint32 *)malloc(sizeof(cmph_uint32) * 2 * nedges); + graph->next = (cmph_uint32 *)malloc(sizeof(cmph_uint32) * 2 * nedges); + graph->first = (cmph_uint32 *)malloc(sizeof(cmph_uint32) * nnodes); + graph->critical_nodes = NULL; /* included -- Fabiano*/ + graph->ncritical_nodes = 0; /* included -- Fabiano*/ + graph->nnodes = nnodes; + graph->nedges = nedges; + + graph_clear_edges(graph); + return graph; +} + + +void graph_destroy(graph_t *graph) +{ + DEBUGP("Destroying graph\n"); + free(graph->edges); + free(graph->first); + free(graph->next); + free(graph->critical_nodes); /* included -- Fabiano*/ + free(graph); + return; +} + +void graph_print(graph_t *g) +{ + cmph_uint32 i, e; + for (i = 0; i < g->nnodes; ++i) + { + DEBUGP("Printing edges connected to %u\n", i); + e = g->first[i]; + if (e != EMPTY) + { + printf("%u -> %u\n", g->edges[abs_edge(e, 0)], g->edges[abs_edge(e, 1)]); + while ((e = g->next[e]) != EMPTY) + { + printf("%u -> %u\n", g->edges[abs_edge(e, 0)], g->edges[abs_edge(e, 1)]); + } + } + + } + return; +} + +void graph_add_edge(graph_t *g, cmph_uint32 v1, cmph_uint32 v2) +{ + cmph_uint32 e = g->cedges; + + assert(v1 < g->nnodes); + assert(v2 < g->nnodes); + assert(e < g->nedges); + assert(!g->shrinking); + + g->next[e] = g->first[v1]; + g->first[v1] = e; + g->edges[e] = v2; + + g->next[e + g->nedges] = g->first[v2]; + g->first[v2] = e + g->nedges; + g->edges[e + g->nedges] = v1; + + ++(g->cedges); +} + +static int check_edge(graph_t *g, cmph_uint32 e, cmph_uint32 v1, cmph_uint32 v2) +{ + DEBUGP("Checking edge %u %u looking for %u %u\n", g->edges[abs_edge(e, 0)], g->edges[abs_edge(e, 1)], v1, v2); + if (g->edges[abs_edge(e, 0)] == v1 && g->edges[abs_edge(e, 1)] == v2) return 1; + if (g->edges[abs_edge(e, 0)] == v2 && g->edges[abs_edge(e, 1)] == v1) return 1; + return 0; +} + +cmph_uint32 graph_edge_id(graph_t *g, cmph_uint32 v1, cmph_uint32 v2) +{ + cmph_uint32 e; + e = g->first[v1]; + assert(e != EMPTY); + if (check_edge(g, e, v1, v2)) return abs_edge(e, 0); + do + { + e = g->next[e]; + assert(e != EMPTY); + } + while (!check_edge(g, e, v1, v2)); + return abs_edge(e, 0); +} +static void del_edge_point(graph_t *g, cmph_uint32 v1, cmph_uint32 v2) +{ + cmph_uint32 e, prev; + + DEBUGP("Deleting edge point %u %u\n", v1, v2); + e = g->first[v1]; + if (check_edge(g, e, v1, v2)) + { + g->first[v1] = g->next[e]; + //g->edges[e] = EMPTY; + DEBUGP("Deleted\n"); + return; + } + DEBUGP("Checking linked list\n"); + do + { + prev = e; + e = g->next[e]; + assert(e != EMPTY); + } + while (!check_edge(g, e, v1, v2)); + + g->next[prev] = g->next[e]; + //g->edges[e] = EMPTY; + DEBUGP("Deleted\n"); +} + + +void graph_del_edge(graph_t *g, cmph_uint32 v1, cmph_uint32 v2) +{ + g->shrinking = 1; + del_edge_point(g, v1, v2); + del_edge_point(g, v2, v1); +} + +void graph_clear_edges(graph_t *g) +{ + cmph_uint32 i; + for (i = 0; i < g->nnodes; ++i) g->first[i] = EMPTY; + for (i = 0; i < g->nedges*2; ++i) + { + g->edges[i] = EMPTY; + g->next[i] = EMPTY; + } + g->cedges = 0; + g->shrinking = 0; +} + +static cmph_uint8 find_degree1_edge(graph_t *g, cmph_uint32 v, cmph_uint8 *deleted, cmph_uint32 *e) +{ + cmph_uint32 edge = g->first[v]; + cmph_uint8 found = 0; + DEBUGP("Checking degree of vertex %u\n", v); + if (edge == EMPTY) return 0; + else if (!(GETBIT(deleted, abs_edge(edge, 0)))) + { + found = 1; + *e = edge; + } + while(1) + { + edge = g->next[edge]; + if (edge == EMPTY) break; + if (GETBIT(deleted, abs_edge(edge, 0))) continue; + if (found) return 0; + DEBUGP("Found first edge\n"); + *e = edge; + found = 1; + } + return found; +} + +static void cyclic_del_edge(graph_t *g, cmph_uint32 v, cmph_uint8 *deleted) +{ + + cmph_uint32 e = 0; + cmph_uint8 degree1; + cmph_uint32 v1 = v; + cmph_uint32 v2 = 0; + + degree1 = find_degree1_edge(g, v1, deleted, &e); + if (!degree1) return; + while(1) + { + DEBUGP("Deleting edge %u (%u->%u)\n", e, g->edges[abs_edge(e, 0)], g->edges[abs_edge(e, 1)]); + SETBIT(deleted, abs_edge(e, 0)); + + v2 = g->edges[abs_edge(e, 0)]; + if (v2 == v1) v2 = g->edges[abs_edge(e, 1)]; + + DEBUGP("Checking if second endpoint %u has degree 1\n", v2); + degree1 = find_degree1_edge(g, v2, deleted, &e); + if (degree1) + { + DEBUGP("Inspecting vertex %u\n", v2); + v1 = v2; + } + else break; + } +} + +int graph_is_cyclic(graph_t *g) +{ + cmph_uint32 i; + cmph_uint32 v; + cmph_uint8 *deleted = (cmph_uint8 *)malloc((g->nedges*sizeof(cmph_uint8))/8 + 1); + size_t deleted_len = g->nedges/8 + 1; + memset(deleted, 0, deleted_len); + + DEBUGP("Looking for cycles in graph with %u vertices and %u edges\n", g->nnodes, g->nedges); + for (v = 0; v < g->nnodes; ++v) + { + cyclic_del_edge(g, v, deleted); + } + for (i = 0; i < g->nedges; ++i) + { + if (!(GETBIT(deleted, i))) + { + DEBUGP("Edge %u %u->%u was not deleted\n", i, g->edges[i], g->edges[i + g->nedges]); + free(deleted); + return 1; + } + } + free(deleted); + return 0; +} + +cmph_uint8 graph_node_is_critical(graph_t * g, cmph_uint32 v) /* included -- Fabiano */ +{ + return (cmph_uint8)GETBIT(g->critical_nodes,v); +} + +void graph_obtain_critical_nodes(graph_t *g) /* included -- Fabiano*/ +{ + cmph_uint32 i; + cmph_uint32 v; + cmph_uint8 *deleted = (cmph_uint8 *)malloc((g->nedges*sizeof(cmph_uint8))/8+1); + size_t deleted_len = g->nedges/8 + 1; + memset(deleted, 0, deleted_len); + free(g->critical_nodes); + g->critical_nodes = (cmph_uint8 *)malloc((g->nnodes*sizeof(cmph_uint8))/8 + 1); + g->ncritical_nodes = 0; + memset(g->critical_nodes, 0, (g->nnodes*sizeof(cmph_uint8))/8 + 1); + DEBUGP("Looking for the 2-core in graph with %u vertices and %u edges\n", g->nnodes, g->nedges); + for (v = 0; v < g->nnodes; ++v) + { + cyclic_del_edge(g, v, deleted); + } + + for (i = 0; i < g->nedges; ++i) + { + if (!(GETBIT(deleted,i))) + { + DEBUGP("Edge %u %u->%u belongs to the 2-core\n", i, g->edges[i], g->edges[i + g->nedges]); + if(!(GETBIT(g->critical_nodes,g->edges[i]))) + { + g->ncritical_nodes ++; + SETBIT(g->critical_nodes,g->edges[i]); + } + if(!(GETBIT(g->critical_nodes,g->edges[i + g->nedges]))) + { + g->ncritical_nodes ++; + SETBIT(g->critical_nodes,g->edges[i + g->nedges]); + } + } + } + free(deleted); +} + +cmph_uint8 graph_contains_edge(graph_t *g, cmph_uint32 v1, cmph_uint32 v2) /* included -- Fabiano*/ +{ + cmph_uint32 e; + e = g->first[v1]; + if(e == EMPTY) return 0; + if (check_edge(g, e, v1, v2)) return 1; + do + { + e = g->next[e]; + if(e == EMPTY) return 0; + } + while (!check_edge(g, e, v1, v2)); + return 1; +} + +cmph_uint32 graph_vertex_id(graph_t *g, cmph_uint32 e, cmph_uint32 id) /* included -- Fabiano*/ +{ + return (g->edges[e + id*g->nedges]); +} + +cmph_uint32 graph_ncritical_nodes(graph_t *g) /* included -- Fabiano*/ +{ + return g->ncritical_nodes; +} + +graph_iterator_t graph_neighbors_it(graph_t *g, cmph_uint32 v) +{ + graph_iterator_t it; + it.vertex = v; + it.edge = g->first[v]; + return it; +} +cmph_uint32 graph_next_neighbor(graph_t *g, graph_iterator_t* it) +{ + cmph_uint32 ret; + if(it->edge == EMPTY) return GRAPH_NO_NEIGHBOR; + if (g->edges[it->edge] == it->vertex) ret = g->edges[it->edge + g->nedges]; + else ret = g->edges[it->edge]; + it->edge = g->next[it->edge]; + return ret; +} + + diff --git a/girepository/cmph/graph.h b/girepository/cmph/graph.h new file mode 100644 index 000000000..e1b5de6f6 --- /dev/null +++ b/girepository/cmph/graph.h @@ -0,0 +1,40 @@ +#ifndef _CMPH_GRAPH_H__ +#define _CMPH_GRAPH_H__ + +#include +#include "cmph_types.h" + +#define GRAPH_NO_NEIGHBOR UINT_MAX + +typedef struct __graph_t graph_t; +typedef struct __graph_iterator_t graph_iterator_t; +struct __graph_iterator_t +{ + cmph_uint32 vertex; + cmph_uint32 edge; +}; + + + +graph_t *graph_new(cmph_uint32 nnodes, cmph_uint32 nedges); +void graph_destroy(graph_t *graph); + +void graph_add_edge(graph_t *g, cmph_uint32 v1, cmph_uint32 v2); +void graph_del_edge(graph_t *g, cmph_uint32 v1, cmph_uint32 v2); +void graph_clear_edges(graph_t *g); +cmph_uint32 graph_edge_id(graph_t *g, cmph_uint32 v1, cmph_uint32 v2); +cmph_uint8 graph_contains_edge(graph_t *g, cmph_uint32 v1, cmph_uint32 v2); + +graph_iterator_t graph_neighbors_it(graph_t *g, cmph_uint32 v); +cmph_uint32 graph_next_neighbor(graph_t *g, graph_iterator_t* it); + +void graph_obtain_critical_nodes(graph_t *g); /* included -- Fabiano*/ +cmph_uint8 graph_node_is_critical(graph_t * g, cmph_uint32 v); /* included -- Fabiano */ +cmph_uint32 graph_ncritical_nodes(graph_t *g); /* included -- Fabiano*/ +cmph_uint32 graph_vertex_id(graph_t *g, cmph_uint32 e, cmph_uint32 id); /* included -- Fabiano*/ + +int graph_is_cyclic(graph_t *g); + +void graph_print(graph_t *); + +#endif diff --git a/girepository/cmph/hash.c b/girepository/cmph/hash.c new file mode 100644 index 000000000..be86d6e75 --- /dev/null +++ b/girepository/cmph/hash.c @@ -0,0 +1,216 @@ +#include "hash_state.h" +#include +#include +#include +#include + +//#define DEBUG +#include "debug.h" + +const char *cmph_hash_names[] = { "jenkins", NULL }; + +hash_state_t *hash_state_new(CMPH_HASH hashfunc, cmph_uint32 hashsize) +{ + hash_state_t *state = NULL; + switch (hashfunc) + { + case CMPH_HASH_JENKINS: + DEBUGP("Jenkins function - %u\n", hashsize); + state = (hash_state_t *)jenkins_state_new(hashsize); + DEBUGP("Jenkins function created\n"); + break; + default: + assert(0); + } + state->hashfunc = hashfunc; + return state; +} +cmph_uint32 hash(hash_state_t *state, const char *key, cmph_uint32 keylen) +{ + switch (state->hashfunc) + { + case CMPH_HASH_JENKINS: + return jenkins_hash((jenkins_state_t *)state, key, keylen); + default: + assert(0); + } + assert(0); + return 0; +} + +void hash_vector(hash_state_t *state, const char *key, cmph_uint32 keylen, cmph_uint32 * hashes) +{ + switch (state->hashfunc) + { + case CMPH_HASH_JENKINS: + jenkins_hash_vector_((jenkins_state_t *)state, key, keylen, hashes); + break; + default: + assert(0); + } +} + + +void hash_state_dump(hash_state_t *state, char **buf, cmph_uint32 *buflen) +{ + char *algobuf; + size_t len; + switch (state->hashfunc) + { + case CMPH_HASH_JENKINS: + jenkins_state_dump((jenkins_state_t *)state, &algobuf, buflen); + if (*buflen == UINT_MAX) return; + break; + default: + assert(0); + } + *buf = (char *)malloc(strlen(cmph_hash_names[state->hashfunc]) + 1 + *buflen); + memcpy(*buf, cmph_hash_names[state->hashfunc], strlen(cmph_hash_names[state->hashfunc]) + 1); + DEBUGP("Algobuf is %u\n", *(cmph_uint32 *)algobuf); + len = *buflen; + memcpy(*buf + strlen(cmph_hash_names[state->hashfunc]) + 1, algobuf, len); + *buflen = (cmph_uint32)strlen(cmph_hash_names[state->hashfunc]) + 1 + *buflen; + free(algobuf); + return; +} + +hash_state_t * hash_state_copy(hash_state_t *src_state) +{ + hash_state_t *dest_state = NULL; + switch (src_state->hashfunc) + { + case CMPH_HASH_JENKINS: + dest_state = (hash_state_t *)jenkins_state_copy((jenkins_state_t *)src_state); + break; + default: + assert(0); + } + dest_state->hashfunc = src_state->hashfunc; + return dest_state; +} + +hash_state_t *hash_state_load(const char *buf, cmph_uint32 buflen) +{ + cmph_uint32 i; + cmph_uint32 offset; + CMPH_HASH hashfunc = CMPH_HASH_COUNT; + for (i = 0; i < CMPH_HASH_COUNT; ++i) + { + if (strcmp(buf, cmph_hash_names[i]) == 0) + { + hashfunc = i; + break; + } + } + if (hashfunc == CMPH_HASH_COUNT) return NULL; + offset = (cmph_uint32)strlen(cmph_hash_names[hashfunc]) + 1; + switch (hashfunc) + { + case CMPH_HASH_JENKINS: + return (hash_state_t *)jenkins_state_load(buf + offset, buflen - offset); + default: + return NULL; + } + return NULL; +} +void hash_state_destroy(hash_state_t *state) +{ + switch (state->hashfunc) + { + case CMPH_HASH_JENKINS: + jenkins_state_destroy((jenkins_state_t *)state); + break; + default: + assert(0); + } + return; +} + +/** \fn void hash_state_pack(hash_state_t *state, void *hash_packed) + * \brief Support the ability to pack a hash function into a preallocated contiguous memory space pointed by hash_packed. + * \param state points to the hash function + * \param hash_packed pointer to the contiguous memory area used to store the hash function. The size of hash_packed must be at least hash_state_packed_size() + * + * Support the ability to pack a hash function into a preallocated contiguous memory space pointed by hash_packed. + * However, the hash function type must be packed outside. + */ +void hash_state_pack(hash_state_t *state, void *hash_packed) +{ + switch (state->hashfunc) + { + case CMPH_HASH_JENKINS: + // pack the jenkins hash function + jenkins_state_pack((jenkins_state_t *)state, hash_packed); + break; + default: + assert(0); + } + return; +} + +/** \fn cmph_uint32 hash_state_packed_size(CMPH_HASH hashfunc) + * \brief Return the amount of space needed to pack a hash function. + * \param hashfunc function type + * \return the size of the packed function or zero for failures + */ +cmph_uint32 hash_state_packed_size(CMPH_HASH hashfunc) +{ + cmph_uint32 size = 0; + switch (hashfunc) + { + case CMPH_HASH_JENKINS: + size += jenkins_state_packed_size(); + break; + default: + assert(0); + } + return size; +} + +/** \fn cmph_uint32 hash_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen) + * \param hash_packed is a pointer to a contiguous memory area + * \param hashfunc is the type of the hash function packed in hash_packed + * \param key is a pointer to a key + * \param keylen is the key length + * \return an integer that represents a hash value of 32 bits. + */ +cmph_uint32 hash_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen) +{ + switch (hashfunc) + { + case CMPH_HASH_JENKINS: + return jenkins_hash_packed(hash_packed, k, keylen); + default: + assert(0); + } + assert(0); + return 0; +} + +/** \fn hash_vector_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes) + * \param hash_packed is a pointer to a contiguous memory area + * \param key is a pointer to a key + * \param keylen is the key length + * \param hashes is a pointer to a memory large enough to fit three 32-bit integers. + */ +void hash_vector_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes) +{ + switch (hashfunc) + { + case CMPH_HASH_JENKINS: + jenkins_hash_vector_packed(hash_packed, k, keylen, hashes); + break; + default: + assert(0); + } +} + + +/** \fn CMPH_HASH hash_get_type(hash_state_t *state); + * \param state is a pointer to a hash_state_t structure + * \return the hash function type pointed by state + */ +CMPH_HASH hash_get_type(hash_state_t *state) +{ + return state->hashfunc; +} diff --git a/girepository/cmph/hash.h b/girepository/cmph/hash.h new file mode 100644 index 000000000..0ec4ce1c7 --- /dev/null +++ b/girepository/cmph/hash.h @@ -0,0 +1,76 @@ +#ifndef __CMPH_HASH_H__ +#define __CMPH_HASH_H__ + +#include "cmph_types.h" + +typedef union __hash_state_t hash_state_t; + +hash_state_t *hash_state_new(CMPH_HASH, cmph_uint32 hashsize); + +/** \fn cmph_uint32 hash(hash_state_t *state, const char *key, cmph_uint32 keylen); + * \param state is a pointer to a hash_state_t structure + * \param key is a pointer to a key + * \param keylen is the key length + * \return an integer that represents a hash value of 32 bits. + */ +cmph_uint32 hash(hash_state_t *state, const char *key, cmph_uint32 keylen); + +/** \fn void hash_vector(hash_state_t *state, const char *key, cmph_uint32 keylen, cmph_uint32 * hashes); + * \param state is a pointer to a hash_state_t structure + * \param key is a pointer to a key + * \param keylen is the key length + * \param hashes is a pointer to a memory large enough to fit three 32-bit integers. + */ +void hash_vector(hash_state_t *state, const char *key, cmph_uint32 keylen, cmph_uint32 * hashes); + +void hash_state_dump(hash_state_t *state, char **buf, cmph_uint32 *buflen); + +hash_state_t * hash_state_copy(hash_state_t *src_state); + +hash_state_t *hash_state_load(const char *buf, cmph_uint32 buflen); + +void hash_state_destroy(hash_state_t *state); + +/** \fn void hash_state_pack(hash_state_t *state, void *hash_packed); + * \brief Support the ability to pack a hash function into a preallocated contiguous memory space pointed by hash_packed. + * \param state points to the hash function + * \param hash_packed pointer to the contiguous memory area used to store the hash function. The size of hash_packed must be at least hash_state_packed_size() + * + * Support the ability to pack a hash function into a preallocated contiguous memory space pointed by hash_packed. + * However, the hash function type must be packed outside. + */ +void hash_state_pack(hash_state_t *state, void *hash_packed); + +/** \fn cmph_uint32 hash_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen); + * \param hash_packed is a pointer to a contiguous memory area + * \param hashfunc is the type of the hash function packed in hash_packed + * \param key is a pointer to a key + * \param keylen is the key length + * \return an integer that represents a hash value of 32 bits. + */ +cmph_uint32 hash_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen); + +/** \fn cmph_uint32 hash_state_packed_size(CMPH_HASH hashfunc) + * \brief Return the amount of space needed to pack a hash function. + * \param hashfunc function type + * \return the size of the packed function or zero for failures + */ +cmph_uint32 hash_state_packed_size(CMPH_HASH hashfunc); + + +/** \fn hash_vector_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes); + * \param hash_packed is a pointer to a contiguous memory area + * \param key is a pointer to a key + * \param keylen is the key length + * \param hashes is a pointer to a memory large enough to fit three 32-bit integers. + */ +void hash_vector_packed(void *hash_packed, CMPH_HASH hashfunc, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes); + + +/** \fn CMPH_HASH hash_get_type(hash_state_t *state); + * \param state is a pointer to a hash_state_t structure + * \return the hash function type pointed by state + */ +CMPH_HASH hash_get_type(hash_state_t *state); + +#endif diff --git a/girepository/cmph/hash_state.h b/girepository/cmph/hash_state.h new file mode 100644 index 000000000..1b567dca1 --- /dev/null +++ b/girepository/cmph/hash_state.h @@ -0,0 +1,12 @@ +#ifndef __HASH_STATE_H__ +#define __HASH_STATE_H__ + +#include "hash.h" +#include "jenkins_hash.h" +union __hash_state_t +{ + CMPH_HASH hashfunc; + jenkins_state_t jenkins; +}; + +#endif diff --git a/girepository/cmph/hashtree.c b/girepository/cmph/hashtree.c new file mode 100644 index 000000000..2f3567e55 --- /dev/null +++ b/girepository/cmph/hashtree.c @@ -0,0 +1,289 @@ +#include "graph.h" +#include "hashtree.h" +#include "cmph_structs.h" +#include "hastree_structs.h" +#include "hash.h" +#include "bitbool.h" + +#include +#include +#include +#include +#include + +//#define DEBUG +#include "debug.h" + +hashtree_config_data_t *hashtree_config_new() +{ + hashtree_config_data_t *hashtree; + hashtree = (hashtree_config_data_t *)malloc(sizeof(hashtree_config_data_t)); + if (!hashtree) return NULL; + memset(hashtree, 0, sizeof(hashtree_config_data_t)); + hashtree->hashfuncs[0] = CMPH_HASH_JENKINS; + hashtree->hashfuncs[1] = CMPH_HASH_JENKINS; + hashtree->hashfuncs[2] = CMPH_HASH_JENKINS; + hashtree->memory = 32 * 1024 * 1024; + return hashtree; +} +void hashtree_config_destroy(cmph_config_t *mph) +{ + hashtree_config_data_t *data = (hashtree_config_data_t *)mph->data; + DEBUGP("Destroying algorithm dependent data\n"); + free(data); +} + +void hashtree_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs) +{ + hashtree_config_data_t *hashtree = (hashtree_config_data_t *)mph->data; + CMPH_HASH *hashptr = hashfuncs; + cmph_uint32 i = 0; + while(*hashptr != CMPH_HASH_COUNT) + { + if (i >= 3) break; //hashtree only uses three hash functions + hashtree->hashfuncs[i] = *hashptr; + ++i, ++hashptr; + } +} + +cmph_t *hashtree_new(cmph_config_t *mph, double c) +{ + cmph_t *mphf = NULL; + hashtree_data_t *hashtreef = NULL; + + cmph_uint32 i; + cmph_uint32 iterations = 20; + cmph_uint8 *visited = NULL; + hashtree_config_data_t *hashtree = (hashtree_config_data_t *)mph->data; + hashtree->m = mph->key_source->nkeys; + hashtree->n = ceil(c * mph->key_source->nkeys); + DEBUGP("m (edges): %u n (vertices): %u c: %f\n", hashtree->m, hashtree->n, c); + hashtree->graph = graph_new(hashtree->n, hashtree->m); + DEBUGP("Created graph\n"); + + hashtree->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*3); + for(i = 0; i < 3; ++i) hashtree->hashes[i] = NULL; + //Mapping step + if (mph->verbosity) + { + fprintf(stderr, "Entering mapping step for mph creation of %u keys with graph sized %u\n", hashtree->m, hashtree->n); + } + while(1) + { + int ok; + hashtree->hashes[0] = hash_state_new(hashtree->hashfuncs[0], hashtree->n); + hashtree->hashes[1] = hash_state_new(hashtree->hashfuncs[1], hashtree->n); + ok = hashtree_gen_edges(mph); + if (!ok) + { + --iterations; + hash_state_destroy(hashtree->hashes[0]); + hashtree->hashes[0] = NULL; + hash_state_destroy(hashtree->hashes[1]); + hashtree->hashes[1] = NULL; + DEBUGP("%u iterations remaining\n", iterations); + if (mph->verbosity) + { + fprintf(stderr, "Acyclic graph creation failure - %u iterations remaining\n", iterations); + } + if (iterations == 0) break; + } + else break; + } + if (iterations == 0) + { + graph_destroy(hashtree->graph); + return NULL; + } + + //Assignment step + if (mph->verbosity) + { + fprintf(stderr, "Starting assignment step\n"); + } + DEBUGP("Assignment step\n"); + visited = (char *)malloc(hashtree->n/8 + 1); + memset(visited, 0, hashtree->n/8 + 1); + free(hashtree->g); + hashtree->g = (cmph_uint32 *)malloc(hashtree->n * sizeof(cmph_uint32)); + assert(hashtree->g); + for (i = 0; i < hashtree->n; ++i) + { + if (!GETBIT(visited,i)) + { + hashtree->g[i] = 0; + hashtree_traverse(hashtree, visited, i); + } + } + graph_destroy(hashtree->graph); + free(visited); + hashtree->graph = NULL; + + mphf = (cmph_t *)malloc(sizeof(cmph_t)); + mphf->algo = mph->algo; + hashtreef = (hashtree_data_t *)malloc(sizeof(hashtree_data_t)); + hashtreef->g = hashtree->g; + hashtree->g = NULL; //transfer memory ownership + hashtreef->hashes = hashtree->hashes; + hashtree->hashes = NULL; //transfer memory ownership + hashtreef->n = hashtree->n; + hashtreef->m = hashtree->m; + mphf->data = hashtreef; + mphf->size = hashtree->m; + DEBUGP("Successfully generated minimal perfect hash\n"); + if (mph->verbosity) + { + fprintf(stderr, "Successfully generated minimal perfect hash function\n"); + } + return mphf; +} + +static void hashtree_traverse(hashtree_config_data_t *hashtree, cmph_uint8 *visited, cmph_uint32 v) +{ + + graph_iterator_t it = graph_neighbors_it(hashtree->graph, v); + cmph_uint32 neighbor = 0; + SETBIT(visited,v); + + DEBUGP("Visiting vertex %u\n", v); + while((neighbor = graph_next_neighbor(hashtree->graph, &it)) != GRAPH_NO_NEIGHBOR) + { + DEBUGP("Visiting neighbor %u\n", neighbor); + if(GETBIT(visited,neighbor)) continue; + DEBUGP("Visiting neighbor %u\n", neighbor); + DEBUGP("Visiting edge %u->%u with id %u\n", v, neighbor, graph_edge_id(hashtree->graph, v, neighbor)); + hashtree->g[neighbor] = graph_edge_id(hashtree->graph, v, neighbor) - hashtree->g[v]; + DEBUGP("g is %u (%u - %u mod %u)\n", hashtree->g[neighbor], graph_edge_id(hashtree->graph, v, neighbor), hashtree->g[v], hashtree->m); + hashtree_traverse(hashtree, visited, neighbor); + } +} + +static int hashtree_gen_edges(cmph_config_t *mph) +{ + cmph_uint32 e; + hashtree_config_data_t *hashtree = (hashtree_config_data_t *)mph->data; + int cycles = 0; + + DEBUGP("Generating edges for %u vertices with hash functions %s and %s\n", hashtree->n, cmph_hash_names[hashtree->hashfuncs[0]], cmph_hash_names[hashtree->hashfuncs[1]]); + graph_clear_edges(hashtree->graph); + mph->key_source->rewind(mph->key_source->data); + for (e = 0; e < mph->key_source->nkeys; ++e) + { + cmph_uint32 h1, h2; + cmph_uint32 keylen; + char *key; + mph->key_source->read(mph->key_source->data, &key, &keylen); + h1 = hash(hashtree->hashes[0], key, keylen) % hashtree->n; + h2 = hash(hashtree->hashes[1], key, keylen) % hashtree->n; + if (h1 == h2) if (++h2 >= hashtree->n) h2 = 0; + if (h1 == h2) + { + if (mph->verbosity) fprintf(stderr, "Self loop for key %u\n", e); + mph->key_source->dispose(mph->key_source->data, key, keylen); + return 0; + } + DEBUGP("Adding edge: %u -> %u for key %s\n", h1, h2, key); + mph->key_source->dispose(mph->key_source->data, key, keylen); + graph_add_edge(hashtree->graph, h1, h2); + } + cycles = graph_is_cyclic(hashtree->graph); + if (mph->verbosity && cycles) fprintf(stderr, "Cyclic graph generated\n"); + DEBUGP("Looking for cycles: %u\n", cycles); + + return ! cycles; +} + +int hashtree_dump(cmph_t *mphf, FILE *fd) +{ + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 two = 2; //number of hash functions + hashtree_data_t *data = (hashtree_data_t *)mphf->data; + __cmph_dump(mphf, fd); + + fwrite(&two, sizeof(cmph_uint32), 1, fd); + hash_state_dump(data->hashes[0], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + fwrite(&buflen, sizeof(cmph_uint32), 1, fd); + fwrite(buf, buflen, 1, fd); + free(buf); + + hash_state_dump(data->hashes[1], &buf, &buflen); + DEBUGP("Dumping hash state with %u bytes to disk\n", buflen); + fwrite(&buflen, sizeof(cmph_uint32), 1, fd); + fwrite(buf, buflen, 1, fd); + free(buf); + + fwrite(&(data->n), sizeof(cmph_uint32), 1, fd); + fwrite(&(data->m), sizeof(cmph_uint32), 1, fd); + + fwrite(data->g, sizeof(cmph_uint32)*data->n, 1, fd); + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < data->n; ++i) fprintf(stderr, "%u ", data->g[i]); + fprintf(stderr, "\n"); + #endif + return 1; +} + +void hashtree_load(FILE *f, cmph_t *mphf) +{ + cmph_uint32 nhashes; + char *buf = NULL; + cmph_uint32 buflen; + cmph_uint32 i; + hashtree_data_t *hashtree = (hashtree_data_t *)malloc(sizeof(hashtree_data_t)); + + DEBUGP("Loading hashtree mphf\n"); + mphf->data = hashtree; + fread(&nhashes, sizeof(cmph_uint32), 1, f); + hashtree->hashes = (hash_state_t **)malloc(sizeof(hash_state_t *)*(nhashes + 1)); + hashtree->hashes[nhashes] = NULL; + DEBUGP("Reading %u hashes\n", nhashes); + for (i = 0; i < nhashes; ++i) + { + hash_state_t *state = NULL; + fread(&buflen, sizeof(cmph_uint32), 1, f); + DEBUGP("Hash state has %u bytes\n", buflen); + buf = (char *)malloc(buflen); + fread(buf, buflen, 1, f); + state = hash_state_load(buf, buflen); + hashtree->hashes[i] = state; + free(buf); + } + + DEBUGP("Reading m and n\n"); + fread(&(hashtree->n), sizeof(cmph_uint32), 1, f); + fread(&(hashtree->m), sizeof(cmph_uint32), 1, f); + + hashtree->g = (cmph_uint32 *)malloc(sizeof(cmph_uint32)*hashtree->n); + fread(hashtree->g, hashtree->n*sizeof(cmph_uint32), 1, f); + #ifdef DEBUG + fprintf(stderr, "G: "); + for (i = 0; i < hashtree->n; ++i) fprintf(stderr, "%u ", hashtree->g[i]); + fprintf(stderr, "\n"); + #endif + return; +} + + +cmph_uint32 hashtree_search(cmph_t *mphf, const char *key, cmph_uint32 keylen) +{ + hashtree_data_t *hashtree = mphf->data; + cmph_uint32 h1 = hash(hashtree->hashes[0], key, keylen) % hashtree->n; + cmph_uint32 h2 = hash(hashtree->hashes[1], key, keylen) % hashtree->n; + DEBUGP("key: %s h1: %u h2: %u\n", key, h1, h2); + if (h1 == h2 && ++h2 >= hashtree->n) h2 = 0; + DEBUGP("key: %s g[h1]: %u g[h2]: %u edges: %u\n", key, hashtree->g[h1], hashtree->g[h2], hashtree->m); + return (hashtree->g[h1] + hashtree->g[h2]) % hashtree->m; +} +void hashtree_destroy(cmph_t *mphf) +{ + hashtree_data_t *data = (hashtree_data_t *)mphf->data; + free(data->g); + hash_state_destroy(data->hashes[0]); + hash_state_destroy(data->hashes[1]); + free(data->hashes); + free(data); + free(mphf); +} diff --git a/girepository/cmph/hashtree.h b/girepository/cmph/hashtree.h new file mode 100644 index 000000000..8bff67462 --- /dev/null +++ b/girepository/cmph/hashtree.h @@ -0,0 +1,19 @@ +#ifndef __CMPH_HASHTREE_H__ +#define __CMPH_HASHTREE_H__ + +#include "cmph.h" + +typedef struct __hashtree_data_t hashtree_data_t; +typedef struct __hashtree_config_data_t hashtree_config_data_t; + +hashtree_config_data_t *hashtree_config_new(); +void hashtree_config_set_hashfuncs(cmph_config_t *mph, CMPH_HASH *hashfuncs); +void hashtree_config_set_leaf_algo(cmph_config_t *mph, CMPH_ALGO leaf_algo); +void hashtree_config_destroy(cmph_config_t *mph); +cmph_t *hashtree_new(cmph_config_t *mph, double c); + +void hashtree_load(FILE *f, cmph_t *mphf); +int hashtree_dump(cmph_t *mphf, FILE *f); +void hashtree_destroy(cmph_t *mphf); +cmph_uint32 hashtree_search(cmph_t *mphf, const char *key, cmph_uint32 keylen); +#endif diff --git a/girepository/cmph/hashtree_structs.h b/girepository/cmph/hashtree_structs.h new file mode 100644 index 000000000..7258cd399 --- /dev/null +++ b/girepository/cmph/hashtree_structs.h @@ -0,0 +1,32 @@ +#ifndef __CMPH_HASHTREE_STRUCTS_H__ +#define __CMPH_HASHTREE_STRUCTS_H__ + +#include "hash_state.h" + +struct __hashtree_data_t +{ + cmph_uint32 m; //edges (words) count + double c; //constant c + cmph_uint8 *size; //size[i] stores the number of edges represented by g[i] + cmph_uint32 **g; + cmph_uint32 k; //number of components + hash_state_t **h1; + hash_state_t **h2; + hash_state_t *h3; +}; + +struct __hashtree_config_data_t +{ + CMPH_ALGO leaf_algo; + CMPH_HASH hashfuncs[3]; + cmph_uint32 m; //edges (words) count + cmph_uint8 *size; //size[i] stores the number of edges represented by g[i] + cmph_uint32 *offset; //offset[i] stores the sum size[0] + ... size[i - 1] + cmph_uint32 k; //number of components + cmph_uint32 memory; + hash_state_t **h1; + hash_state_t **h2; + hash_state_t *h3; +}; + +#endif diff --git a/girepository/cmph/jenkins_hash.c b/girepository/cmph/jenkins_hash.c new file mode 100644 index 000000000..65cdff959 --- /dev/null +++ b/girepository/cmph/jenkins_hash.c @@ -0,0 +1,297 @@ +#include "jenkins_hash.h" +#include +#ifdef WIN32 +#define _USE_MATH_DEFINES //For M_LOG2E +#endif +#include +#include +#include + +//#define DEBUG +#include "debug.h" + +#define hashsize(n) ((cmph_uint32)1<<(n)) +#define hashmask(n) (hashsize(n)-1) + + + +//#define NM2 /* Define this if you do not want power of 2 table sizes*/ + + +/* + -------------------------------------------------------------------- + mix -- mix 3 32-bit values reversibly. + For every delta with one or two bits set, and the deltas of all three + high bits or all three low bits, whether the original value of a,b,c + is almost all zero or is uniformly distributed, + * If mix() is run forward or backward, at least 32 bits in a,b,c + have at least 1/4 probability of changing. + * If mix() is run forward, every bit of c will change between 1/3 and + 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) + mix() was built out of 36 single-cycle latency instructions in a + structure that could supported 2x parallelism, like so: + a -= b; + a -= c; x = (c>>13); + b -= c; a ^= x; + b -= a; x = (a<<8); + c -= a; b ^= x; + c -= b; x = (b>>13); + ... + Unfortunately, superscalar Pentiums and Sparcs can't take advantage + of that parallelism. They've also turned some of those single-cycle + latency instructions into multi-cycle latency instructions. Still, + this is the fastest good hash I could find. There were about 2^^68 + to choose from. I only looked at a billion or so. + -------------------------------------------------------------------- + */ +#define mix(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +/* + -------------------------------------------------------------------- + hash() -- hash a variable-length key into a 32-bit value +k : the key (the unaligned variable-length array of bytes) +len : the length of the key, counting by bytes +initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Every 1-bit and 2-bit delta achieves avalanche. +About 6*len+35 instructions. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do +h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (cmph_uint8 **)k, do it like this: +for (i=0, h=0; iseed = ((cmph_uint32)rand() % size); + return state; +} +void jenkins_state_destroy(jenkins_state_t *state) +{ + free(state); +} + + +static inline void __jenkins_hash_vector(cmph_uint32 seed, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes) +{ + register cmph_uint32 len, length; + + /* Set up the internal state */ + length = keylen; + len = length; + hashes[0] = hashes[1] = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + hashes[2] = seed; /* the previous hash value - seed in our case */ + + /*---------------------------------------- handle most of the key */ + while (len >= 12) + { + hashes[0] += ((cmph_uint32)k[0] +((cmph_uint32)k[1]<<8) +((cmph_uint32)k[2]<<16) +((cmph_uint32)k[3]<<24)); + hashes[1] += ((cmph_uint32)k[4] +((cmph_uint32)k[5]<<8) +((cmph_uint32)k[6]<<16) +((cmph_uint32)k[7]<<24)); + hashes[2] += ((cmph_uint32)k[8] +((cmph_uint32)k[9]<<8) +((cmph_uint32)k[10]<<16)+((cmph_uint32)k[11]<<24)); + mix(hashes[0],hashes[1],hashes[2]); + k += 12; len -= 12; + } + + /*------------------------------------- handle the last 11 bytes */ + hashes[2] += length; + switch(len) /* all the case statements fall through */ + { + case 11: + hashes[2] +=((cmph_uint32)k[10]<<24); + case 10: + hashes[2] +=((cmph_uint32)k[9]<<16); + case 9 : + hashes[2] +=((cmph_uint32)k[8]<<8); + /* the first byte of hashes[2] is reserved for the length */ + case 8 : + hashes[1] +=((cmph_uint32)k[7]<<24); + case 7 : + hashes[1] +=((cmph_uint32)k[6]<<16); + case 6 : + hashes[1] +=((cmph_uint32)k[5]<<8); + case 5 : + hashes[1] +=(cmph_uint8) k[4]; + case 4 : + hashes[0] +=((cmph_uint32)k[3]<<24); + case 3 : + hashes[0] +=((cmph_uint32)k[2]<<16); + case 2 : + hashes[0] +=((cmph_uint32)k[1]<<8); + case 1 : + hashes[0] +=(cmph_uint8)k[0]; + /* case 0: nothing left to add */ + } + + mix(hashes[0],hashes[1],hashes[2]); +} + +cmph_uint32 jenkins_hash(jenkins_state_t *state, const char *k, cmph_uint32 keylen) +{ + cmph_uint32 hashes[3]; + __jenkins_hash_vector(state->seed, k, keylen, hashes); + return hashes[2]; +/* cmph_uint32 a, b, c; + cmph_uint32 len, length; + + // Set up the internal state + length = keylen; + len = length; + a = b = 0x9e3779b9; // the golden ratio; an arbitrary value + c = state->seed; // the previous hash value - seed in our case + + // handle most of the key + while (len >= 12) + { + a += (k[0] +((cmph_uint32)k[1]<<8) +((cmph_uint32)k[2]<<16) +((cmph_uint32)k[3]<<24)); + b += (k[4] +((cmph_uint32)k[5]<<8) +((cmph_uint32)k[6]<<16) +((cmph_uint32)k[7]<<24)); + c += (k[8] +((cmph_uint32)k[9]<<8) +((cmph_uint32)k[10]<<16)+((cmph_uint32)k[11]<<24)); + mix(a,b,c); + k += 12; len -= 12; + } + + // handle the last 11 bytes + c += length; + switch(len) /// all the case statements fall through + { + case 11: + c +=((cmph_uint32)k[10]<<24); + case 10: + c +=((cmph_uint32)k[9]<<16); + case 9 : + c +=((cmph_uint32)k[8]<<8); + // the first byte of c is reserved for the length + case 8 : + b +=((cmph_uint32)k[7]<<24); + case 7 : + b +=((cmph_uint32)k[6]<<16); + case 6 : + b +=((cmph_uint32)k[5]<<8); + case 5 : + b +=k[4]; + case 4 : + a +=((cmph_uint32)k[3]<<24); + case 3 : + a +=((cmph_uint32)k[2]<<16); + case 2 : + a +=((cmph_uint32)k[1]<<8); + case 1 : + a +=k[0]; + // case 0: nothing left to add + } + + mix(a,b,c); + + /// report the result + + return c; + */ +} + +void jenkins_hash_vector_(jenkins_state_t *state, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes) +{ + __jenkins_hash_vector(state->seed, k, keylen, hashes); +} + +void jenkins_state_dump(jenkins_state_t *state, char **buf, cmph_uint32 *buflen) +{ + *buflen = sizeof(cmph_uint32); + *buf = (char *)malloc(sizeof(cmph_uint32)); + if (!*buf) + { + *buflen = UINT_MAX; + return; + } + memcpy(*buf, &(state->seed), sizeof(cmph_uint32)); + DEBUGP("Dumped jenkins state with seed %u\n", state->seed); + return; +} + +jenkins_state_t *jenkins_state_copy(jenkins_state_t *src_state) +{ + jenkins_state_t *dest_state = (jenkins_state_t *)malloc(sizeof(jenkins_state_t)); + dest_state->hashfunc = src_state->hashfunc; + dest_state->seed = src_state->seed; + return dest_state; +} + +jenkins_state_t *jenkins_state_load(const char *buf, cmph_uint32 buflen) +{ + jenkins_state_t *state = (jenkins_state_t *)malloc(sizeof(jenkins_state_t)); + state->seed = *(cmph_uint32 *)buf; + state->hashfunc = CMPH_HASH_JENKINS; + DEBUGP("Loaded jenkins state with seed %u\n", state->seed); + return state; +} + + +/** \fn void jenkins_state_pack(jenkins_state_t *state, void *jenkins_packed); + * \brief Support the ability to pack a jenkins function into a preallocated contiguous memory space pointed by jenkins_packed. + * \param state points to the jenkins function + * \param jenkins_packed pointer to the contiguous memory area used to store the jenkins function. The size of jenkins_packed must be at least jenkins_state_packed_size() + */ +void jenkins_state_pack(jenkins_state_t *state, void *jenkins_packed) +{ + if (state && jenkins_packed) + { + memcpy(jenkins_packed, &(state->seed), sizeof(cmph_uint32)); + } +} + +/** \fn cmph_uint32 jenkins_state_packed_size(jenkins_state_t *state); + * \brief Return the amount of space needed to pack a jenkins function. + * \return the size of the packed function or zero for failures + */ +cmph_uint32 jenkins_state_packed_size(void) +{ + return sizeof(cmph_uint32); +} + + +/** \fn cmph_uint32 jenkins_hash_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen); + * \param jenkins_packed is a pointer to a contiguous memory area + * \param key is a pointer to a key + * \param keylen is the key length + * \return an integer that represents a hash value of 32 bits. + */ +cmph_uint32 jenkins_hash_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen) +{ + cmph_uint32 hashes[3]; + __jenkins_hash_vector(*((cmph_uint32 *)jenkins_packed), k, keylen, hashes); + return hashes[2]; +} + +/** \fn jenkins_hash_vector_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes); + * \param jenkins_packed is a pointer to a contiguous memory area + * \param key is a pointer to a key + * \param keylen is the key length + * \param hashes is a pointer to a memory large enough to fit three 32-bit integers. + */ +void jenkins_hash_vector_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes) +{ + __jenkins_hash_vector(*((cmph_uint32 *)jenkins_packed), k, keylen, hashes); +} diff --git a/girepository/cmph/jenkins_hash.h b/girepository/cmph/jenkins_hash.h new file mode 100644 index 000000000..39626e204 --- /dev/null +++ b/girepository/cmph/jenkins_hash.h @@ -0,0 +1,65 @@ +#ifndef __JEKINS_HASH_H__ +#define __JEKINS_HASH_H__ + +#include "hash.h" + +typedef struct __jenkins_state_t +{ + CMPH_HASH hashfunc; + cmph_uint32 seed; +} jenkins_state_t; + +jenkins_state_t *jenkins_state_new(cmph_uint32 size); //size of hash table + +/** \fn cmph_uint32 jenkins_hash(jenkins_state_t *state, const char *k, cmph_uint32 keylen); + * \param state is a pointer to a jenkins_state_t structure + * \param key is a pointer to a key + * \param keylen is the key length + * \return an integer that represents a hash value of 32 bits. + */ +cmph_uint32 jenkins_hash(jenkins_state_t *state, const char *k, cmph_uint32 keylen); + +/** \fn void jenkins_hash_vector_(jenkins_state_t *state, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes); + * \param state is a pointer to a jenkins_state_t structure + * \param key is a pointer to a key + * \param keylen is the key length + * \param hashes is a pointer to a memory large enough to fit three 32-bit integers. + */ +void jenkins_hash_vector_(jenkins_state_t *state, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes); + +void jenkins_state_dump(jenkins_state_t *state, char **buf, cmph_uint32 *buflen); +jenkins_state_t *jenkins_state_copy(jenkins_state_t *src_state); +jenkins_state_t *jenkins_state_load(const char *buf, cmph_uint32 buflen); +void jenkins_state_destroy(jenkins_state_t *state); + +/** \fn void jenkins_state_pack(jenkins_state_t *state, void *jenkins_packed); + * \brief Support the ability to pack a jenkins function into a preallocated contiguous memory space pointed by jenkins_packed. + * \param state points to the jenkins function + * \param jenkins_packed pointer to the contiguous memory area used to store the jenkins function. The size of jenkins_packed must be at least jenkins_state_packed_size() + */ +void jenkins_state_pack(jenkins_state_t *state, void *jenkins_packed); + +/** \fn cmph_uint32 jenkins_state_packed_size(); + * \brief Return the amount of space needed to pack a jenkins function. + * \return the size of the packed function or zero for failures + */ +cmph_uint32 jenkins_state_packed_size(void); + + +/** \fn cmph_uint32 jenkins_hash_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen); + * \param jenkins_packed is a pointer to a contiguous memory area + * \param key is a pointer to a key + * \param keylen is the key length + * \return an integer that represents a hash value of 32 bits. + */ +cmph_uint32 jenkins_hash_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen); + +/** \fn jenkins_hash_vector_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes); + * \param jenkins_packed is a pointer to a contiguous memory area + * \param key is a pointer to a key + * \param keylen is the key length + * \param hashes is a pointer to a memory large enough to fit three 32-bit integers. + */ +void jenkins_hash_vector_packed(void *jenkins_packed, const char *k, cmph_uint32 keylen, cmph_uint32 * hashes); + +#endif diff --git a/girepository/cmph/main.c b/girepository/cmph/main.c new file mode 100644 index 000000000..f739b325f --- /dev/null +++ b/girepository/cmph/main.c @@ -0,0 +1,342 @@ +#ifdef WIN32 +#include "wingetopt.h" +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include "cmph.h" +#include "hash.h" + +#ifdef WIN32 +#define VERSION "0.8" +#else +#include "config.h" +#endif + + +void usage(const char *prg) +{ + fprintf(stderr, "usage: %s [-v] [-h] [-V] [-k nkeys] [-f hash_function] [-g [-c algorithm_dependent_value][-s seed] ] [-a algorithm] [-M memory_in_MB] [-b algorithm_dependent_value] [-t keys_per_bin] [-d tmp_dir] [-m file.mph] keysfile\n", prg); +} +void usage_long(const char *prg) +{ + cmph_uint32 i; + fprintf(stderr, "usage: %s [-v] [-h] [-V] [-k nkeys] [-f hash_function] [-g [-c algorithm_dependent_value][-s seed] ] [-a algorithm] [-M memory_in_MB] [-b algorithm_dependent_value] [-t keys_per_bin] [-d tmp_dir] [-m file.mph] keysfile\n", prg); + fprintf(stderr, "Minimum perfect hashing tool\n\n"); + fprintf(stderr, " -h\t print this help message\n"); + fprintf(stderr, " -c\t c value determines:\n"); + fprintf(stderr, " \t * the number of vertices in the graph for the algorithms BMZ and CHM\n"); + fprintf(stderr, " \t * the number of bits per key required in the FCH algorithm\n"); + fprintf(stderr, " \t * the load factor in the CHD_PH algorithm\n"); + fprintf(stderr, " -a\t algorithm - valid values are\n"); + for (i = 0; i < CMPH_COUNT; ++i) fprintf(stderr, " \t * %s\n", cmph_names[i]); + fprintf(stderr, " -f\t hash function (may be used multiple times) - valid values are\n"); + for (i = 0; i < CMPH_HASH_COUNT; ++i) fprintf(stderr, " \t * %s\n", cmph_hash_names[i]); + fprintf(stderr, " -V\t print version number and exit\n"); + fprintf(stderr, " -v\t increase verbosity (may be used multiple times)\n"); + fprintf(stderr, " -k\t number of keys\n"); + fprintf(stderr, " -g\t generation mode\n"); + fprintf(stderr, " -s\t random seed\n"); + fprintf(stderr, " -m\t minimum perfect hash function file \n"); + fprintf(stderr, " -M\t main memory availability (in MB) used in BRZ algorithm \n"); + fprintf(stderr, " -d\t temporary directory used in BRZ algorithm \n"); + fprintf(stderr, " -b\t the meaning of this parameter depends on the algorithm selected in the -a option:\n"); + fprintf(stderr, " \t * For BRZ it is used to make the maximal number of keys in a bucket lower than 256.\n"); + fprintf(stderr, " \t In this case its value should be an integer in the range [64,175]. Default is 128.\n\n"); + fprintf(stderr, " \t * For BDZ it is used to determine the size of some precomputed rank\n"); + fprintf(stderr, " \t information and its value should be an integer in the range [3,10]. Default\n"); + fprintf(stderr, " \t is 7. The larger is this value, the more compact are the resulting functions\n"); + fprintf(stderr, " \t and the slower are them at evaluation time.\n\n"); + fprintf(stderr, " \t * For CHD and CHD_PH it is used to set the average number of keys per bucket\n"); + fprintf(stderr, " \t and its value should be an integer in the range [1,32]. Default is 4. The\n"); + fprintf(stderr, " \t larger is this value, the slower is the construction of the functions.\n"); + fprintf(stderr, " \t This parameter has no effect for other algorithms.\n\n"); + fprintf(stderr, " -t\t set the number of keys per bin for a t-perfect hashing function. A t-perfect\n"); + fprintf(stderr, " \t hash function allows at most t collisions in a given bin. This parameter applies\n"); + fprintf(stderr, " \t only to the CHD and CHD_PH algorithms. Its value should be an integer in the\n"); + fprintf(stderr, " \t range [1,128]. Defaul is 1\n"); + fprintf(stderr, " keysfile\t line separated file with keys\n"); +} + +int main(int argc, char **argv) +{ + cmph_uint32 verbosity = 0; + char generate = 0; + char *mphf_file = NULL; + FILE *mphf_fd = stdout; + const char *keys_file = NULL; + FILE *keys_fd; + cmph_uint32 nkeys = UINT_MAX; + cmph_uint32 seed = UINT_MAX; + CMPH_HASH *hashes = NULL; + cmph_uint32 nhashes = 0; + cmph_uint32 i; + CMPH_ALGO mph_algo = CMPH_CHM; + double c = 0; + cmph_config_t *config = NULL; + cmph_t *mphf = NULL; + char * tmp_dir = NULL; + cmph_io_adapter_t *source; + cmph_uint32 memory_availability = 0; + cmph_uint32 b = 0; + cmph_uint32 keys_per_bin = 1; + while (1) + { + char ch = (char)getopt(argc, argv, "hVvgc:k:a:M:b:t:f:m:d:s:"); + if (ch == -1) break; + switch (ch) + { + case 's': + { + char *cptr; + seed = (cmph_uint32)strtoul(optarg, &cptr, 10); + if(*cptr != 0) { + fprintf(stderr, "Invalid seed %s\n", optarg); + exit(1); + } + } + break; + case 'c': + { + char *endptr; + c = strtod(optarg, &endptr); + if(*endptr != 0) { + fprintf(stderr, "Invalid c value %s\n", optarg); + exit(1); + } + } + break; + case 'g': + generate = 1; + break; + case 'k': + { + char *endptr; + nkeys = (cmph_uint32)strtoul(optarg, &endptr, 10); + if(*endptr != 0) { + fprintf(stderr, "Invalid number of keys %s\n", optarg); + exit(1); + } + } + break; + case 'm': + mphf_file = strdup(optarg); + break; + case 'd': + tmp_dir = strdup(optarg); + break; + case 'M': + { + char *cptr; + memory_availability = (cmph_uint32)strtoul(optarg, &cptr, 10); + if(*cptr != 0) { + fprintf(stderr, "Invalid memory availability %s\n", optarg); + exit(1); + } + } + break; + case 'b': + { + char *cptr; + b = (cmph_uint32)strtoul(optarg, &cptr, 10); + if(*cptr != 0) { + fprintf(stderr, "Parameter b was not found: %s\n", optarg); + exit(1); + } + } + break; + case 't': + { + char *cptr; + keys_per_bin = (cmph_uint32)strtoul(optarg, &cptr, 10); + if(*cptr != 0) { + fprintf(stderr, "Parameter t was not found: %s\n", optarg); + exit(1); + } + } + break; + case 'v': + ++verbosity; + break; + case 'V': + printf("%s\n", VERSION); + return 0; + case 'h': + usage_long(argv[0]); + return 0; + case 'a': + { + char valid = 0; + for (i = 0; i < CMPH_COUNT; ++i) + { + if (strcmp(cmph_names[i], optarg) == 0) + { + mph_algo = i; + valid = 1; + break; + } + } + if (!valid) + { + fprintf(stderr, "Invalid mph algorithm: %s. It is not available in version %s\n", optarg, VERSION); + return -1; + } + } + break; + case 'f': + { + char valid = 0; + for (i = 0; i < CMPH_HASH_COUNT; ++i) + { + if (strcmp(cmph_hash_names[i], optarg) == 0) + { + hashes = (CMPH_HASH *)realloc(hashes, sizeof(CMPH_HASH) * ( nhashes + 2 )); + hashes[nhashes] = i; + hashes[nhashes + 1] = CMPH_HASH_COUNT; + ++nhashes; + valid = 1; + break; + } + } + if (!valid) + { + fprintf(stderr, "Invalid hash function: %s\n", optarg); + return -1; + } + } + break; + default: + usage(argv[0]); + return 1; + } + } + + if (optind != argc - 1) + { + usage(argv[0]); + return 1; + } + keys_file = argv[optind]; + + if (seed == UINT_MAX) seed = (cmph_uint32)time(NULL); + srand(seed); + int ret = 0; + if (mphf_file == NULL) + { + mphf_file = (char *)malloc(strlen(keys_file) + 5); + memcpy(mphf_file, keys_file, strlen(keys_file)); + memcpy(mphf_file + strlen(keys_file), ".mph\0", (size_t)5); + } + + keys_fd = fopen(keys_file, "r"); + + if (keys_fd == NULL) + { + fprintf(stderr, "Unable to open file %s: %s\n", keys_file, strerror(errno)); + return -1; + } + + if (seed == UINT_MAX) seed = (cmph_uint32)time(NULL); + if(nkeys == UINT_MAX) source = cmph_io_nlfile_adapter(keys_fd); + else source = cmph_io_nlnkfile_adapter(keys_fd, nkeys); + if (generate) + { + //Create mphf + mphf_fd = fopen(mphf_file, "w"); + config = cmph_config_new(source); + cmph_config_set_algo(config, mph_algo); + if (nhashes) cmph_config_set_hashfuncs(config, hashes); + cmph_config_set_verbosity(config, verbosity); + cmph_config_set_tmp_dir(config, (cmph_uint8 *) tmp_dir); + cmph_config_set_mphf_fd(config, mphf_fd); + cmph_config_set_memory_availability(config, memory_availability); + cmph_config_set_b(config, b); + cmph_config_set_keys_per_bin(config, keys_per_bin); + + //if((mph_algo == CMPH_BMZ || mph_algo == CMPH_BRZ) && c >= 2.0) c=1.15; + if(mph_algo == CMPH_BMZ && c >= 2.0) c=1.15; + if (c != 0) cmph_config_set_graphsize(config, c); + mphf = cmph_new(config); + + cmph_config_destroy(config); + if (mphf == NULL) + { + fprintf(stderr, "Unable to create minimum perfect hashing function\n"); + //cmph_config_destroy(config); + free(mphf_file); + return -1; + } + + if (mphf_fd == NULL) + { + fprintf(stderr, "Unable to open output file %s: %s\n", mphf_file, strerror(errno)); + free(mphf_file); + return -1; + } + cmph_dump(mphf, mphf_fd); + cmph_destroy(mphf); + fclose(mphf_fd); + } + else + { + cmph_uint8 * hashtable = NULL; + mphf_fd = fopen(mphf_file, "r"); + if (mphf_fd == NULL) + { + fprintf(stderr, "Unable to open input file %s: %s\n", mphf_file, strerror(errno)); + free(mphf_file); + return -1; + } + mphf = cmph_load(mphf_fd); + fclose(mphf_fd); + if (!mphf) + { + fprintf(stderr, "Unable to parser input file %s\n", mphf_file); + free(mphf_file); + return -1; + } + cmph_uint32 siz = cmph_size(mphf); + hashtable = (cmph_uint8*)calloc(siz, sizeof(cmph_uint8)); + memset(hashtable, 0,(size_t) siz); + //check all keys + for (i = 0; i < source->nkeys; ++i) + { + cmph_uint32 h; + char *buf; + cmph_uint32 buflen = 0; + source->read(source->data, &buf, &buflen); + h = cmph_search(mphf, buf, buflen); + if (!(h < siz)) + { + fprintf(stderr, "Unknown key %*s in the input.\n", buflen, buf); + ret = 1; + } else if(hashtable[h] >= keys_per_bin) + { + fprintf(stderr, "More than %u keys were mapped to bin %u\n", keys_per_bin, h); + fprintf(stderr, "Duplicated or unknown key %*s in the input\n", buflen, buf); + ret = 1; + } else hashtable[h]++; + + if (verbosity) + { + printf("%s -> %u\n", buf, h); + } + source->dispose(source->data, buf, buflen); + } + + cmph_destroy(mphf); + free(hashtable); + } + fclose(keys_fd); + free(mphf_file); + free(tmp_dir); + cmph_io_nlfile_adapter_destroy(source); + return ret; + +} diff --git a/girepository/cmph/meson.build b/girepository/cmph/meson.build new file mode 100644 index 000000000..157b09e96 --- /dev/null +++ b/girepository/cmph/meson.build @@ -0,0 +1,77 @@ +cmph_sources = [ + 'bdz.c', + 'bdz_ph.c', + 'bmz8.c', + 'bmz.c', + 'brz.c', + 'buffer_entry.c', + 'buffer_manager.c', + 'chd.c', + 'chd_ph.c', + 'chm.c', + 'cmph.c', + 'cmph_structs.c', + 'compressed_rank.c', + 'compressed_seq.c', + 'fch_buckets.c', + 'fch.c', + 'graph.c', + 'hash.c', + 'jenkins_hash.c', + 'miller_rabin.c', + 'select.c', + 'vqueue.c', + 'vstack.c', +] + +cmph_deps = [ + glib_dep, + gobject_dep, + cc.find_library('m', required: false), +] + +custom_c_args = [] + +if cc.get_id() != 'msvc' + custom_c_args = cc.get_supported_arguments([ + '-Wno-implicit-fallthrough', + '-Wno-old-style-definition', + '-Wno-suggest-attribute=noreturn', + '-Wno-type-limits', + '-Wno-undef', + '-Wno-unused-parameter', + '-Wno-cast-align', + '-Wno-unused-function', + '-Wno-return-type', + '-Wno-sometimes-uninitialized', + ]) +endif + +cmph = static_library('cmph', + sources: cmph_sources, + c_args: gi_hidden_visibility_cflags + custom_c_args, + dependencies: cmph_deps, +) + +cmph_dep = declare_dependency( + link_with: cmph, + include_directories: include_directories('.'), +) + +if cc.get_id() != 'msvc' + custom_c_args = cc.get_supported_arguments([ + '-Wno-old-style-definition', + '-Wno-type-limits', + ]) +endif + +cmph_test = executable('cmph-bdz-test', '../cmph-bdz-test.c', + dependencies: [ + cmph_dep, + glib_dep, + gobject_dep, + ], + c_args: custom_c_args, +) + +test('cmph-bdz-test', cmph_test) diff --git a/girepository/cmph/miller_rabin.c b/girepository/cmph/miller_rabin.c new file mode 100644 index 000000000..17d0ed344 --- /dev/null +++ b/girepository/cmph/miller_rabin.c @@ -0,0 +1,67 @@ +#include "miller_rabin.h" + +static inline cmph_uint64 int_pow(cmph_uint64 a, cmph_uint64 d, cmph_uint64 n) +{ + cmph_uint64 a_pow = a; + cmph_uint64 res = 1; + while(d > 0) + { + if((d & 1) == 1) + res =(((cmph_uint64)res) * a_pow) % n; + a_pow = (((cmph_uint64)a_pow) * a_pow) % n; + d /= 2; + }; + return res; +}; + +static inline cmph_uint8 check_witness(cmph_uint64 a_exp_d, cmph_uint64 n, cmph_uint64 s) +{ + cmph_uint64 i; + cmph_uint64 a_exp = a_exp_d; + if(a_exp == 1 || a_exp == (n - 1)) + return 1; + for(i = 1; i < s; i++) + { + a_exp = (((cmph_uint64)a_exp) * a_exp) % n; + if(a_exp == (n - 1)) + return 1; + }; + return 0; +}; + +cmph_uint8 check_primality(cmph_uint64 n) +{ + cmph_uint64 a, d, s, a_exp_d; + if((n % 2) == 0) + return 0; + if((n % 3) == 0) + return 0; + if((n % 5) == 0) + return 0; + if((n % 7 ) == 0) + return 0; + //we decompoe the number n - 1 into 2^s*d + s = 0; + d = n - 1; + do + { + s++; + d /= 2; + }while((d % 2) == 0); + + a = 2; + a_exp_d = int_pow(a, d, n); + if(check_witness(a_exp_d, n, s) == 0) + return 0; + a = 7; + a_exp_d = int_pow(a, d, n); + if(check_witness(a_exp_d, n, s) == 0) + return 0; + a = 61; + a_exp_d = int_pow(a, d, n); + if(check_witness(a_exp_d, n, s) == 0) + return 0; + return 1; +}; + + diff --git a/girepository/cmph/miller_rabin.h b/girepository/cmph/miller_rabin.h new file mode 100644 index 000000000..42dc6ce5e --- /dev/null +++ b/girepository/cmph/miller_rabin.h @@ -0,0 +1,5 @@ +#ifndef _CMPH_MILLER_RABIN_H__ +#define _CMPH_MILLER_RABIN_H__ +#include "cmph_types.h" +cmph_uint8 check_primality(cmph_uint64 n); +#endif diff --git a/girepository/cmph/sdbm_hash.c b/girepository/cmph/sdbm_hash.c new file mode 100644 index 000000000..2f706c9ff --- /dev/null +++ b/girepository/cmph/sdbm_hash.c @@ -0,0 +1,49 @@ +#include "sdbm_hash.h" +#include + +sdbm_state_t *sdbm_state_new() +{ + sdbm_state_t *state = (sdbm_state_t *)malloc(sizeof(sdbm_state_t)); + state->hashfunc = CMPH_HASH_SDBM; + return state; +} + +void sdbm_state_destroy(sdbm_state_t *state) +{ + free(state); +} + +cmph_uint32 sdbm_hash(sdbm_state_t *state, const char *k, cmph_uint32 keylen) +{ + register cmph_uint32 hash = 0; + const unsigned char *ptr = (unsigned char *)k; + cmph_uint32 i = 0; + + while(i < keylen) { + hash = *ptr + (hash << 6) + (hash << 16) - hash; + ++ptr, ++i; + } + return hash; +} + + +void sdbm_state_dump(sdbm_state_t *state, char **buf, cmph_uint32 *buflen) +{ + *buf = NULL; + *buflen = 0; + return; +} + +sdbm_state_t *sdbm_state_copy(sdbm_state_t *src_state) +{ + sdbm_state_t *dest_state = (sdbm_state_t *)malloc(sizeof(sdbm_state_t)); + dest_state->hashfunc = src_state->hashfunc; + return dest_state; +} + +sdbm_state_t *sdbm_state_load(const char *buf, cmph_uint32 buflen) +{ + sdbm_state_t *state = (sdbm_state_t *)malloc(sizeof(sdbm_state_t)); + state->hashfunc = CMPH_HASH_SDBM; + return state; +} diff --git a/girepository/cmph/sdbm_hash.h b/girepository/cmph/sdbm_hash.h new file mode 100644 index 000000000..f44b2f15a --- /dev/null +++ b/girepository/cmph/sdbm_hash.h @@ -0,0 +1,18 @@ +#ifndef __SDBM_HASH_H__ +#define __SDBM_HASH_H__ + +#include "hash.h" + +typedef struct __sdbm_state_t +{ + CMPH_HASH hashfunc; +} sdbm_state_t; + +sdbm_state_t *sdbm_state_new(); +cmph_uint32 sdbm_hash(sdbm_state_t *state, const char *k, cmph_uint32 keylen); +void sdbm_state_dump(sdbm_state_t *state, char **buf, cmph_uint32 *buflen); +sdbm_state_t *sdbm_state_copy(sdbm_state_t *src_state); +sdbm_state_t *sdbm_state_load(const char *buf, cmph_uint32 buflen); +void sdbm_state_destroy(sdbm_state_t *state); + +#endif diff --git a/girepository/cmph/select.c b/girepository/cmph/select.c new file mode 100644 index 000000000..fec4b7ada --- /dev/null +++ b/girepository/cmph/select.c @@ -0,0 +1,337 @@ +#include +#include +#include +#include +#include +#include "select_lookup_tables.h" +#include "select.h" + +//#define DEBUG +#include "debug.h" + +#ifndef STEP_SELECT_TABLE +#define STEP_SELECT_TABLE 128 +#endif + +#ifndef NBITS_STEP_SELECT_TABLE +#define NBITS_STEP_SELECT_TABLE 7 +#endif + +#ifndef MASK_STEP_SELECT_TABLE +#define MASK_STEP_SELECT_TABLE 0x7f // 0x7f = 127 +#endif + +static inline void select_insert_0(cmph_uint32 * buffer) +{ + (*buffer) >>= 1; +}; + +static inline void select_insert_1(cmph_uint32 * buffer) +{ + (*buffer) >>= 1; + (*buffer) |= 0x80000000; +}; + +void select_init(select_t * sel) +{ + sel->n = 0; + sel->m = 0; + sel->bits_vec = 0; + sel->select_table = 0; +}; + +cmph_uint32 select_get_space_usage(select_t * sel) +{ + register cmph_uint32 nbits; + register cmph_uint32 vec_size; + register cmph_uint32 sel_table_size; + register cmph_uint32 space_usage; + + nbits = sel->n + sel->m; + vec_size = (nbits + 31) >> 5; + sel_table_size = (sel->n >> NBITS_STEP_SELECT_TABLE) + 1; // (sel->n >> NBITS_STEP_SELECT_TABLE) = (sel->n/STEP_SELECT_TABLE) + + space_usage = 2 * sizeof(cmph_uint32) * 8; // n and m + space_usage += vec_size * (cmph_uint32) sizeof(cmph_uint32) * 8; + space_usage += sel_table_size * (cmph_uint32)sizeof(cmph_uint32) * 8; + return space_usage; +} + +void select_destroy(select_t * sel) +{ + free(sel->bits_vec); + free(sel->select_table); + sel->bits_vec = 0; + sel->select_table = 0; +}; + +static inline void select_generate_sel_table(select_t * sel) +{ + register cmph_uint8 * bits_table = (cmph_uint8 *)sel->bits_vec; + register cmph_uint32 part_sum, old_part_sum; + register cmph_uint32 vec_idx, one_idx, sel_table_idx; + + part_sum = vec_idx = one_idx = sel_table_idx = 0; + + for(;;) + { + // FABIANO: Should'n it be one_idx >= sel->n + if(one_idx >= sel->n) + break; + do + { + old_part_sum = part_sum; + part_sum += rank_lookup_table[bits_table[vec_idx]]; + vec_idx++; + } while (part_sum <= one_idx); + + sel->select_table[sel_table_idx] = select_lookup_table[bits_table[vec_idx - 1]][one_idx - old_part_sum] + ((vec_idx - 1) << 3); // ((vec_idx - 1) << 3) = ((vec_idx - 1) * 8) + one_idx += STEP_SELECT_TABLE ; + sel_table_idx++; + }; +}; + +void select_generate(select_t * sel, cmph_uint32 * keys_vec, cmph_uint32 n, cmph_uint32 m) +{ + register cmph_uint32 i, j, idx; + cmph_uint32 buffer = 0; + + register cmph_uint32 nbits; + register cmph_uint32 vec_size; + register cmph_uint32 sel_table_size; + sel->n = n; + sel->m = m; // n values in the range [0,m-1] + + nbits = sel->n + sel->m; + vec_size = (nbits + 31) >> 5; // (nbits + 31) >> 5 = (nbits + 31)/32 + + sel_table_size = (sel->n >> NBITS_STEP_SELECT_TABLE) + 1; // (sel->n >> NBITS_STEP_SELECT_TABLE) = (sel->n/STEP_SELECT_TABLE) + + if(sel->bits_vec) + { + free(sel->bits_vec); + } + sel->bits_vec = (cmph_uint32 *)calloc(vec_size, sizeof(cmph_uint32)); + + if(sel->select_table) + { + free(sel->select_table); + } + sel->select_table = (cmph_uint32 *)calloc(sel_table_size, sizeof(cmph_uint32)); + + + + idx = i = j = 0; + + for(;;) + { + while(keys_vec[j]==i) + { + select_insert_1(&buffer); + idx++; + + if((idx & 0x1f) == 0 ) // (idx & 0x1f) = idx % 32 + sel->bits_vec[(idx >> 5) - 1] = buffer; // (idx >> 5) = idx/32 + j++; + + if(j == sel->n) + goto loop_end; + + //assert(keys_vec[j] < keys_vec[j-1]); + } + + if(i == sel->m) + break; + + while(keys_vec[j] > i) + { + select_insert_0(&buffer); + idx++; + + if((idx & 0x1f) == 0 ) // (idx & 0x1f) = idx % 32 + sel->bits_vec[(idx >> 5) - 1] = buffer; // (idx >> 5) = idx/32 + i++; + }; + + }; + loop_end: + if((idx & 0x1f) != 0 ) // (idx & 0x1f) = idx % 32 + { + buffer >>= 32 - (idx & 0x1f); + sel->bits_vec[ (idx - 1) >> 5 ] = buffer; + }; + + select_generate_sel_table(sel); +}; + +static inline cmph_uint32 _select_query(cmph_uint8 * bits_table, cmph_uint32 * select_table, cmph_uint32 one_idx) +{ + register cmph_uint32 vec_bit_idx ,vec_byte_idx; + register cmph_uint32 part_sum, old_part_sum; + + vec_bit_idx = select_table[one_idx >> NBITS_STEP_SELECT_TABLE]; // one_idx >> NBITS_STEP_SELECT_TABLE = one_idx/STEP_SELECT_TABLE + vec_byte_idx = vec_bit_idx >> 3; // vec_bit_idx / 8 + + one_idx &= MASK_STEP_SELECT_TABLE; // one_idx %= STEP_SELECT_TABLE == one_idx &= MASK_STEP_SELECT_TABLE + one_idx += rank_lookup_table[bits_table[vec_byte_idx] & ((1 << (vec_bit_idx & 0x7)) - 1)]; + part_sum = 0; + + do + { + old_part_sum = part_sum; + part_sum += rank_lookup_table[bits_table[vec_byte_idx]]; + vec_byte_idx++; + + }while (part_sum <= one_idx); + + return select_lookup_table[bits_table[vec_byte_idx - 1]][one_idx - old_part_sum] + ((vec_byte_idx-1) << 3); +} + +cmph_uint32 select_query(select_t * sel, cmph_uint32 one_idx) +{ + return _select_query((cmph_uint8 *)sel->bits_vec, sel->select_table, one_idx); +}; + + +static inline cmph_uint32 _select_next_query(cmph_uint8 * bits_table, cmph_uint32 vec_bit_idx) +{ + register cmph_uint32 vec_byte_idx, one_idx; + register cmph_uint32 part_sum, old_part_sum; + + vec_byte_idx = vec_bit_idx >> 3; + + one_idx = rank_lookup_table[bits_table[vec_byte_idx] & ((1U << (vec_bit_idx & 0x7)) - 1U)] + 1U; + part_sum = 0; + + do + { + old_part_sum = part_sum; + part_sum += rank_lookup_table[bits_table[vec_byte_idx]]; + vec_byte_idx++; + + }while (part_sum <= one_idx); + + return select_lookup_table[bits_table[(vec_byte_idx - 1)]][(one_idx - old_part_sum)] + ((vec_byte_idx - 1) << 3); +} + +cmph_uint32 select_next_query(select_t * sel, cmph_uint32 vec_bit_idx) +{ + return _select_next_query((cmph_uint8 *)sel->bits_vec, vec_bit_idx); +}; + +void select_dump(select_t *sel, char **buf, cmph_uint32 *buflen) +{ + register cmph_uint32 nbits = sel->n + sel->m; + register cmph_uint32 vec_size = ((nbits + 31) >> 5) * (cmph_uint32)sizeof(cmph_uint32); // (nbits + 31) >> 5 = (nbits + 31)/32 + register cmph_uint32 sel_table_size = ((sel->n >> NBITS_STEP_SELECT_TABLE) + 1) * (cmph_uint32)sizeof(cmph_uint32); // (sel->n >> NBITS_STEP_SELECT_TABLE) = (sel->n/STEP_SELECT_TABLE) + register cmph_uint32 pos = 0; + + *buflen = 2*(cmph_uint32)sizeof(cmph_uint32) + vec_size + sel_table_size; + + *buf = (char *)calloc(*buflen, sizeof(char)); + + if (!*buf) + { + *buflen = UINT_MAX; + return; + } + + memcpy(*buf, &(sel->n), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + memcpy(*buf + pos, &(sel->m), sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + memcpy(*buf + pos, sel->bits_vec, vec_size); + pos += vec_size; + memcpy(*buf + pos, sel->select_table, sel_table_size); + + DEBUGP("Dumped select structure with size %u bytes\n", *buflen); +} + +void select_load(select_t * sel, const char *buf, cmph_uint32 buflen) +{ + register cmph_uint32 pos = 0; + register cmph_uint32 nbits = 0; + register cmph_uint32 vec_size = 0; + register cmph_uint32 sel_table_size = 0; + + memcpy(&(sel->n), buf, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + memcpy(&(sel->m), buf + pos, sizeof(cmph_uint32)); + pos += (cmph_uint32)sizeof(cmph_uint32); + + nbits = sel->n + sel->m; + vec_size = ((nbits + 31) >> 5) * (cmph_uint32)sizeof(cmph_uint32); // (nbits + 31) >> 5 = (nbits + 31)/32 + sel_table_size = ((sel->n >> NBITS_STEP_SELECT_TABLE) + 1) * (cmph_uint32)sizeof(cmph_uint32); // (sel->n >> NBITS_STEP_SELECT_TABLE) = (sel->n/STEP_SELECT_TABLE) + + if(sel->bits_vec) + { + free(sel->bits_vec); + } + sel->bits_vec = (cmph_uint32 *)calloc(vec_size/sizeof(cmph_uint32), sizeof(cmph_uint32)); + + if(sel->select_table) + { + free(sel->select_table); + } + sel->select_table = (cmph_uint32 *)calloc(sel_table_size/sizeof(cmph_uint32), sizeof(cmph_uint32)); + + memcpy(sel->bits_vec, buf + pos, vec_size); + pos += vec_size; + memcpy(sel->select_table, buf + pos, sel_table_size); + + DEBUGP("Loaded select structure with size %u bytes\n", buflen); +} + + +/** \fn void select_pack(select_t *sel, void *sel_packed); + * \brief Support the ability to pack a select structure function into a preallocated contiguous memory space pointed by sel_packed. + * \param sel points to the select structure + * \param sel_packed pointer to the contiguous memory area used to store the select structure. The size of sel_packed must be at least @see select_packed_size + */ +void select_pack(select_t *sel, void *sel_packed) +{ + if (sel && sel_packed) + { + char *buf = NULL; + cmph_uint32 buflen = 0; + select_dump(sel, &buf, &buflen); + memcpy(sel_packed, buf, buflen); + free(buf); + } +} + + +/** \fn cmph_uint32 select_packed_size(select_t *sel); + * \brief Return the amount of space needed to pack a select structure. + * \return the size of the packed select structure or zero for failures + */ +cmph_uint32 select_packed_size(select_t *sel) +{ + register cmph_uint32 nbits = sel->n + sel->m; + register cmph_uint32 vec_size = ((nbits + 31) >> 5) * (cmph_uint32)sizeof(cmph_uint32); // (nbits + 31) >> 5 = (nbits + 31)/32 + register cmph_uint32 sel_table_size = ((sel->n >> NBITS_STEP_SELECT_TABLE) + 1) * (cmph_uint32)sizeof(cmph_uint32); // (sel->n >> NBITS_STEP_SELECT_TABLE) = (sel->n/STEP_SELECT_TABLE) + return 2*(cmph_uint32)sizeof(cmph_uint32) + vec_size + sel_table_size; +} + + + +cmph_uint32 select_query_packed(void * sel_packed, cmph_uint32 one_idx) +{ + register cmph_uint32 *ptr = (cmph_uint32 *)sel_packed; + register cmph_uint32 n = *ptr++; + register cmph_uint32 m = *ptr++; + register cmph_uint32 nbits = n + m; + register cmph_uint32 vec_size = (nbits + 31) >> 5; // (nbits + 31) >> 5 = (nbits + 31)/32 + register cmph_uint8 * bits_vec = (cmph_uint8 *)ptr; + register cmph_uint32 * select_table = ptr + vec_size; + + return _select_query(bits_vec, select_table, one_idx); +} + + +cmph_uint32 select_next_query_packed(void * sel_packed, cmph_uint32 vec_bit_idx) +{ + register cmph_uint8 * bits_vec = (cmph_uint8 *)sel_packed; + bits_vec += 8; // skipping n and m + return _select_next_query(bits_vec, vec_bit_idx); +} diff --git a/girepository/cmph/select.h b/girepository/cmph/select.h new file mode 100644 index 000000000..a31eb0f22 --- /dev/null +++ b/girepository/cmph/select.h @@ -0,0 +1,61 @@ +#ifndef __CMPH_SELECT_H__ +#define __CMPH_SELECT_H__ + +#include "cmph_types.h" + +struct _select_t +{ + cmph_uint32 n,m; + cmph_uint32 * bits_vec; + cmph_uint32 * select_table; +}; + +typedef struct _select_t select_t; + +void select_init(select_t * sel); + +void select_destroy(select_t * sel); + +void select_generate(select_t * sel, cmph_uint32 * keys_vec, cmph_uint32 n, cmph_uint32 m); + +cmph_uint32 select_query(select_t * sel, cmph_uint32 one_idx); + +cmph_uint32 select_next_query(select_t * sel, cmph_uint32 vec_bit_idx); + +cmph_uint32 select_get_space_usage(select_t * sel); + +void select_dump(select_t *sel, char **buf, cmph_uint32 *buflen); + +void select_load(select_t * sel, const char *buf, cmph_uint32 buflen); + + +/** \fn void select_pack(select_t *sel, void *sel_packed); + * \brief Support the ability to pack a select structure into a preallocated contiguous memory space pointed by sel_packed. + * \param sel points to the select structure + * \param sel_packed pointer to the contiguous memory area used to store the select structure. The size of sel_packed must be at least @see select_packed_size + */ +void select_pack(select_t *sel, void *sel_packed); + +/** \fn cmph_uint32 select_packed_size(select_t *sel); + * \brief Return the amount of space needed to pack a select structure. + * \return the size of the packed select structure or zero for failures + */ +cmph_uint32 select_packed_size(select_t *sel); + + +/** \fn cmph_uint32 select_query_packed(void * sel_packed, cmph_uint32 one_idx); + * \param sel_packed is a pointer to a contiguous memory area + * \param one_idx is the rank for which we want to calculate the inverse function select + * \return an integer that represents the select value of rank idx. + */ +cmph_uint32 select_query_packed(void * sel_packed, cmph_uint32 one_idx); + + +/** \fn cmph_uint32 select_next_query_packed(void * sel_packed, cmph_uint32 vec_bit_idx); + * \param sel_packed is a pointer to a contiguous memory area + * \param vec_bit_idx is a value prior computed by @see select_query_packed + * \return an integer that represents the next select value greater than @see vec_bit_idx. + */ +cmph_uint32 select_next_query_packed(void * sel_packed, cmph_uint32 vec_bit_idx); + +#endif diff --git a/girepository/cmph/select_lookup_tables.h b/girepository/cmph/select_lookup_tables.h new file mode 100644 index 000000000..efd595ed5 --- /dev/null +++ b/girepository/cmph/select_lookup_tables.h @@ -0,0 +1,170 @@ +#ifndef SELECT_LOOKUP_TABLES +#define SELECT_LOOKUP_TABLES + +#include "cmph_types.h" + +/* +rank_lookup_table[i] simply gives the number of bits set to one in the byte of value i. +For example if i = 01010101 in binary then we have : +rank_lookup_table[i] = 4 +*/ + +static cmph_uint8 rank_lookup_table[256] ={ + 0 , 1 , 1 , 2 , 1 , 2 , 2 , 3 , 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 +, 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 +, 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 +, 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 +, 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 +, 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 +, 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 +, 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 +, 1 , 2 , 2 , 3 , 2 , 3 , 3 , 4 , 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 +, 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 +, 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 +, 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 +, 2 , 3 , 3 , 4 , 3 , 4 , 4 , 5 , 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 +, 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 +, 3 , 4 , 4 , 5 , 4 , 5 , 5 , 6 , 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 +, 4 , 5 , 5 , 6 , 5 , 6 , 6 , 7 , 5 , 6 , 6 , 7 , 6 , 7 , 7 , 8 + }; + +/* +select_lookup_table[i][j] simply gives the index of the j'th bit set to one in the byte of value i. +For example if i=01010101 in binary then we have : +select_lookup_table[i][0] = 0, the first bit set to one is at position 0 +select_lookup_table[i][1] = 2, the second bit set to one is at position 2 +select_lookup_table[i][2] = 4, the third bit set to one is at position 4 +select_lookup_table[i][3] = 6, the fourth bit set to one is at position 6 +select_lookup_table[i][4] = 255, there is no more than 4 bits set to one in i, so we return escape value 255. +*/ +static cmph_uint8 select_lookup_table[256][8]={ +{ 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 2 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 255 , 255 , 255 , 255 , 255 } , +{ 3 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 255 , 255 , 255 , 255 , 255 } , +{ 2 , 3 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 255 , 255 , 255 , 255 } , +{ 4 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 4 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 4 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 4 , 255 , 255 , 255 , 255 , 255 } , +{ 2 , 4 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 4 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 4 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 4 , 255 , 255 , 255 , 255 } , +{ 3 , 4 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 4 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 4 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 4 , 255 , 255 , 255 , 255 } , +{ 2 , 3 , 4 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 4 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 4 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 4 , 255 , 255 , 255 } , +{ 5 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 5 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 5 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 5 , 255 , 255 , 255 , 255 , 255 } , +{ 2 , 5 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 5 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 5 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 5 , 255 , 255 , 255 , 255 } , +{ 3 , 5 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 5 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 5 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 5 , 255 , 255 , 255 , 255 } , +{ 2 , 3 , 5 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 5 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 5 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 5 , 255 , 255 , 255 } , +{ 4 , 5 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 4 , 5 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 4 , 5 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 4 , 5 , 255 , 255 , 255 , 255 } , +{ 2 , 4 , 5 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 4 , 5 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 4 , 5 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 4 , 5 , 255 , 255 , 255 } , +{ 3 , 4 , 5 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 4 , 5 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 4 , 5 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 4 , 5 , 255 , 255 , 255 } , +{ 2 , 3 , 4 , 5 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 4 , 5 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 4 , 5 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 4 , 5 , 255 , 255 } , +{ 6 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 6 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 6 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 6 , 255 , 255 , 255 , 255 , 255 } , +{ 2 , 6 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 6 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 6 , 255 , 255 , 255 , 255 } , +{ 3 , 6 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 6 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 6 , 255 , 255 , 255 , 255 } , +{ 2 , 3 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 6 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 6 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 6 , 255 , 255 , 255 } , +{ 4 , 6 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 4 , 6 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 4 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 4 , 6 , 255 , 255 , 255 , 255 } , +{ 2 , 4 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 4 , 6 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 4 , 6 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 4 , 6 , 255 , 255 , 255 } , +{ 3 , 4 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 4 , 6 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 4 , 6 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 4 , 6 , 255 , 255 , 255 } , +{ 2 , 3 , 4 , 6 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 4 , 6 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 4 , 6 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 4 , 6 , 255 , 255 } , +{ 5 , 6 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 5 , 6 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 5 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 5 , 6 , 255 , 255 , 255 , 255 } , +{ 2 , 5 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 5 , 6 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 5 , 6 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 5 , 6 , 255 , 255 , 255 } , +{ 3 , 5 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 5 , 6 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 5 , 6 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 5 , 6 , 255 , 255 , 255 } , +{ 2 , 3 , 5 , 6 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 5 , 6 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 5 , 6 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 5 , 6 , 255 , 255 } , +{ 4 , 5 , 6 , 255 , 255 , 255 , 255 , 255 } , { 0 , 4 , 5 , 6 , 255 , 255 , 255 , 255 } , +{ 1 , 4 , 5 , 6 , 255 , 255 , 255 , 255 } , { 0 , 1 , 4 , 5 , 6 , 255 , 255 , 255 } , +{ 2 , 4 , 5 , 6 , 255 , 255 , 255 , 255 } , { 0 , 2 , 4 , 5 , 6 , 255 , 255 , 255 } , +{ 1 , 2 , 4 , 5 , 6 , 255 , 255 , 255 } , { 0 , 1 , 2 , 4 , 5 , 6 , 255 , 255 } , +{ 3 , 4 , 5 , 6 , 255 , 255 , 255 , 255 } , { 0 , 3 , 4 , 5 , 6 , 255 , 255 , 255 } , +{ 1 , 3 , 4 , 5 , 6 , 255 , 255 , 255 } , { 0 , 1 , 3 , 4 , 5 , 6 , 255 , 255 } , +{ 2 , 3 , 4 , 5 , 6 , 255 , 255 , 255 } , { 0 , 2 , 3 , 4 , 5 , 6 , 255 , 255 } , +{ 1 , 2 , 3 , 4 , 5 , 6 , 255 , 255 } , { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 255 } , +{ 7 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 7 , 255 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 7 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 7 , 255 , 255 , 255 , 255 , 255 } , +{ 2 , 7 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 7 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 7 , 255 , 255 , 255 , 255 } , +{ 3 , 7 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 7 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 7 , 255 , 255 , 255 , 255 } , +{ 2 , 3 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 7 , 255 , 255 , 255 } , +{ 4 , 7 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 4 , 7 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 4 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 4 , 7 , 255 , 255 , 255 , 255 } , +{ 2 , 4 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 4 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 4 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 4 , 7 , 255 , 255 , 255 } , +{ 3 , 4 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 4 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 4 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 4 , 7 , 255 , 255 , 255 } , +{ 2 , 3 , 4 , 7 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 4 , 7 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 4 , 7 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 4 , 7 , 255 , 255 } , +{ 5 , 7 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 5 , 7 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 5 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 5 , 7 , 255 , 255 , 255 , 255 } , +{ 2 , 5 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 5 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 5 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 5 , 7 , 255 , 255 , 255 } , +{ 3 , 5 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 5 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 5 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 5 , 7 , 255 , 255 , 255 } , +{ 2 , 3 , 5 , 7 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 5 , 7 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 5 , 7 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 5 , 7 , 255 , 255 } , +{ 4 , 5 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 4 , 5 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 4 , 5 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 4 , 5 , 7 , 255 , 255 , 255 } , +{ 2 , 4 , 5 , 7 , 255 , 255 , 255 , 255 } , { 0 , 2 , 4 , 5 , 7 , 255 , 255 , 255 } , +{ 1 , 2 , 4 , 5 , 7 , 255 , 255 , 255 } , { 0 , 1 , 2 , 4 , 5 , 7 , 255 , 255 } , +{ 3 , 4 , 5 , 7 , 255 , 255 , 255 , 255 } , { 0 , 3 , 4 , 5 , 7 , 255 , 255 , 255 } , +{ 1 , 3 , 4 , 5 , 7 , 255 , 255 , 255 } , { 0 , 1 , 3 , 4 , 5 , 7 , 255 , 255 } , +{ 2 , 3 , 4 , 5 , 7 , 255 , 255 , 255 } , { 0 , 2 , 3 , 4 , 5 , 7 , 255 , 255 } , +{ 1 , 2 , 3 , 4 , 5 , 7 , 255 , 255 } , { 0 , 1 , 2 , 3 , 4 , 5 , 7 , 255 } , +{ 6 , 7 , 255 , 255 , 255 , 255 , 255 , 255 } , { 0 , 6 , 7 , 255 , 255 , 255 , 255 , 255 } , +{ 1 , 6 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 1 , 6 , 7 , 255 , 255 , 255 , 255 } , +{ 2 , 6 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 2 , 6 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 2 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 2 , 6 , 7 , 255 , 255 , 255 } , +{ 3 , 6 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 3 , 6 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 3 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 3 , 6 , 7 , 255 , 255 , 255 } , +{ 2 , 3 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 2 , 3 , 6 , 7 , 255 , 255 , 255 } , +{ 1 , 2 , 3 , 6 , 7 , 255 , 255 , 255 } , { 0 , 1 , 2 , 3 , 6 , 7 , 255 , 255 } , +{ 4 , 6 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 4 , 6 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 4 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 4 , 6 , 7 , 255 , 255 , 255 } , +{ 2 , 4 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 2 , 4 , 6 , 7 , 255 , 255 , 255 } , +{ 1 , 2 , 4 , 6 , 7 , 255 , 255 , 255 } , { 0 , 1 , 2 , 4 , 6 , 7 , 255 , 255 } , +{ 3 , 4 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 3 , 4 , 6 , 7 , 255 , 255 , 255 } , +{ 1 , 3 , 4 , 6 , 7 , 255 , 255 , 255 } , { 0 , 1 , 3 , 4 , 6 , 7 , 255 , 255 } , +{ 2 , 3 , 4 , 6 , 7 , 255 , 255 , 255 } , { 0 , 2 , 3 , 4 , 6 , 7 , 255 , 255 } , +{ 1 , 2 , 3 , 4 , 6 , 7 , 255 , 255 } , { 0 , 1 , 2 , 3 , 4 , 6 , 7 , 255 } , +{ 5 , 6 , 7 , 255 , 255 , 255 , 255 , 255 } , { 0 , 5 , 6 , 7 , 255 , 255 , 255 , 255 } , +{ 1 , 5 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 1 , 5 , 6 , 7 , 255 , 255 , 255 } , +{ 2 , 5 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 2 , 5 , 6 , 7 , 255 , 255 , 255 } , +{ 1 , 2 , 5 , 6 , 7 , 255 , 255 , 255 } , { 0 , 1 , 2 , 5 , 6 , 7 , 255 , 255 } , +{ 3 , 5 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 3 , 5 , 6 , 7 , 255 , 255 , 255 } , +{ 1 , 3 , 5 , 6 , 7 , 255 , 255 , 255 } , { 0 , 1 , 3 , 5 , 6 , 7 , 255 , 255 } , +{ 2 , 3 , 5 , 6 , 7 , 255 , 255 , 255 } , { 0 , 2 , 3 , 5 , 6 , 7 , 255 , 255 } , +{ 1 , 2 , 3 , 5 , 6 , 7 , 255 , 255 } , { 0 , 1 , 2 , 3 , 5 , 6 , 7 , 255 } , +{ 4 , 5 , 6 , 7 , 255 , 255 , 255 , 255 } , { 0 , 4 , 5 , 6 , 7 , 255 , 255 , 255 } , +{ 1 , 4 , 5 , 6 , 7 , 255 , 255 , 255 } , { 0 , 1 , 4 , 5 , 6 , 7 , 255 , 255 } , +{ 2 , 4 , 5 , 6 , 7 , 255 , 255 , 255 } , { 0 , 2 , 4 , 5 , 6 , 7 , 255 , 255 } , +{ 1 , 2 , 4 , 5 , 6 , 7 , 255 , 255 } , { 0 , 1 , 2 , 4 , 5 , 6 , 7 , 255 } , +{ 3 , 4 , 5 , 6 , 7 , 255 , 255 , 255 } , { 0 , 3 , 4 , 5 , 6 , 7 , 255 , 255 } , +{ 1 , 3 , 4 , 5 , 6 , 7 , 255 , 255 } , { 0 , 1 , 3 , 4 , 5 , 6 , 7 , 255 } , +{ 2 , 3 , 4 , 5 , 6 , 7 , 255 , 255 } , { 0 , 2 , 3 , 4 , 5 , 6 , 7 , 255 } , +{ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 255 } , { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 } }; + +#endif diff --git a/girepository/cmph/vqueue.c b/girepository/cmph/vqueue.c new file mode 100644 index 000000000..0619dd7cc --- /dev/null +++ b/girepository/cmph/vqueue.c @@ -0,0 +1,51 @@ +#include "vqueue.h" +#include +#include +#include +struct __vqueue_t +{ + cmph_uint32 * values; + cmph_uint32 beg, end, capacity; +}; + +vqueue_t * vqueue_new(cmph_uint32 capacity) +{ + size_t capacity_plus_one = capacity + 1; + vqueue_t *q = (vqueue_t *)malloc(sizeof(vqueue_t)); + assert(q); + q->values = (cmph_uint32 *)calloc(capacity_plus_one, sizeof(cmph_uint32)); + q->beg = q->end = 0; + q->capacity = (cmph_uint32) capacity_plus_one; + return q; +} + +cmph_uint8 vqueue_is_empty(vqueue_t * q) +{ + return (cmph_uint8)(q->beg == q->end); +} + +void vqueue_insert(vqueue_t * q, cmph_uint32 val) +{ + assert((q->end + 1)%q->capacity != q->beg); // Is queue full? + q->end = (q->end + 1)%q->capacity; + q->values[q->end] = val; +} + +cmph_uint32 vqueue_remove(vqueue_t * q) +{ + assert(!vqueue_is_empty(q)); // Is queue empty? + q->beg = (q->beg + 1)%q->capacity; + return q->values[q->beg]; +} + +void vqueue_print(vqueue_t * q) +{ + cmph_uint32 i; + for (i = q->beg; i != q->end; i = (i + 1)%q->capacity) + fprintf(stderr, "%u\n", q->values[(i + 1)%q->capacity]); +} + +void vqueue_destroy(vqueue_t *q) +{ + free(q->values); q->values = NULL; free(q); +} diff --git a/girepository/cmph/vqueue.h b/girepository/cmph/vqueue.h new file mode 100644 index 000000000..86fccab68 --- /dev/null +++ b/girepository/cmph/vqueue.h @@ -0,0 +1,18 @@ +#ifndef __CMPH_VQUEUE_H__ +#define __CMPH_VQUEUE_H__ + +#include "cmph_types.h" +typedef struct __vqueue_t vqueue_t; + +vqueue_t * vqueue_new(cmph_uint32 capacity); + +cmph_uint8 vqueue_is_empty(vqueue_t * q); + +void vqueue_insert(vqueue_t * q, cmph_uint32 val); + +cmph_uint32 vqueue_remove(vqueue_t * q); + +void vqueue_print(vqueue_t * q); + +void vqueue_destroy(vqueue_t * q); +#endif diff --git a/girepository/cmph/vstack.c b/girepository/cmph/vstack.c new file mode 100644 index 000000000..96f5380ab --- /dev/null +++ b/girepository/cmph/vstack.c @@ -0,0 +1,79 @@ +#include "vstack.h" + +#include +#include + +//#define DEBUG +#include "debug.h" + +struct __vstack_t +{ + cmph_uint32 pointer; + cmph_uint32 *values; + cmph_uint32 capacity; +}; + +vstack_t *vstack_new(void) +{ + vstack_t *stack = (vstack_t *)malloc(sizeof(vstack_t)); + assert(stack); + stack->pointer = 0; + stack->values = NULL; + stack->capacity = 0; + return stack; +} + +void vstack_destroy(vstack_t *stack) +{ + assert(stack); + free(stack->values); + free(stack); +} + +void vstack_push(vstack_t *stack, cmph_uint32 val) +{ + assert(stack); + vstack_reserve(stack, stack->pointer + 1); + stack->values[stack->pointer] = val; + ++(stack->pointer); +} +void vstack_pop(vstack_t *stack) +{ + assert(stack); + assert(stack->pointer > 0); + --(stack->pointer); +} + +cmph_uint32 vstack_top(vstack_t *stack) +{ + assert(stack); + assert(stack->pointer > 0); + return stack->values[(stack->pointer - 1)]; +} +int vstack_empty(vstack_t *stack) +{ + assert(stack); + return stack->pointer == 0; +} +cmph_uint32 vstack_size(vstack_t *stack) +{ + return stack->pointer; +} +void vstack_reserve(vstack_t *stack, cmph_uint32 size) +{ + assert(stack); + if (stack->capacity < size) + { + cmph_uint32 new_capacity = stack->capacity + 1; + DEBUGP("Increasing current capacity %u to %u\n", stack->capacity, size); + while (new_capacity < size) + { + new_capacity *= 2; + } + stack->values = (cmph_uint32 *)realloc(stack->values, sizeof(cmph_uint32)*new_capacity); + assert(stack->values); + stack->capacity = new_capacity; + DEBUGP("Increased\n"); + } +} + diff --git a/girepository/cmph/vstack.h b/girepository/cmph/vstack.h new file mode 100644 index 000000000..fecc7d55d --- /dev/null +++ b/girepository/cmph/vstack.h @@ -0,0 +1,18 @@ +#ifndef __CMPH_VSTACK_H__ +#define __CMPH_VSTACK_H__ + +#include "cmph_types.h" +typedef struct __vstack_t vstack_t; + +vstack_t *vstack_new(void); +void vstack_destroy(vstack_t *stack); + +void vstack_push(vstack_t *stack, cmph_uint32 val); +cmph_uint32 vstack_top(vstack_t *stack); +void vstack_pop(vstack_t *stack); +int vstack_empty(vstack_t *stack); +cmph_uint32 vstack_size(vstack_t *stack); + +void vstack_reserve(vstack_t *stack, cmph_uint32 size); + +#endif diff --git a/girepository/cmph/wingetopt.c b/girepository/cmph/wingetopt.c new file mode 100644 index 000000000..c981d0f04 --- /dev/null +++ b/girepository/cmph/wingetopt.c @@ -0,0 +1,179 @@ +#ifdef WIN32 +/***************************************************************************** + * + * MODULE NAME : GETOPT.C + * + * COPYRIGHTS: + * This module contains code made available by IBM + * Corporation on an AS IS basis. Any one receiving the + * module is considered to be licensed under IBM copyrights + * to use the IBM-provided source code in any way he or she + * deems fit, including copying it, compiling it, modifying + * it, and redistributing it, with or without + * modifications. No license under any IBM patents or + * patent applications is to be implied from this copyright + * license. + * + * A user of the module should understand that IBM cannot + * provide technical support for the module and will not be + * responsible for any consequences of use of the program. + * + * Any notices, including this one, are not to be removed + * from the module without the prior written consent of + * IBM. + * + * AUTHOR: Original author: + * G. R. Blair (BOBBLAIR at AUSVM1) + * Internet: bobblair@bobblair.austin.ibm.com + * + * Extensively revised by: + * John Q. Walker II, Ph.D. (JOHHQ at RALVM6) + * Internet: johnq@ralvm6.vnet.ibm.com + * + *****************************************************************************/ + +/****************************************************************************** + * getopt() + * + * The getopt() function is a command line parser. It returns the next + * option character in argv that matches an option character in opstring. + * + * The argv argument points to an array of argc+1 elements containing argc + * pointers to character strings followed by a null pointer. + * + * The opstring argument points to a string of option characters; if an + * option character is followed by a colon, the option is expected to have + * an argument that may or may not be separated from it by white space. + * The external variable optarg is set to point to the start of the option + * argument on return from getopt(). + * + * The getopt() function places in optind the argv index of the next argument + * to be processed. The system initializes the external variable optind to + * 1 before the first call to getopt(). + * + * When all options have been processed (that is, up to the first nonoption + * argument), getopt() returns EOF. The special option "--" may be used to + * delimit the end of the options; EOF will be returned, and "--" will be + * skipped. + * + * The getopt() function returns a question mark (?) when it encounters an + * option character not included in opstring. This error message can be + * disabled by setting opterr to zero. Otherwise, it returns the option + * character that was detected. + * + * If the special option "--" is detected, or all options have been + * processed, EOF is returned. + * + * Options are marked by either a minus sign (-) or a slash (/). + * + * No errors are defined. + *****************************************************************************/ + +#include /* for EOF */ +#include /* for strchr() */ + +/* static (global) variables that are specified as exported by getopt() */ +extern char *optarg; /* pointer to the start of the option argument */ +extern int optind; /* number of the next argv[] to be evaluated */ +extern int opterr; /* non-zero if a question mark should be returned + when a non-valid option character is detected */ + +/* handle possible future character set concerns by putting this in a macro */ +#define _next_char(string) (char)(*(string+1)) + +int getopt(int argc, char *argv[], char *opstring) +{ + static char *pIndexPosition = NULL; /* place inside current argv string */ + char *pArgString = NULL; /* where to start from next */ + char *pOptString; /* the string in our program */ + + + if (pIndexPosition != NULL) { + /* we last left off inside an argv string */ + if (*(++pIndexPosition)) { + /* there is more to come in the most recent argv */ + pArgString = pIndexPosition; + } + } + + if (pArgString == NULL) { + /* we didn't leave off in the middle of an argv string */ + if (optind >= argc) { + /* more command-line arguments than the argument count */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* used up all command-line arguments */ + } + + /*--------------------------------------------------------------------- + * If the next argv[] is not an option, there can be no more options. + *-------------------------------------------------------------------*/ + pArgString = argv[optind++]; /* set this to the next argument ptr */ + + if (('/' != *pArgString) && /* doesn't start with a slash or a dash? */ + ('-' != *pArgString)) { + --optind; /* point to current arg once we're done */ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* used up all the command-line flags */ + } + + /* check for special end-of-flags markers */ + if ((strcmp(pArgString, "-") == 0) || + (strcmp(pArgString, "--") == 0)) { + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* encountered the special flag */ + } + + pArgString++; /* look past the / or - */ + } + + if (':' == *pArgString) { /* is it a colon? */ + /*--------------------------------------------------------------------- + * Rare case: if opterr is non-zero, return a question mark; + * otherwise, just return the colon we're on. + *-------------------------------------------------------------------*/ + return (opterr ? (int)'?' : (int)':'); + } + else if ((pOptString = strchr(opstring, *pArgString)) == 0) { + /*--------------------------------------------------------------------- + * The letter on the command-line wasn't any good. + *-------------------------------------------------------------------*/ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = NULL; /* not in the middle of anything */ + return (opterr ? (int)'?' : (int)*pArgString); + } + else { + /*--------------------------------------------------------------------- + * The letter on the command-line matches one we expect to see + *-------------------------------------------------------------------*/ + if (':' == _next_char(pOptString)) { /* is the next letter a colon? */ + /* It is a colon. Look for an argument string. */ + if ('\0' != _next_char(pArgString)) { /* argument in this argv? */ + optarg = &pArgString[1]; /* Yes, it is */ + } + else { + /*------------------------------------------------------------- + * The argument string must be in the next argv. + * But, what if there is none (bad input from the user)? + * In that case, return the letter, and optarg as NULL. + *-----------------------------------------------------------*/ + if (optind < argc) + optarg = argv[optind++]; + else { + optarg = NULL; + return (opterr ? (int)'?' : (int)*pArgString); + } + } + pIndexPosition = NULL; /* not in the middle of anything */ + } + else { + /* it's not a colon, so just return the letter */ + optarg = NULL; /* no argument follows the option */ + pIndexPosition = pArgString; /* point to the letter we're on */ + } + return (int)*pArgString; /* return the letter that matched */ + } +} + +#endif //WIN32 diff --git a/girepository/cmph/wingetopt.h b/girepository/cmph/wingetopt.h new file mode 100644 index 000000000..9596853d9 --- /dev/null +++ b/girepository/cmph/wingetopt.h @@ -0,0 +1,25 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef WIN32 + #include +#else + #ifndef _GETOPT_ + #define _GETOPT_ + + #include /* for EOF */ + #include /* for strchr() */ + + char *optarg = NULL; /* pointer to the start of the option argument */ + int optind = 1; /* number of the next argv[] to be evaluated */ + int opterr = 1; /* non-zero if a question mark should be returned */ + + int getopt(int argc, char *argv[], char *opstring); + #endif //_GETOPT_ +#endif //WIN32 + +#ifdef __cplusplus +} +#endif + diff --git a/girepository/docs.c b/girepository/docs.c new file mode 100644 index 000000000..b032b8231 --- /dev/null +++ b/girepository/docs.c @@ -0,0 +1,33 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Dump introspection data + * + * Copyright (C) 2013 Dieter Verfaillie + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* This file collects documentation for macros, typedefs and + * the like, which have no good home in any of the 'real' source + * files. + */ + +/** + * SECTION:gicommontypes + * @title: Common Types + * @short_description: TODO + * + * TODO + */ diff --git a/girepository/gdump.c b/girepository/gdump.c new file mode 100644 index 000000000..c63dc4556 --- /dev/null +++ b/girepository/gdump.c @@ -0,0 +1,683 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Dump introspection data + * + * Copyright (C) 2008 Colin Walters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include +#include +#include + +/* This file is both compiled into libgirepository.so, and installed + * on the filesystem. But for the dumper, we want to avoid linking + * to libgirepository; see + * https://bugzilla.gnome.org/show_bug.cgi?id=630342 + */ +#ifdef G_IREPOSITORY_COMPILATION +#include "config.h" +#include "girepository.h" +#endif + +#include + +static void +escaped_printf (GOutputStream *out, const char *fmt, ...) G_GNUC_PRINTF (2, 3); + +static void +escaped_printf (GOutputStream *out, const char *fmt, ...) +{ + char *str; + va_list args; + gsize written; + GError *error = NULL; + + va_start (args, fmt); + + str = g_markup_vprintf_escaped (fmt, args); + if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL, &error)) + { + g_critical ("failed to write to iochannel: %s", error->message); + g_clear_error (&error); + } + g_free (str); + + va_end (args); +} + +static void +goutput_write (GOutputStream *out, const char *str) +{ + gsize written; + GError *error = NULL; + if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL, &error)) + { + g_critical ("failed to write to iochannel: %s", error->message); + g_clear_error (&error); + } +} + +typedef GType (*GetTypeFunc)(void); +typedef GQuark (*ErrorQuarkFunc)(void); + +static GType +invoke_get_type (GModule *self, const char *symbol, GError **error) +{ + GetTypeFunc sym; + GType ret; + + if (!g_module_symbol (self, symbol, (void**)&sym)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Failed to find symbol '%s'", symbol); + return G_TYPE_INVALID; + } + + ret = sym (); + if (ret == G_TYPE_INVALID) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Function '%s' returned G_TYPE_INVALID", symbol); + } + return ret; +} + +static GQuark +invoke_error_quark (GModule *self, const char *symbol, GError **error) +{ + ErrorQuarkFunc sym; + + if (!g_module_symbol (self, symbol, (void**)&sym)) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Failed to find symbol '%s'", symbol); + return G_TYPE_INVALID; + } + + return sym (); +} + +static char * +value_transform_to_string (const GValue *value) +{ + GValue tmp = G_VALUE_INIT; + char *s = NULL; + + g_value_init (&tmp, G_TYPE_STRING); + + if (g_value_transform (value, &tmp)) + { + const char *str = g_value_get_string (&tmp); + + if (str != NULL) + s = g_strescape (str, NULL); + } + + g_value_unset (&tmp); + + return s; +} + +/* A simpler version of g_strdup_value_contents(), but with stable + * output and less complex semantics + */ +static char * +value_to_string (const GValue *value) +{ + if (value == NULL) + return NULL; + + if (G_VALUE_HOLDS_STRING (value)) + { + const char *s = g_value_get_string (value); + + if (s == NULL) + return g_strdup ("NULL"); + + return g_strescape (s, NULL); + } + else + { + GType value_type = G_VALUE_TYPE (value); + + switch (G_TYPE_FUNDAMENTAL (value_type)) + { + case G_TYPE_BOXED: + if (g_value_get_boxed (value) == NULL) + return NULL; + else + return value_transform_to_string (value); + break; + + case G_TYPE_OBJECT: + if (g_value_get_object (value) == NULL) + return NULL; + else + return value_transform_to_string (value); + break; + + case G_TYPE_POINTER: + return NULL; + + default: + return value_transform_to_string (value); + } + } + + return NULL; +} + +static void +dump_properties (GType type, GOutputStream *out) +{ + guint i; + guint n_properties; + GParamSpec **props; + + if (G_TYPE_FUNDAMENTAL (type) == G_TYPE_OBJECT) + { + GObjectClass *klass; + klass = g_type_class_ref (type); + props = g_object_class_list_properties (klass, &n_properties); + } + else + { + void *klass; + klass = g_type_default_interface_ref (type); + props = g_object_interface_list_properties (klass, &n_properties); + } + + for (i = 0; i < n_properties; i++) + { + GParamSpec *prop; + + prop = props[i]; + if (prop->owner_type != type) + continue; + + const GValue *v = g_param_spec_get_default_value (prop); + char *default_value = value_to_string (v); + + if (v != NULL && default_value != NULL) + { + escaped_printf (out, " \n", + prop->name, + g_type_name (prop->value_type), + prop->flags, + default_value); + } + else + { + escaped_printf (out, " \n", + prop->name, + g_type_name (prop->value_type), + prop->flags); + } + + g_free (default_value); + } + + g_free (props); +} + +static void +dump_signals (GType type, GOutputStream *out) +{ + guint i; + guint n_sigs; + guint *sig_ids; + + sig_ids = g_signal_list_ids (type, &n_sigs); + for (i = 0; i < n_sigs; i++) + { + guint sigid; + GSignalQuery query; + guint j; + + sigid = sig_ids[i]; + g_signal_query (sigid, &query); + + escaped_printf (out, " \n"); + + for (j = 0; j < query.n_params; j++) + { + escaped_printf (out, " \n", + g_type_name (query.param_types[j])); + } + goutput_write (out, " \n"); + } + g_free (sig_ids); +} + +static void +dump_object_type (GType type, const char *symbol, GOutputStream *out) +{ + guint n_interfaces; + guint i; + GType *interfaces; + + escaped_printf (out, " str); + + g_string_free (parent_str, TRUE); + } + + if (G_TYPE_IS_ABSTRACT (type)) + escaped_printf (out, " abstract=\"1\""); + +#if GLIB_CHECK_VERSION (2, 70, 0) + if (G_TYPE_IS_FINAL (type)) + escaped_printf (out, " final=\"1\""); +#endif + + goutput_write (out, ">\n"); + + interfaces = g_type_interfaces (type, &n_interfaces); + for (i = 0; i < n_interfaces; i++) + { + GType itype = interfaces[i]; + escaped_printf (out, " \n", + g_type_name (itype)); + } + g_free (interfaces); + + dump_properties (type, out); + dump_signals (type, out); + goutput_write (out, " \n"); +} + +static void +dump_interface_type (GType type, const char *symbol, GOutputStream *out) +{ + guint n_interfaces; + guint i; + GType *interfaces; + + escaped_printf (out, " \n", + g_type_name (type), symbol); + + interfaces = g_type_interface_prerequisites (type, &n_interfaces); + for (i = 0; i < n_interfaces; i++) + { + GType itype = interfaces[i]; + if (itype == G_TYPE_OBJECT) + { + /* Treat this as implicit for now; in theory GInterfaces are + * supported on things like GstMiniObject, but right now + * the introspection system only supports GObject. + * http://bugzilla.gnome.org/show_bug.cgi?id=559706 + */ + continue; + } + escaped_printf (out, " \n", + g_type_name (itype)); + } + g_free (interfaces); + + dump_properties (type, out); + dump_signals (type, out); + goutput_write (out, " \n"); +} + +static void +dump_boxed_type (GType type, const char *symbol, GOutputStream *out) +{ + escaped_printf (out, " \n", + g_type_name (type), symbol); +} + +static void +dump_flags_type (GType type, const char *symbol, GOutputStream *out) +{ + guint i; + GFlagsClass *klass; + + klass = g_type_class_ref (type); + escaped_printf (out, " \n", + g_type_name (type), symbol); + + for (i = 0; i < klass->n_values; i++) + { + GFlagsValue *value = &(klass->values[i]); + + escaped_printf (out, " \n", + value->value_name, value->value_nick, value->value); + } + goutput_write (out, " \n"); +} + +static void +dump_enum_type (GType type, const char *symbol, GOutputStream *out) +{ + guint i; + GEnumClass *klass; + + klass = g_type_class_ref (type); + escaped_printf (out, " \n", + g_type_name (type), symbol); + + for (i = 0; i < klass->n_values; i++) + { + GEnumValue *value = &(klass->values[i]); + + escaped_printf (out, " \n", + value->value_name, value->value_nick, value->value); + } + goutput_write (out, " "); +} + +static void +dump_fundamental_type (GType type, const char *symbol, GOutputStream *out) +{ + guint n_interfaces; + guint i; + GType *interfaces; + GString *parent_str; + GType parent; + gboolean first = TRUE; + + + escaped_printf (out, " len > 0) + escaped_printf (out, " parents=\"%s\"", parent_str->str); + g_string_free (parent_str, TRUE); + + goutput_write (out, ">\n"); + + interfaces = g_type_interfaces (type, &n_interfaces); + for (i = 0; i < n_interfaces; i++) + { + GType itype = interfaces[i]; + escaped_printf (out, " \n", + g_type_name (itype)); + } + g_free (interfaces); + goutput_write (out, " \n"); +} + +static void +dump_type (GType type, const char *symbol, GOutputStream *out) +{ + switch (g_type_fundamental (type)) + { + case G_TYPE_OBJECT: + dump_object_type (type, symbol, out); + break; + case G_TYPE_INTERFACE: + dump_interface_type (type, symbol, out); + break; + case G_TYPE_BOXED: + dump_boxed_type (type, symbol, out); + break; + case G_TYPE_FLAGS: + dump_flags_type (type, symbol, out); + break; + case G_TYPE_ENUM: + dump_enum_type (type, symbol, out); + break; + case G_TYPE_POINTER: + /* GValue, etc. Just skip them. */ + break; + default: + dump_fundamental_type (type, symbol, out); + break; + } +} + +static void +dump_error_quark (GQuark quark, const char *symbol, GOutputStream *out) +{ + escaped_printf (out, " \n", + symbol, g_quark_to_string (quark)); +} + +/** + * g_irepository_dump: + * @arg: Comma-separated pair of input and output filenames + * @error: a %GError + * + * Argument specified is a comma-separated pair of filenames; i.e. of + * the form "input.txt,output.xml". The input file should be a + * UTF-8 Unix-line-ending text file, with each line containing either + * "get-type:" followed by the name of a GType _get_type function, or + * "error-quark:" followed by the name of an error quark function. No + * extra whitespace is allowed. + * + * The output file should already exist, but be empty. This function will + * overwrite its contents. + * + * Returns: %TRUE on success, %FALSE on error + */ +#ifndef G_IREPOSITORY_COMPILATION +static gboolean +dump_irepository (const char *arg, GError **error) G_GNUC_UNUSED; +static gboolean +dump_irepository (const char *arg, GError **error) +#else +gboolean +g_irepository_dump (const char *arg, GError **error) +#endif +{ + GHashTable *output_types; + char **args; + GFile *input_file; + GFile *output_file; + GFileInputStream *input; + GFileOutputStream *output; + GDataInputStream *in; + GModule *self; + gboolean caught_error = FALSE; + + self = g_module_open (NULL, 0); + if (!self) + { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to open self: %s", + g_module_error ()); + return FALSE; + } + + args = g_strsplit (arg, ",", 2); + + input_file = g_file_new_for_path (args[0]); + output_file = g_file_new_for_path (args[1]); + + g_strfreev (args); + + input = g_file_read (input_file, NULL, error); + g_object_unref (input_file); + + if (input == NULL) + { + g_object_unref (output_file); + return FALSE; + } + + output = g_file_replace (output_file, NULL, FALSE, 0, NULL, error); + g_object_unref (output_file); + + if (output == NULL) + { + g_input_stream_close (G_INPUT_STREAM (input), NULL, NULL); + g_object_unref (input); + return FALSE; + } + + goutput_write (G_OUTPUT_STREAM (output), "\n"); + goutput_write (G_OUTPUT_STREAM (output), "\n"); + + output_types = g_hash_table_new (NULL, NULL); + + in = g_data_input_stream_new (G_INPUT_STREAM (input)); + g_object_unref (input); + + while (TRUE) + { + gsize len; + char *line = g_data_input_stream_read_line (in, &len, NULL, NULL); + const char *function; + + if (line == NULL || *line == '\0') + { + g_free (line); + break; + } + + g_strchomp (line); + + if (strncmp (line, "get-type:", strlen ("get-type:")) == 0) + { + GType type; + + function = line + strlen ("get-type:"); + + type = invoke_get_type (self, function, error); + + if (type == G_TYPE_INVALID) + { + g_printerr ("Invalid GType function: '%s'\n", function); + caught_error = TRUE; + g_free (line); + break; + } + + if (g_hash_table_lookup (output_types, (gpointer) type)) + goto next; + g_hash_table_insert (output_types, (gpointer) type, (gpointer) type); + + dump_type (type, function, G_OUTPUT_STREAM (output)); + } + else if (strncmp (line, "error-quark:", strlen ("error-quark:")) == 0) + { + GQuark quark; + function = line + strlen ("error-quark:"); + quark = invoke_error_quark (self, function, error); + + if (quark == 0) + { + g_printerr ("Invalid error quark function: '%s'\n", function); + caught_error = TRUE; + g_free (line); + break; + } + + dump_error_quark (quark, function, G_OUTPUT_STREAM (output)); + } + + + next: + g_free (line); + } + + g_hash_table_destroy (output_types); + + goutput_write (G_OUTPUT_STREAM (output), "\n"); + + { + /* Avoid overwriting an earlier set error */ + caught_error |= !g_input_stream_close (G_INPUT_STREAM (in), NULL, + caught_error ? NULL : error); + caught_error |= !g_output_stream_close (G_OUTPUT_STREAM (output), NULL, + caught_error ? NULL : error); + } + + g_object_unref (in); + g_object_unref (output); + + return !caught_error; +} diff --git a/girepository/gi-dump-types.c b/girepository/gi-dump-types.c new file mode 100644 index 000000000..aeef774ce --- /dev/null +++ b/girepository/gi-dump-types.c @@ -0,0 +1,46 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +#include "gdump.c" +#ifdef G_OS_WIN32 + #include + #include /* For _get_osfhandle() */ + #include +#else + #include +#endif + +int +main (int argc, + char **argv) +{ + int i; + GOutputStream *Stdout; + GModule *self; + +#if defined(G_OS_WIN32) + HANDLE *hnd = (HANDLE) _get_osfhandle (1); + + g_return_val_if_fail (hnd && hnd != INVALID_HANDLE_VALUE, 1); + Stdout = g_win32_output_stream_new (hnd, FALSE); +#else + Stdout = g_unix_output_stream_new (1, FALSE); +#endif + + self = g_module_open (NULL, 0); + + for (i = 1; i < argc; i++) + { + GError *error = NULL; + GType type; + + type = invoke_get_type (self, argv[i], &error); + if (!type) + { + g_printerr ("%s\n", error->message); + g_clear_error (&error); + } + else + dump_type (type, argv[i], Stdout); + } + + return 0; +} diff --git a/girepository/giarginfo.c b/girepository/giarginfo.c new file mode 100644 index 000000000..96afccb6f --- /dev/null +++ b/girepository/giarginfo.c @@ -0,0 +1,332 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Argument implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include "gitypelib-internal.h" +#include "girepository-private.h" + + +/* GIArgInfo functions */ + +/** + * SECTION:giarginfo + * @title: GIArgInfo + * @short_description: Struct representing an argument + * + * GIArgInfo represents an argument of a callable. + * + * An argument is always part of a #GICallableInfo. + */ + +/** + * g_arg_info_get_direction: + * @info: a #GIArgInfo + * + * Obtain the direction of the argument. Check #GIDirection for possible + * direction values. + * + * Returns: the direction + */ +GIDirection +g_arg_info_get_direction (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_ARG_INFO (info), -1); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->in && blob->out) + return GI_DIRECTION_INOUT; + else if (blob->out) + return GI_DIRECTION_OUT; + else + return GI_DIRECTION_IN; +} + +/** + * g_arg_info_is_return_value: + * @info: a #GIArgInfo + * + * Obtain if the argument is a return value. It can either be a + * parameter or a return value. + * + * Returns: %TRUE if it is a return value + */ +gboolean +g_arg_info_is_return_value (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_ARG_INFO (info), FALSE); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->return_value; +} + +/** + * g_arg_info_is_caller_allocates: + * @info: a #GIArgInfo + * + * Obtain if the argument is a pointer to a struct or object that will + * receive an output of a function. The default assumption for + * %GI_DIRECTION_OUT arguments which have allocation is that the + * callee allocates; if this is %TRUE, then the caller must allocate. + * + * Returns: %TRUE if caller is required to have allocated the argument + */ +gboolean +g_arg_info_is_caller_allocates (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_ARG_INFO (info), FALSE); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->caller_allocates; +} + +/** + * g_arg_info_is_optional: + * @info: a #GIArgInfo + * + * Obtain if the argument is optional. For 'out' arguments this means + * that you can pass %NULL in order to ignore the result. + * + * Returns: %TRUE if it is an optional argument + */ +gboolean +g_arg_info_is_optional (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_ARG_INFO (info), FALSE); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->optional; +} + +/** + * g_arg_info_may_be_null: + * @info: a #GIArgInfo + * + * Obtain if the type of the argument includes the possibility of %NULL. + * For 'in' values this means that %NULL is a valid value. For 'out' + * values, this means that %NULL may be returned. + * + * See also g_arg_info_is_optional(). + * + * Returns: %TRUE if the value may be %NULL + */ +gboolean +g_arg_info_may_be_null (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_ARG_INFO (info), FALSE); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->nullable; +} + +/** + * g_arg_info_is_skip: + * @info: a #GIArgInfo + * + * Obtain if an argument is only useful in C. + * + * Returns: %TRUE if argument is only useful in C. + * Since: 1.30 + */ +gboolean +g_arg_info_is_skip (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_ARG_INFO (info), FALSE); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->skip; +} + +/** + * g_arg_info_get_ownership_transfer: + * @info: a #GIArgInfo + * + * Obtain the ownership transfer for this argument. + * #GITransfer contains a list of possible values. + * + * Returns: the transfer + */ +GITransfer +g_arg_info_get_ownership_transfer (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_ARG_INFO (info), -1); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->transfer_ownership) + return GI_TRANSFER_EVERYTHING; + else if (blob->transfer_container_ownership) + return GI_TRANSFER_CONTAINER; + else + return GI_TRANSFER_NOTHING; +} + +/** + * g_arg_info_get_scope: + * @info: a #GIArgInfo + * + * Obtain the scope type for this argument. The scope type explains + * how a callback is going to be invoked, most importantly when + * the resources required to invoke it can be freed. + * #GIScopeType contains a list of possible values. + * + * Returns: the scope type + */ +GIScopeType +g_arg_info_get_scope (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_ARG_INFO (info), -1); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->scope; +} + +/** + * g_arg_info_get_closure: + * @info: a #GIArgInfo + * + * Obtain the index of the user data argument. This is only valid + * for arguments which are callbacks. + * + * Returns: index of the user data argument or -1 if there is none + */ +gint +g_arg_info_get_closure (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_ARG_INFO (info), -1); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->closure; +} + +/** + * g_arg_info_get_destroy: + * @info: a #GIArgInfo + * + * Obtains the index of the #GDestroyNotify argument. This is only valid + * for arguments which are callbacks. + * + * Returns: index of the #GDestroyNotify argument or -1 if there is none + */ +gint +g_arg_info_get_destroy (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ArgBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_ARG_INFO (info), -1); + + blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->destroy; +} + +/** + * g_arg_info_get_type: + * @info: a #GIArgInfo + * + * Obtain the type information for @info. + * + * Returns: (transfer full): the #GITypeInfo holding the type + * information for @info, free it with g_base_info_unref() + * when done. + */ +GITypeInfo * +g_arg_info_get_type (GIArgInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_ARG_INFO (info), NULL); + + return _g_type_info_new ((GIBaseInfo*)info, rinfo->typelib, rinfo->offset + G_STRUCT_OFFSET (ArgBlob, arg_type)); +} + +/** + * g_arg_info_load_type: + * @info: a #GIArgInfo + * @type: (out caller-allocates): Initialized with information about type of @info + * + * Obtain information about a the type of given argument @info; this + * function is a variant of g_arg_info_get_type() designed for stack + * allocation. + * + * The initialized @type must not be referenced after @info is deallocated. + */ +void +g_arg_info_load_type (GIArgInfo *info, + GITypeInfo *type) +{ + GIRealInfo *rinfo = (GIRealInfo*) info; + + g_return_if_fail (info != NULL); + g_return_if_fail (GI_IS_ARG_INFO (info)); + + _g_type_info_init (type, (GIBaseInfo*)info, rinfo->typelib, rinfo->offset + G_STRUCT_OFFSET (ArgBlob, arg_type)); +} diff --git a/girepository/giarginfo.h b/girepository/giarginfo.h new file mode 100644 index 000000000..0cd1f79a7 --- /dev/null +++ b/girepository/giarginfo.h @@ -0,0 +1,82 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Argument + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIARGINFO_H__ +#define __GIARGINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_ARG_INFO + * @info: an info structure + * + * Checks if @info is a GIArgInfo. + */ +#define GI_IS_ARG_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_ARG) + + +GI_AVAILABLE_IN_ALL +GIDirection g_arg_info_get_direction (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_arg_info_is_return_value (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_arg_info_is_optional (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_arg_info_is_caller_allocates (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_arg_info_may_be_null (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_arg_info_is_skip (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +GITransfer g_arg_info_get_ownership_transfer (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +GIScopeType g_arg_info_get_scope (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_arg_info_get_closure (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_arg_info_get_destroy (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +GITypeInfo * g_arg_info_get_type (GIArgInfo *info); + +GI_AVAILABLE_IN_ALL +void g_arg_info_load_type (GIArgInfo *info, + GITypeInfo *type); +G_END_DECLS + +#endif /* __GIARGINFO_H__ */ diff --git a/girepository/gibaseinfo.c b/girepository/gibaseinfo.c new file mode 100644 index 000000000..d7a3a0171 --- /dev/null +++ b/girepository/gibaseinfo.c @@ -0,0 +1,674 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Base struct implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include "gitypelib-internal.h" +#include "girepository-private.h" + +#define INVALID_REFCOUNT 0x7FFFFFFF + +/* GBoxed registration of BaseInfo. */ +GType +g_base_info_gtype_get_type (void) +{ + static GType our_type = 0; + + if (our_type == 0) + our_type = + g_boxed_type_register_static ("GIBaseInfo", + (GBoxedCopyFunc) g_base_info_ref, + (GBoxedFreeFunc) g_base_info_unref); + + return our_type; +} + +/* info creation */ +GIBaseInfo * +_g_info_new_full (GIInfoType type, + GIRepository *repository, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset) +{ + GIRealInfo *info; + + g_return_val_if_fail (container != NULL || repository != NULL, NULL); + + info = g_slice_new (GIRealInfo); + + _g_info_init (info, type, repository, container, typelib, offset); + info->ref_count = 1; + + if (container && ((GIRealInfo *) container)->ref_count != INVALID_REFCOUNT) + g_base_info_ref (info->container); + + g_object_ref (info->repository); + + return (GIBaseInfo*)info; +} + +/** + * g_info_new: + * @type: TODO + * @container: TODO + * @typelib: TODO + * @offset: TODO + * + * TODO + * + * Returns: TODO + */ +GIBaseInfo * +g_info_new (GIInfoType type, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset) +{ + return _g_info_new_full (type, ((GIRealInfo*)container)->repository, container, typelib, offset); +} + +void +_g_info_init (GIRealInfo *info, + GIInfoType type, + GIRepository *repository, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset) +{ + memset (info, 0, sizeof (GIRealInfo)); + + /* Invalid refcount used to flag stack-allocated infos */ + info->ref_count = INVALID_REFCOUNT; + info->type = type; + + info->typelib = typelib; + info->offset = offset; + + if (container) + info->container = container; + + g_assert (G_IS_IREPOSITORY (repository)); + info->repository = repository; +} + +GIBaseInfo * +_g_info_from_entry (GIRepository *repository, + GITypelib *typelib, + guint16 index) +{ + GIBaseInfo *result; + DirEntry *entry = g_typelib_get_dir_entry (typelib, index); + + if (entry->local) + result = _g_info_new_full (entry->blob_type, repository, NULL, typelib, entry->offset); + else + { + const gchar *namespace = g_typelib_get_string (typelib, entry->offset); + const gchar *name = g_typelib_get_string (typelib, entry->name); + + result = g_irepository_find_by_name (repository, namespace, name); + if (result == NULL) + { + GIUnresolvedInfo *unresolved; + + unresolved = g_slice_new0 (GIUnresolvedInfo); + + unresolved->type = GI_INFO_TYPE_UNRESOLVED; + unresolved->ref_count = 1; + unresolved->repository = g_object_ref (repository); + unresolved->container = NULL; + unresolved->name = name; + unresolved->namespace = namespace; + + return (GIBaseInfo *)unresolved; + } + return (GIBaseInfo *)result; + } + + return (GIBaseInfo *)result; +} + +GITypeInfo * +_g_type_info_new (GIBaseInfo *container, + GITypelib *typelib, + guint32 offset) +{ + SimpleTypeBlob *type = (SimpleTypeBlob *)&typelib->data[offset]; + + return (GITypeInfo *) g_info_new (GI_INFO_TYPE_TYPE, container, typelib, + (type->flags.reserved == 0 && type->flags.reserved2 == 0) ? offset : type->offset); +} + +void +_g_type_info_init (GIBaseInfo *info, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset) +{ + GIRealInfo *rinfo = (GIRealInfo*)container; + SimpleTypeBlob *type = (SimpleTypeBlob *)&typelib->data[offset]; + + _g_info_init ((GIRealInfo*)info, GI_INFO_TYPE_TYPE, rinfo->repository, container, typelib, + (type->flags.reserved == 0 && type->flags.reserved2 == 0) ? offset : type->offset); +} + +/* GIBaseInfo functions */ + +/** + * SECTION:gibaseinfo + * @title: GIBaseInfo + * @short_description: Base struct for all GITypelib structs + * + * GIBaseInfo is the common base struct of all other Info structs + * accessible through the #GIRepository API. + * + * All info structures can be cast to a #GIBaseInfo, for instance: + * + * |[ + * GIFunctionInfo *function_info = ...; + * GIBaseInfo *info = (GIBaseInfo *) function_info; + * ]| + * + * Most #GIRepository APIs returning a #GIBaseInfo is actually + * creating a new struct; in other words, g_base_info_unref() has to + * be called when done accessing the data. + * + * #GIBaseInfo structuress are normally accessed by calling either + * g_irepository_find_by_name(), g_irepository_find_by_gtype() or + * g_irepository_get_info(). + * + * |[ + * GIBaseInfo *button_info = + * g_irepository_find_by_name (NULL, "Gtk", "Button"); + * + * // ... use button_info ... + * + * g_base_info_unref (button_info); + * ]| + * + * ## Hierarchy + * + * |[ + * GIBaseInfo + * +---- GIArgInfo + * +---- GICallableInfo + * +---- GIConstantInfo + * +---- GIFieldInfo + * +---- GIPropertyInfo + * +---- GIRegisteredTypeInfo + * +---- GITypeInfo + * ]| + */ + +/** + * g_base_info_ref: (skip) + * @info: a #GIBaseInfo + * + * Increases the reference count of @info. + * + * Returns: the same @info. + */ +GIBaseInfo * +g_base_info_ref (GIBaseInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*)info; + + g_assert (rinfo->ref_count != INVALID_REFCOUNT); + g_atomic_int_inc (&rinfo->ref_count); + + return info; +} + +/** + * g_base_info_unref: (skip) + * @info: a #GIBaseInfo + * + * Decreases the reference count of @info. When its reference count + * drops to 0, the info is freed. + */ +void +g_base_info_unref (GIBaseInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*)info; + + g_assert (rinfo->ref_count > 0 && rinfo->ref_count != INVALID_REFCOUNT); + + if (!g_atomic_int_dec_and_test (&rinfo->ref_count)) + return; + + if (rinfo->container && ((GIRealInfo *) rinfo->container)->ref_count != INVALID_REFCOUNT) + g_base_info_unref (rinfo->container); + + if (rinfo->repository) + g_object_unref (rinfo->repository); + + if (rinfo->type == GI_INFO_TYPE_UNRESOLVED) + g_slice_free (GIUnresolvedInfo, (GIUnresolvedInfo *) rinfo); + else + g_slice_free (GIRealInfo, rinfo); +} + +/** + * g_base_info_get_type: + * @info: a #GIBaseInfo + * + * Obtain the info type of the GIBaseInfo. + * + * Returns: the info type of @info + */ +GIInfoType +g_base_info_get_type (GIBaseInfo *info) +{ + + return ((GIRealInfo*)info)->type; +} + +/** + * g_base_info_get_name: + * @info: a #GIBaseInfo + * + * Obtain the name of the @info. What the name represents depends on + * the #GIInfoType of the @info. For instance for #GIFunctionInfo it is + * the name of the function. + * + * Returns: the name of @info or %NULL if it lacks a name. + */ +const gchar * +g_base_info_get_name (GIBaseInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*)info; + g_assert (rinfo->ref_count > 0); + switch (rinfo->type) + { + case GI_INFO_TYPE_FUNCTION: + case GI_INFO_TYPE_CALLBACK: + case GI_INFO_TYPE_STRUCT: + case GI_INFO_TYPE_BOXED: + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + case GI_INFO_TYPE_OBJECT: + case GI_INFO_TYPE_INTERFACE: + case GI_INFO_TYPE_CONSTANT: + case GI_INFO_TYPE_INVALID_0: + case GI_INFO_TYPE_UNION: + { + CommonBlob *blob = (CommonBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->name); + } + break; + + case GI_INFO_TYPE_VALUE: + { + ValueBlob *blob = (ValueBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->name); + } + break; + + case GI_INFO_TYPE_SIGNAL: + { + SignalBlob *blob = (SignalBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->name); + } + break; + + case GI_INFO_TYPE_PROPERTY: + { + PropertyBlob *blob = (PropertyBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->name); + } + break; + + case GI_INFO_TYPE_VFUNC: + { + VFuncBlob *blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->name); + } + break; + + case GI_INFO_TYPE_FIELD: + { + FieldBlob *blob = (FieldBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->name); + } + break; + + case GI_INFO_TYPE_ARG: + { + ArgBlob *blob = (ArgBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->name); + } + break; + case GI_INFO_TYPE_UNRESOLVED: + { + GIUnresolvedInfo *unresolved = (GIUnresolvedInfo *)info; + + return unresolved->name; + } + break; + case GI_INFO_TYPE_TYPE: + return NULL; + default: ; + g_assert_not_reached (); + /* unnamed */ + } + + return NULL; +} + +/** + * g_base_info_get_namespace: + * @info: a #GIBaseInfo + * + * Obtain the namespace of @info. + * + * Returns: the namespace + */ +const gchar * +g_base_info_get_namespace (GIBaseInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*) info; + Header *header = (Header *)rinfo->typelib->data; + + g_assert (rinfo->ref_count > 0); + + if (rinfo->type == GI_INFO_TYPE_UNRESOLVED) + { + GIUnresolvedInfo *unresolved = (GIUnresolvedInfo *)info; + + return unresolved->namespace; + } + + return g_typelib_get_string (rinfo->typelib, header->namespace); +} + +/** + * g_base_info_is_deprecated: + * @info: a #GIBaseInfo + * + * Obtain whether the @info is represents a metadata which is + * deprecated or not. + * + * Returns: %TRUE if deprecated + */ +gboolean +g_base_info_is_deprecated (GIBaseInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*) info; + switch (rinfo->type) + { + case GI_INFO_TYPE_FUNCTION: + case GI_INFO_TYPE_CALLBACK: + case GI_INFO_TYPE_STRUCT: + case GI_INFO_TYPE_BOXED: + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + case GI_INFO_TYPE_OBJECT: + case GI_INFO_TYPE_INTERFACE: + case GI_INFO_TYPE_CONSTANT: + case GI_INFO_TYPE_INVALID_0: + { + CommonBlob *blob = (CommonBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->deprecated; + } + break; + + case GI_INFO_TYPE_VALUE: + { + ValueBlob *blob = (ValueBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->deprecated; + } + break; + + case GI_INFO_TYPE_SIGNAL: + { + SignalBlob *blob = (SignalBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->deprecated; + } + break; + + case GI_INFO_TYPE_PROPERTY: + { + PropertyBlob *blob = (PropertyBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->deprecated; + } + break; + + case GI_INFO_TYPE_VFUNC: + case GI_INFO_TYPE_FIELD: + case GI_INFO_TYPE_ARG: + case GI_INFO_TYPE_TYPE: + default: ; + /* no deprecation flag for these */ + } + + return FALSE; +} + +/** + * g_base_info_get_attribute: + * @info: a #GIBaseInfo + * @name: a freeform string naming an attribute + * + * Retrieve an arbitrary attribute associated with this node. + * + * Returns: The value of the attribute, or %NULL if no such attribute exists + */ +const gchar * +g_base_info_get_attribute (GIBaseInfo *info, + const gchar *name) +{ + GIAttributeIter iter = { 0, }; + gchar *curname, *curvalue; + while (g_base_info_iterate_attributes (info, &iter, &curname, &curvalue)) + { + if (strcmp (name, curname) == 0) + return (const gchar*) curvalue; + } + + return NULL; +} + +static int +cmp_attribute (const void *av, + const void *bv) +{ + const AttributeBlob *a = av; + const AttributeBlob *b = bv; + + if (a->offset < b->offset) + return -1; + else if (a->offset == b->offset) + return 0; + else + return 1; +} + +/* + * _attribute_blob_find_first: + * @GIBaseInfo: A #GIBaseInfo. + * @blob_offset: The offset for the blob to find the first attribute for. + * + * Searches for the first #AttributeBlob for @blob_offset and returns + * it if found. + * + * Returns: A pointer to #AttributeBlob or %NULL if not found. + */ +AttributeBlob * +_attribute_blob_find_first (GIBaseInfo *info, + guint32 blob_offset) +{ + GIRealInfo *rinfo = (GIRealInfo *) info; + Header *header = (Header *)rinfo->typelib->data; + AttributeBlob blob, *first, *res, *previous; + + blob.offset = blob_offset; + + first = (AttributeBlob *) &rinfo->typelib->data[header->attributes]; + + res = bsearch (&blob, first, header->n_attributes, + header->attribute_blob_size, cmp_attribute); + + if (res == NULL) + return NULL; + + previous = res - 1; + while (previous >= first && previous->offset == blob_offset) + { + res = previous; + previous = res - 1; + } + + return res; +} + +/** + * g_base_info_iterate_attributes: + * @info: a #GIBaseInfo + * @iterator: (inout): a #GIAttributeIter structure, must be initialized; see below + * @name: (out) (transfer none): Returned name, must not be freed + * @value: (out) (transfer none): Returned name, must not be freed + * + * Iterate over all attributes associated with this node. The iterator + * structure is typically stack allocated, and must have its first + * member initialized to %NULL. Attributes are arbitrary namespaced key–value + * pairs which can be attached to almost any item. They are intended for use + * by software higher in the toolchain than bindings, and are distinct from + * normal GIR annotations. + * + * Both the @name and @value should be treated as constants + * and must not be freed. + * + * |[ + * void + * print_attributes (GIBaseInfo *info) + * { + * GIAttributeIter iter = { 0, }; + * char *name; + * char *value; + * while (g_base_info_iterate_attributes (info, &iter, &name, &value)) + * { + * g_print ("attribute name: %s value: %s", name, value); + * } + * } + * ]| + * + * Returns: %TRUE if there are more attributes + */ +gboolean +g_base_info_iterate_attributes (GIBaseInfo *info, + GIAttributeIter *iterator, + gchar **name, + gchar **value) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + AttributeBlob *next, *after; + + after = (AttributeBlob *) &rinfo->typelib->data[header->attributes + + header->n_attributes * header->attribute_blob_size]; + + if (iterator->data != NULL) + next = (AttributeBlob *) iterator->data; + else + next = _attribute_blob_find_first (info, rinfo->offset); + + if (next == NULL || next->offset != rinfo->offset || next >= after) + return FALSE; + + *name = (gchar*) g_typelib_get_string (rinfo->typelib, next->name); + *value = (gchar*) g_typelib_get_string (rinfo->typelib, next->value); + iterator->data = next + 1; + + return TRUE; +} + +/** + * g_base_info_get_container: + * @info: a #GIBaseInfo + * + * Obtain the container of the @info. The container is the parent + * GIBaseInfo. For instance, the parent of a #GIFunctionInfo is an + * #GIObjectInfo or #GIInterfaceInfo. + * + * Returns: (transfer none): the container + */ +GIBaseInfo * +g_base_info_get_container (GIBaseInfo *info) +{ + return ((GIRealInfo*)info)->container; +} + +/** + * g_base_info_get_typelib: + * @info: a #GIBaseInfo + * + * Obtain the typelib this @info belongs to + * + * Returns: (transfer none): the typelib. + */ +GITypelib * +g_base_info_get_typelib (GIBaseInfo *info) +{ + return ((GIRealInfo*)info)->typelib; +} + +/** + * g_base_info_equal: + * @info1: a #GIBaseInfo + * @info2: a #GIBaseInfo + * + * Compare two #GIBaseInfo. + * + * Using pointer comparison is not practical since many functions return + * different instances of #GIBaseInfo that refers to the same part of the + * TypeLib; use this function instead to do #GIBaseInfo comparisons. + * + * Returns: %TRUE if and only if @info1 equals @info2. + */ +gboolean +g_base_info_equal (GIBaseInfo *info1, GIBaseInfo *info2) +{ + /* Compare the TypeLib pointers, which are mmapped. */ + GIRealInfo *rinfo1 = (GIRealInfo*)info1; + GIRealInfo *rinfo2 = (GIRealInfo*)info2; + return rinfo1->typelib->data + rinfo1->offset == rinfo2->typelib->data + rinfo2->offset; +} + + diff --git a/girepository/gibaseinfo.h b/girepository/gibaseinfo.h new file mode 100644 index 000000000..69dfe4c1e --- /dev/null +++ b/girepository/gibaseinfo.h @@ -0,0 +1,102 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: GIBaseInfo + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIBASEINFO_H__ +#define __GIBASEINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include + +G_BEGIN_DECLS + +/** + * GIAttributeIter: + * + * An opaque structure used to iterate over attributes + * in a #GIBaseInfo struct. + */ +typedef struct { + /*< private >*/ + gpointer data; + gpointer data2; + gpointer data3; + gpointer data4; +} GIAttributeIter; + +#define GI_TYPE_BASE_INFO (g_base_info_gtype_get_type ()) + + +GI_AVAILABLE_IN_ALL +GType g_base_info_gtype_get_type (void) G_GNUC_CONST; + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_base_info_ref (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +void g_base_info_unref (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +GIInfoType g_base_info_get_type (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +const gchar * g_base_info_get_name (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +const gchar * g_base_info_get_namespace (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_base_info_is_deprecated (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +const gchar * g_base_info_get_attribute (GIBaseInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gboolean g_base_info_iterate_attributes (GIBaseInfo *info, + GIAttributeIter *iterator, + char **name, + char **value); + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_base_info_get_container (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +GITypelib * g_base_info_get_typelib (GIBaseInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_base_info_equal (GIBaseInfo *info1, + GIBaseInfo *info2); + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_info_new (GIInfoType type, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset); + +G_END_DECLS + +#endif /* __GIBASEINFO_H__ */ diff --git a/girepository/gicallableinfo.c b/girepository/gicallableinfo.c new file mode 100644 index 000000000..8b9fb3da2 --- /dev/null +++ b/girepository/gicallableinfo.c @@ -0,0 +1,790 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Callable implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "girffi.h" + +/* GICallableInfo functions */ + +/** + * SECTION:gicallableinfo + * @title: GICallableInfo + * @short_description: Struct representing a callable + * + * GICallableInfo represents an entity which is callable. + * + * Examples of callable are: + * + * - functions (#GIFunctionInfo) + * - virtual functions (#GIVFuncInfo) + * - callbacks (#GICallbackInfo). + * + * A callable has a list of arguments (#GIArgInfo), a return type, + * direction and a flag which decides if it returns null. + */ + +static guint32 +signature_offset (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*)info; + int sigoff = -1; + + switch (rinfo->type) + { + case GI_INFO_TYPE_FUNCTION: + sigoff = G_STRUCT_OFFSET (FunctionBlob, signature); + break; + case GI_INFO_TYPE_VFUNC: + sigoff = G_STRUCT_OFFSET (VFuncBlob, signature); + break; + case GI_INFO_TYPE_CALLBACK: + sigoff = G_STRUCT_OFFSET (CallbackBlob, signature); + break; + case GI_INFO_TYPE_SIGNAL: + sigoff = G_STRUCT_OFFSET (SignalBlob, signature); + break; + default: + g_assert_not_reached (); + } + if (sigoff >= 0) + return *(guint32 *)&rinfo->typelib->data[rinfo->offset + sigoff]; + return 0; +} + +/** + * g_callable_info_can_throw_gerror: + * @info: a #GICallableInfo + * + * TODO + * + * Since: 1.34 + * Returns: %TRUE if this #GICallableInfo can throw a #GError + */ +gboolean +g_callable_info_can_throw_gerror (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*)info; + SignatureBlob *signature; + + signature = (SignatureBlob *)&rinfo->typelib->data[signature_offset (info)]; + if (signature->throws) + return TRUE; + + /* Functions and VFuncs store "throws" in their own blobs. + * This info was additionally added to the SignatureBlob + * to support the other callables. For Functions and VFuncs, + * also check their legacy flag for compatibility. + */ + switch (rinfo->type) { + case GI_INFO_TYPE_FUNCTION: + { + FunctionBlob *blob; + blob = (FunctionBlob *)&rinfo->typelib->data[rinfo->offset]; + return blob->throws; + } + case GI_INFO_TYPE_VFUNC: + { + VFuncBlob *blob; + blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset]; + return blob->throws; + } + case GI_INFO_TYPE_CALLBACK: + case GI_INFO_TYPE_SIGNAL: + return FALSE; + default: + g_assert_not_reached (); + } +} + +/** + * g_callable_info_is_method: + * @info: a #GICallableInfo + * + * Determines if the callable info is a method. For #GIVFuncInfos, + * #GICallbackInfos, and #GISignalInfos, + * this is always true. Otherwise, this looks at the %GI_FUNCTION_IS_METHOD + * flag on the #GIFunctionInfo. + * + * Concretely, this function returns whether g_callable_info_get_n_args() + * matches the number of arguments in the raw C method. For methods, there + * is one more C argument than is exposed by introspection: the "self" + * or "this" object. + * + * Returns: %TRUE if @info is a method, %FALSE otherwise + * Since: 1.34 + */ +gboolean +g_callable_info_is_method (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*)info; + switch (rinfo->type) { + case GI_INFO_TYPE_FUNCTION: + { + FunctionBlob *blob; + blob = (FunctionBlob *)&rinfo->typelib->data[rinfo->offset]; + return (!blob->constructor && !blob->is_static); + } + case GI_INFO_TYPE_VFUNC: + case GI_INFO_TYPE_SIGNAL: + return TRUE; + case GI_INFO_TYPE_CALLBACK: + return FALSE; + default: + g_assert_not_reached (); + } +} + +/** + * g_callable_info_get_return_type: + * @info: a #GICallableInfo + * + * Obtain the return type of a callable item as a #GITypeInfo. + * + * Returns: (transfer full): the #GITypeInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GITypeInfo * +g_callable_info_get_return_type (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + guint32 offset; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), NULL); + + offset = signature_offset (info); + + return _g_type_info_new ((GIBaseInfo*)info, rinfo->typelib, offset); +} + + +/** + * g_callable_info_load_return_type: + * @info: a #GICallableInfo + * @type: (out caller-allocates): Initialized with return type of @info + * + * Obtain information about a return value of callable; this + * function is a variant of g_callable_info_get_return_type() designed for stack + * allocation. + * + * The initialized @type must not be referenced after @info is deallocated. + */ +void +g_callable_info_load_return_type (GICallableInfo *info, + GITypeInfo *type) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + guint32 offset; + + g_return_if_fail (info != NULL); + g_return_if_fail (GI_IS_CALLABLE_INFO (info)); + + offset = signature_offset (info); + + _g_type_info_init (type, (GIBaseInfo*)info, rinfo->typelib, offset); +} + +/** + * g_callable_info_may_return_null: + * @info: a #GICallableInfo + * + * See if a callable could return %NULL. + * + * Returns: %TRUE if callable could return %NULL + */ +gboolean +g_callable_info_may_return_null (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SignatureBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), FALSE); + + blob = (SignatureBlob *)&rinfo->typelib->data[signature_offset (info)]; + + return blob->may_return_null; +} + +/** + * g_callable_info_skip_return: + * @info: a #GICallableInfo + * + * See if a callable's return value is only useful in C. + * + * Returns: %TRUE if return value is only useful in C. + */ +gboolean +g_callable_info_skip_return (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SignatureBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), FALSE); + + blob = (SignatureBlob *)&rinfo->typelib->data[signature_offset (info)]; + + return blob->skip_return; +} + +/** + * g_callable_info_get_caller_owns: + * @info: a #GICallableInfo + * + * See whether the caller owns the return value of this callable. + * #GITransfer contains a list of possible transfer values. + * + * Returns: the transfer mode for the return value of the callable + */ +GITransfer +g_callable_info_get_caller_owns (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*) info; + SignatureBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), -1); + + blob = (SignatureBlob *)&rinfo->typelib->data[signature_offset (info)]; + + if (blob->caller_owns_return_value) + return GI_TRANSFER_EVERYTHING; + else if (blob->caller_owns_return_container) + return GI_TRANSFER_CONTAINER; + else + return GI_TRANSFER_NOTHING; +} + +/** + * g_callable_info_get_instance_ownership_transfer: + * @info: a #GICallableInfo + * + * Obtains the ownership transfer for the instance argument. + * #GITransfer contains a list of possible transfer values. + * + * Since: 1.42 + * Returns: the transfer mode of the instance argument + */ +GITransfer +g_callable_info_get_instance_ownership_transfer (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo*) info; + SignatureBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), -1); + + blob = (SignatureBlob *)&rinfo->typelib->data[signature_offset (info)]; + + if (blob->instance_transfer_ownership) + return GI_TRANSFER_EVERYTHING; + else + return GI_TRANSFER_NOTHING; +} + +/** + * g_callable_info_get_n_args: + * @info: a #GICallableInfo + * + * Obtain the number of arguments (both IN and OUT) for this callable. + * + * Returns: The number of arguments this callable expects. + */ +gint +g_callable_info_get_n_args (GICallableInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + gint offset; + SignatureBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), -1); + + offset = signature_offset (info); + blob = (SignatureBlob *)&rinfo->typelib->data[offset]; + + return blob->n_arguments; +} + +/** + * g_callable_info_get_arg: + * @info: a #GICallableInfo + * @n: the argument index to fetch + * + * Obtain information about a particular argument of this callable. + * + * Returns: (transfer full): the #GIArgInfo. Free it with + * g_base_info_unref() when done. + */ +GIArgInfo * +g_callable_info_get_arg (GICallableInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + gint offset; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_CALLABLE_INFO (info), NULL); + + offset = signature_offset (info); + header = (Header *)rinfo->typelib->data; + + return (GIArgInfo *) g_info_new (GI_INFO_TYPE_ARG, (GIBaseInfo*)info, rinfo->typelib, + offset + header->signature_blob_size + n * header->arg_blob_size); +} + +/** + * g_callable_info_load_arg: + * @info: a #GICallableInfo + * @n: the argument index to fetch + * @arg: (out caller-allocates): Initialize with argument number @n + * + * Obtain information about a particular argument of this callable; this + * function is a variant of g_callable_info_get_arg() designed for stack + * allocation. + * + * The initialized @arg must not be referenced after @info is deallocated. + */ +void +g_callable_info_load_arg (GICallableInfo *info, + gint n, + GIArgInfo *arg) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + gint offset; + + g_return_if_fail (info != NULL); + g_return_if_fail (GI_IS_CALLABLE_INFO (info)); + + offset = signature_offset (info); + header = (Header *)rinfo->typelib->data; + + _g_info_init ((GIRealInfo*)arg, GI_INFO_TYPE_ARG, rinfo->repository, (GIBaseInfo*)info, rinfo->typelib, + offset + header->signature_blob_size + n * header->arg_blob_size); +} + +/** + * g_callable_info_get_return_attribute: + * @info: a #GICallableInfo + * @name: a freeform string naming an attribute + * + * Retrieve an arbitrary attribute associated with the return value. + * + * Returns: The value of the attribute, or %NULL if no such attribute exists + */ +const gchar * +g_callable_info_get_return_attribute (GICallableInfo *info, + const gchar *name) +{ + GIAttributeIter iter = { 0, }; + gchar *curname, *curvalue; + while (g_callable_info_iterate_return_attributes (info, &iter, &curname, &curvalue)) + { + if (g_strcmp0 (name, curname) == 0) + return (const gchar*) curvalue; + } + + return NULL; +} + +/** + * g_callable_info_iterate_return_attributes: + * @info: a #GICallableInfo + * @iterator: (inout): a #GIAttributeIter structure, must be initialized; see below + * @name: (out) (transfer none): Returned name, must not be freed + * @value: (out) (transfer none): Returned name, must not be freed + * + * Iterate over all attributes associated with the return value. The + * iterator structure is typically stack allocated, and must have its + * first member initialized to %NULL. + * + * Both the @name and @value should be treated as constants + * and must not be freed. + * + * See g_base_info_iterate_attributes() for an example of how to use a + * similar API. + * + * Returns: %TRUE if there are more attributes + */ +gboolean +g_callable_info_iterate_return_attributes (GICallableInfo *info, + GIAttributeIter *iterator, + char **name, + char **value) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + AttributeBlob *next, *after; + guint32 blob_offset; + + after = (AttributeBlob *) &rinfo->typelib->data[header->attributes + + header->n_attributes * header->attribute_blob_size]; + + blob_offset = signature_offset (info); + + if (iterator->data != NULL) + next = (AttributeBlob *) iterator->data; + else + next = _attribute_blob_find_first (info, blob_offset); + + if (next == NULL || next->offset != blob_offset || next >= after) + return FALSE; + + *name = (gchar*) g_typelib_get_string (rinfo->typelib, next->name); + *value = (gchar*) g_typelib_get_string (rinfo->typelib, next->value); + iterator->data = next + 1; + + return TRUE; +} + +/** + * gi_type_tag_extract_ffi_return_value: + * @return_tag: #GITypeTag of the return value + * @interface_type: #GIInfoType of the underlying interface type + * @ffi_value: pointer to #GIFFIReturnValue union containing the return value + * from `ffi_call()` + * @arg: (out caller-allocates): pointer to an allocated #GIArgument + * + * Extract the correct bits from an `ffi_arg` return value into + * GIArgument. + * + * See: https://bugzilla.gnome.org/show_bug.cgi?id=665152 + * + * Also see `ffi_call(3)`: the storage requirements for return values + * are "special". + * + * The @interface_type argument only applies if @return_tag is + * %GI_TYPE_TAG_INTERFACE. Otherwise it is ignored. + * + * Since: 1.72 + */ +void +gi_type_tag_extract_ffi_return_value (GITypeTag return_tag, + GIInfoType interface_type, + GIFFIReturnValue *ffi_value, + GIArgument *arg) +{ + switch (return_tag) { + case GI_TYPE_TAG_INT8: + arg->v_int8 = (gint8) ffi_value->v_long; + break; + case GI_TYPE_TAG_UINT8: + arg->v_uint8 = (guint8) ffi_value->v_ulong; + break; + case GI_TYPE_TAG_INT16: + arg->v_int16 = (gint16) ffi_value->v_long; + break; + case GI_TYPE_TAG_UINT16: + arg->v_uint16 = (guint16) ffi_value->v_ulong; + break; + case GI_TYPE_TAG_INT32: + arg->v_int32 = (gint32) ffi_value->v_long; + break; + case GI_TYPE_TAG_UINT32: + case GI_TYPE_TAG_BOOLEAN: + case GI_TYPE_TAG_UNICHAR: + arg->v_uint32 = (guint32) ffi_value->v_ulong; + break; + case GI_TYPE_TAG_INT64: + arg->v_int64 = (gint64) ffi_value->v_int64; + break; + case GI_TYPE_TAG_UINT64: + arg->v_uint64 = (guint64) ffi_value->v_uint64; + break; + case GI_TYPE_TAG_FLOAT: + arg->v_float = ffi_value->v_float; + break; + case GI_TYPE_TAG_DOUBLE: + arg->v_double = ffi_value->v_double; + break; + case GI_TYPE_TAG_INTERFACE: + switch(interface_type) { + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + arg->v_int32 = (gint32) ffi_value->v_long; + break; + default: + arg->v_pointer = (gpointer) ffi_value->v_pointer; + break; + } + break; + default: + arg->v_pointer = (gpointer) ffi_value->v_pointer; + break; + } +} + +/** + * gi_type_info_extract_ffi_return_value: + * @return_info: #GITypeInfo describing the return type + * @ffi_value: pointer to #GIFFIReturnValue union containing the return value + * from `ffi_call()` + * @arg: (out caller-allocates): pointer to an allocated #GIArgument + * + * Extract the correct bits from an `ffi_arg` return value into + * #GIArgument. + * + * See: https://bugzilla.gnome.org/show_bug.cgi?id=665152 + * + * Also see `ffi_call(3)`: the storage requirements for return values + * are "special". + */ +void +gi_type_info_extract_ffi_return_value (GITypeInfo *return_info, + GIFFIReturnValue *ffi_value, + GIArgument *arg) +{ + GITypeTag return_tag = g_type_info_get_tag (return_info); + GIInfoType interface_type = GI_INFO_TYPE_INVALID; + + if (return_tag == GI_TYPE_TAG_INTERFACE) + { + GIBaseInfo *interface_info = g_type_info_get_interface (return_info); + interface_type = g_base_info_get_type (interface_info); + g_base_info_unref (interface_info); + } + + gi_type_tag_extract_ffi_return_value (return_tag, interface_type, + ffi_value, arg); +} + +/** + * g_callable_info_invoke: + * @info: TODO + * @function: TODO + * @in_args: (array length=n_in_args): TODO + * @n_in_args: TODO + * @out_args: (array length=n_out_args): TODO + * @n_out_args: TODO + * @return_value: TODO + * @is_method: TODO + * @throws: TODO + * @error: TODO + * + * TODO + */ +gboolean +g_callable_info_invoke (GIFunctionInfo *info, + gpointer function, + const GIArgument *in_args, + int n_in_args, + const GIArgument *out_args, + int n_out_args, + GIArgument *return_value, + gboolean is_method, + gboolean throws, + GError **error) +{ + ffi_cif cif; + ffi_type *rtype; + ffi_type **atypes; + GITypeInfo *tinfo; + GITypeInfo *rinfo; + GITypeTag rtag; + GIArgInfo *ainfo; + gint n_args, n_invoke_args, in_pos, out_pos, i; + gpointer *args; + gboolean success = FALSE; + GError *local_error = NULL; + gpointer error_address = &local_error; + GIFFIReturnValue ffi_return_value; + gpointer return_value_p; /* Will point inside the union return_value */ + + rinfo = g_callable_info_get_return_type ((GICallableInfo *)info); + rtype = g_type_info_get_ffi_type (rinfo); + rtag = g_type_info_get_tag(rinfo); + + in_pos = 0; + out_pos = 0; + + n_args = g_callable_info_get_n_args ((GICallableInfo *)info); + if (is_method) + { + if (n_in_args == 0) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"in\" arguments (handling this)"); + goto out; + } + n_invoke_args = n_args+1; + in_pos++; + } + else + n_invoke_args = n_args; + + if (throws) + /* Add an argument for the GError */ + n_invoke_args ++; + + atypes = g_alloca (sizeof (ffi_type*) * n_invoke_args); + args = g_alloca (sizeof (gpointer) * n_invoke_args); + + if (is_method) + { + atypes[0] = &ffi_type_pointer; + args[0] = (gpointer) &in_args[0]; + } + for (i = 0; i < n_args; i++) + { + int offset = (is_method ? 1 : 0); + ainfo = g_callable_info_get_arg ((GICallableInfo *)info, i); + switch (g_arg_info_get_direction (ainfo)) + { + case GI_DIRECTION_IN: + tinfo = g_arg_info_get_type (ainfo); + atypes[i+offset] = g_type_info_get_ffi_type (tinfo); + g_base_info_unref ((GIBaseInfo *)ainfo); + g_base_info_unref ((GIBaseInfo *)tinfo); + + if (in_pos >= n_in_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"in\" arguments (handling in)"); + goto out; + } + + args[i+offset] = (gpointer)&in_args[in_pos]; + in_pos++; + + break; + case GI_DIRECTION_OUT: + atypes[i+offset] = &ffi_type_pointer; + g_base_info_unref ((GIBaseInfo *)ainfo); + + if (out_pos >= n_out_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"out\" arguments (handling out)"); + goto out; + } + + args[i+offset] = (gpointer)&out_args[out_pos]; + out_pos++; + break; + case GI_DIRECTION_INOUT: + atypes[i+offset] = &ffi_type_pointer; + g_base_info_unref ((GIBaseInfo *)ainfo); + + if (in_pos >= n_in_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"in\" arguments (handling inout)"); + goto out; + } + + if (out_pos >= n_out_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"out\" arguments (handling inout)"); + goto out; + } + + args[i+offset] = (gpointer)&in_args[in_pos]; + in_pos++; + out_pos++; + break; + default: + g_base_info_unref ((GIBaseInfo *)ainfo); + g_assert_not_reached (); + } + } + + if (throws) + { + args[n_invoke_args - 1] = &error_address; + atypes[n_invoke_args - 1] = &ffi_type_pointer; + } + + if (in_pos < n_in_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too many \"in\" arguments (at end)"); + goto out; + } + if (out_pos < n_out_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too many \"out\" arguments (at end)"); + goto out; + } + + if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, n_invoke_args, rtype, atypes) != FFI_OK) + goto out; + + g_return_val_if_fail (return_value, FALSE); + /* See comment for GIFFIReturnValue above */ + switch (rtag) + { + case GI_TYPE_TAG_FLOAT: + return_value_p = &ffi_return_value.v_float; + break; + case GI_TYPE_TAG_DOUBLE: + return_value_p = &ffi_return_value.v_double; + break; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + return_value_p = &ffi_return_value.v_uint64; + break; + default: + return_value_p = &ffi_return_value.v_long; + } + ffi_call (&cif, function, return_value_p, args); + + if (local_error) + { + g_propagate_error (error, local_error); + success = FALSE; + } + else + { + gi_type_info_extract_ffi_return_value (rinfo, &ffi_return_value, return_value); + success = TRUE; + } + out: + g_base_info_unref ((GIBaseInfo *)rinfo); + return success; +} diff --git a/girepository/gicallableinfo.h b/girepository/gicallableinfo.h new file mode 100644 index 000000000..065ff91d6 --- /dev/null +++ b/girepository/gicallableinfo.h @@ -0,0 +1,110 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Callable + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GICALLABLEINFO_H__ +#define __GICALLABLEINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_CALLABLE_INFO + * @info: an info structure + * + * Checks if @info is a #GICallableInfo or derived from it. + */ +#define GI_IS_CALLABLE_INFO(info) \ + ((g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_FUNCTION) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_CALLBACK) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_SIGNAL) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_VFUNC)) + + +GI_AVAILABLE_IN_1_34 +gboolean g_callable_info_is_method (GICallableInfo *info); + +GI_AVAILABLE_IN_1_34 +gboolean g_callable_info_can_throw_gerror (GICallableInfo *info); + +GI_AVAILABLE_IN_ALL +GITypeInfo * g_callable_info_get_return_type (GICallableInfo *info); + +GI_AVAILABLE_IN_ALL +void g_callable_info_load_return_type (GICallableInfo *info, + GITypeInfo *type); + +GI_AVAILABLE_IN_ALL +const gchar * g_callable_info_get_return_attribute (GICallableInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gboolean g_callable_info_iterate_return_attributes (GICallableInfo *info, + GIAttributeIter *iterator, + char **name, + char **value); + +GI_AVAILABLE_IN_ALL +GITransfer g_callable_info_get_caller_owns (GICallableInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_callable_info_may_return_null (GICallableInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_callable_info_skip_return (GICallableInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_callable_info_get_n_args (GICallableInfo *info); + +GI_AVAILABLE_IN_ALL +GIArgInfo * g_callable_info_get_arg (GICallableInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +void g_callable_info_load_arg (GICallableInfo *info, + gint n, + GIArgInfo *arg); + +GI_AVAILABLE_IN_1_34 +gboolean g_callable_info_invoke (GICallableInfo *info, + gpointer function, + const GIArgument *in_args, + int n_in_args, + const GIArgument *out_args, + int n_out_args, + GIArgument *return_value, + gboolean is_method, + gboolean throws, + GError **error); + +GI_AVAILABLE_IN_1_42 +GITransfer g_callable_info_get_instance_ownership_transfer (GICallableInfo *info); + +G_END_DECLS + + +#endif /* __GICALLABLEINFO_H__ */ + diff --git a/girepository/giconstantinfo.c b/girepository/giconstantinfo.c new file mode 100644 index 000000000..148d66b61 --- /dev/null +++ b/girepository/giconstantinfo.c @@ -0,0 +1,179 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Constant implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include // memcpy + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" + +/** + * SECTION:giconstantinfo + * @title: GIConstantInfo + * @short_description: Struct representing a constant + * + * GIConstantInfo represents a constant. + * + * A constant has a type associated which can be obtained by calling + * g_constant_info_get_type() and a value, which can be obtained by + * calling g_constant_info_get_value(). + */ + + +/** + * g_constant_info_get_type: + * @info: a #GIConstantInfo + * + * Obtain the type of the constant as a #GITypeInfo. + * + * Returns: (transfer full): the #GITypeInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GITypeInfo * +g_constant_info_get_type (GIConstantInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_CONSTANT_INFO (info), NULL); + + return _g_type_info_new ((GIBaseInfo*)info, rinfo->typelib, rinfo->offset + 8); +} + +#define DO_ALIGNED_COPY(dest_addr, src_addr, type) \ + memcpy((dest_addr), (src_addr), sizeof(type)) + +/** + * g_constant_info_free_value: (skip) + * @info: a #GIConstantInfo + * @value: the argument + * + * Free the value returned from g_constant_info_get_value(). + * + * Since: 1.32 + */ +void +g_constant_info_free_value (GIConstantInfo *info, + GIArgument *value) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ConstantBlob *blob; + + g_return_if_fail (info != NULL); + g_return_if_fail (GI_IS_CONSTANT_INFO (info)); + + blob = (ConstantBlob *)&rinfo->typelib->data[rinfo->offset]; + + /* FIXME non-basic types ? */ + if (blob->type.flags.reserved == 0 && blob->type.flags.reserved2 == 0) + { + if (blob->type.flags.pointer) + g_free (value->v_pointer); + } +} + +/** + * g_constant_info_get_value: (skip) + * @info: a #GIConstantInfo + * @value: (out): an argument + * + * Obtain the value associated with the #GIConstantInfo and store it in the + * @value parameter. @argument needs to be allocated before passing it in. + * The size of the constant value stored in @argument will be returned. + * Free the value with g_constant_info_free_value(). + * + * Returns: size of the constant + */ +gint +g_constant_info_get_value (GIConstantInfo *info, + GIArgument *value) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ConstantBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_CONSTANT_INFO (info), 0); + + blob = (ConstantBlob *)&rinfo->typelib->data[rinfo->offset]; + + /* FIXME non-basic types ? */ + if (blob->type.flags.reserved == 0 && blob->type.flags.reserved2 == 0) + { + if (blob->type.flags.pointer) + { +#if GLIB_CHECK_VERSION (2, 67, 5) + gsize blob_size = blob->size; + + value->v_pointer = g_memdup2 (&rinfo->typelib->data[blob->offset], blob_size); +#else + value->v_pointer = g_memdup (&rinfo->typelib->data[blob->offset], blob->size); +#endif + } + else + { + switch (blob->type.flags.tag) + { + case GI_TYPE_TAG_BOOLEAN: + value->v_boolean = *(gboolean*)&rinfo->typelib->data[blob->offset]; + break; + case GI_TYPE_TAG_INT8: + value->v_int8 = *(gint8*)&rinfo->typelib->data[blob->offset]; + break; + case GI_TYPE_TAG_UINT8: + value->v_uint8 = *(guint8*)&rinfo->typelib->data[blob->offset]; + break; + case GI_TYPE_TAG_INT16: + value->v_int16 = *(gint16*)&rinfo->typelib->data[blob->offset]; + break; + case GI_TYPE_TAG_UINT16: + value->v_uint16 = *(guint16*)&rinfo->typelib->data[blob->offset]; + break; + case GI_TYPE_TAG_INT32: + value->v_int32 = *(gint32*)&rinfo->typelib->data[blob->offset]; + break; + case GI_TYPE_TAG_UINT32: + value->v_uint32 = *(guint32*)&rinfo->typelib->data[blob->offset]; + break; + case GI_TYPE_TAG_INT64: + DO_ALIGNED_COPY(&value->v_int64, &rinfo->typelib->data[blob->offset], gint64); + break; + case GI_TYPE_TAG_UINT64: + DO_ALIGNED_COPY(&value->v_uint64, &rinfo->typelib->data[blob->offset], guint64); + break; + case GI_TYPE_TAG_FLOAT: + DO_ALIGNED_COPY(&value->v_float, &rinfo->typelib->data[blob->offset], gfloat); + break; + case GI_TYPE_TAG_DOUBLE: + DO_ALIGNED_COPY(&value->v_double, &rinfo->typelib->data[blob->offset], gdouble); + break; + default: + g_assert_not_reached (); + } + } + } + + return blob->size; +} + diff --git a/girepository/giconstantinfo.h b/girepository/giconstantinfo.h new file mode 100644 index 000000000..c958c8fde --- /dev/null +++ b/girepository/giconstantinfo.h @@ -0,0 +1,58 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Constant + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GICONSTANTINFO_H__ +#define __GICONSTANTINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_CONSTANT_INFO + * @info: an info structure + * + * Checks if @info is a #GIConstantInfo. + */ +#define GI_IS_CONSTANT_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_CONSTANT) + + +GI_AVAILABLE_IN_ALL +GITypeInfo * g_constant_info_get_type (GIConstantInfo *info); + +GI_AVAILABLE_IN_ALL +void g_constant_info_free_value(GIConstantInfo *info, + GIArgument *value); + +GI_AVAILABLE_IN_ALL +gint g_constant_info_get_value(GIConstantInfo *info, + GIArgument *value); +G_END_DECLS + + +#endif /* __GICONSTANTINFO_H__ */ + diff --git a/girepository/gienuminfo.c b/girepository/gienuminfo.c new file mode 100644 index 000000000..c41d0308e --- /dev/null +++ b/girepository/gienuminfo.c @@ -0,0 +1,232 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Enum implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" + +/** + * SECTION:gienuminfo + * @title: GIEnumInfo + * @short_description: Structs representing an enumeration and its values + * + * A GIEnumInfo represents an enumeration, and a GIValueInfo represents + * a value in the enumeration. + * + * The GIEnumInfo contains a set of values and a type. + * + * The GIValueInfo is fetched by calling g_enum_info_get_value() on + * a GIEnumInfo. + */ + +/** + * g_enum_info_get_n_values: + * @info: a #GIEnumInfo + * + * Obtain the number of values this enumeration contains. + * + * Returns: the number of enumeration values + */ +gint +g_enum_info_get_n_values (GIEnumInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + EnumBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_ENUM_INFO (info), 0); + + blob = (EnumBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_values; +} + +/** + * g_enum_info_get_error_domain: + * @info: a #GIEnumInfo + * + * Obtain the string form of the quark for the error domain associated with + * this enum, if any. + * + * Returns: (transfer none): the string form of the error domain associated + * with this enum, or %NULL. + * Since: 1.30 + */ +const gchar * +g_enum_info_get_error_domain (GIEnumInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + EnumBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_ENUM_INFO (info), 0); + + blob = (EnumBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->error_domain) + return g_typelib_get_string (rinfo->typelib, blob->error_domain); + else + return NULL; +} + +/** + * g_enum_info_get_value: + * @info: a #GIEnumInfo + * @n: index of value to fetch + * + * Obtain a value for this enumeration. + * + * Returns: (transfer full): the enumeration value or %NULL if type tag is wrong, + * free the struct with g_base_info_unref() when done. + */ +GIValueInfo * +g_enum_info_get_value (GIEnumInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + gint offset; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_ENUM_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + offset = rinfo->offset + header->enum_blob_size + + n * header->value_blob_size; + + return (GIValueInfo *) g_info_new (GI_INFO_TYPE_VALUE, (GIBaseInfo*)info, rinfo->typelib, offset); +} + +/** + * g_enum_info_get_n_methods: + * @info: a #GIEnumInfo + * + * Obtain the number of methods that this enum type has. + * + * Returns: number of methods + * Since: 1.30 + */ +gint +g_enum_info_get_n_methods (GIEnumInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + EnumBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_ENUM_INFO (info), 0); + + blob = (EnumBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_methods; +} + +/** + * g_enum_info_get_method: + * @info: a #GIEnumInfo + * @n: index of method to get + * + * Obtain an enum type method at index @n. + * + * Returns: (transfer full): the #GIFunctionInfo. Free the struct by calling + * g_base_info_unref() when done. + * Since: 1.30 + */ +GIFunctionInfo * +g_enum_info_get_method (GIEnumInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + EnumBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_ENUM_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (EnumBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->enum_blob_size + + blob->n_values * header->value_blob_size + + n * header->function_blob_size; + + return (GIFunctionInfo *) g_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_enum_info_get_storage_type: + * @info: a #GIEnumInfo + * + * Obtain the tag of the type used for the enum in the C ABI. This will + * will be a signed or unsigned integral type. + * + * Note that in the current implementation the width of the type is + * computed correctly, but the signed or unsigned nature of the type + * may not match the sign of the type used by the C compiler. + * + * Returns: the storage type for the enumeration + */ +GITypeTag +g_enum_info_get_storage_type (GIEnumInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + EnumBlob *blob; + + g_return_val_if_fail (info != NULL, GI_TYPE_TAG_BOOLEAN); + g_return_val_if_fail (GI_IS_ENUM_INFO (info), GI_TYPE_TAG_BOOLEAN); + + blob = (EnumBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->storage_type; +} + +/** + * g_value_info_get_value: + * @info: a #GIValueInfo + * + * Obtain the enumeration value of the #GIValueInfo. + * + * Returns: the enumeration value. This will always be representable + * as a 32-bit signed or unsigned value. The use of gint64 as the + * return type is to allow both. + */ +gint64 +g_value_info_get_value (GIValueInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ValueBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_VALUE_INFO (info), -1); + + blob = (ValueBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->unsigned_value) + return (gint64)(guint32)blob->value; + else + return (gint64)blob->value; +} diff --git a/girepository/gienuminfo.h b/girepository/gienuminfo.h new file mode 100644 index 000000000..3f990e656 --- /dev/null +++ b/girepository/gienuminfo.h @@ -0,0 +1,82 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Enum and Enum values + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIENUMINFO_H__ +#define __GIENUMINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_ENUM_INFO + * @info: an info structure + * + * Checks if @info is a #GIEnumInfo. + */ +#define GI_IS_ENUM_INFO(info) \ + ((g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_ENUM) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_FLAGS)) + +/** + * GI_IS_VALUE_INFO + * @info: an info structure + * + * Checks if @info is a #GIValueInfo. + */ +#define GI_IS_VALUE_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_VALUE) + + +GI_AVAILABLE_IN_ALL +gint g_enum_info_get_n_values (GIEnumInfo *info); + +GI_AVAILABLE_IN_ALL +GIValueInfo * g_enum_info_get_value (GIEnumInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gint g_enum_info_get_n_methods (GIEnumInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_enum_info_get_method (GIEnumInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GITypeTag g_enum_info_get_storage_type (GIEnumInfo *info); + +GI_AVAILABLE_IN_ALL +const gchar * g_enum_info_get_error_domain (GIEnumInfo *info); + + +GI_AVAILABLE_IN_ALL +gint64 g_value_info_get_value (GIValueInfo *info); + +G_END_DECLS + + +#endif /* __GIENUMINFO_H__ */ + diff --git a/girepository/gifieldinfo.c b/girepository/gifieldinfo.c new file mode 100644 index 000000000..5bc19899b --- /dev/null +++ b/girepository/gifieldinfo.c @@ -0,0 +1,552 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Field implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" +#include "config.h" + +/** + * SECTION:gifieldinfo + * @title: GIFieldInfo + * @short_description: Struct representing a struct or union field + * + * A GIFieldInfo struct represents a field of a struct, union, or object. + * + * The GIFieldInfo is fetched by calling g_struct_info_get_field(), + * g_union_info_get_field() or g_object_info_get_field(). + * + * A field has a size, type and a struct offset asssociated and a set of flags, + * which are currently #GI_FIELD_IS_READABLE or #GI_FIELD_IS_WRITABLE. + * + * See also: #GIStructInfo, #GIUnionInfo, #GIObjectInfo + */ + +/** + * g_field_info_get_flags: + * @info: a #GIFieldInfo + * + * Obtain the flags for this #GIFieldInfo. See #GIFieldInfoFlags for possible + * flag values. + * + * Returns: the flags + */ +GIFieldInfoFlags +g_field_info_get_flags (GIFieldInfo *info) +{ + GIFieldInfoFlags flags; + GIRealInfo *rinfo = (GIRealInfo *)info; + FieldBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_FIELD_INFO (info), 0); + + blob = (FieldBlob *)&rinfo->typelib->data[rinfo->offset]; + + flags = 0; + + if (blob->readable) + flags = flags | GI_FIELD_IS_READABLE; + + if (blob->writable) + flags = flags | GI_FIELD_IS_WRITABLE; + + return flags; +} + +/** + * g_field_info_get_size: + * @info: a #GIFieldInfo + * + * Obtain the size in bits of the field member, this is how + * much space you need to allocate to store the field. + * + * Returns: the field size + */ +gint +g_field_info_get_size (GIFieldInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + FieldBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_FIELD_INFO (info), 0); + + blob = (FieldBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->bits; +} + +/** + * g_field_info_get_offset: + * @info: a #GIFieldInfo + * + * Obtain the offset in bytes of the field member, this is relative + * to the beginning of the struct or union. + * + * Returns: the field offset + */ +gint +g_field_info_get_offset (GIFieldInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + FieldBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_FIELD_INFO (info), 0); + + blob = (FieldBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->struct_offset; +} + +/** + * g_field_info_get_type: + * @info: a #GIFieldInfo + * + * Obtain the type of a field as a #GITypeInfo. + * + * Returns: (transfer full): the #GITypeInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GITypeInfo * +g_field_info_get_type (GIFieldInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + FieldBlob *blob; + GIRealInfo *type_info; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_FIELD_INFO (info), NULL); + + blob = (FieldBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->has_embedded_type) + { + type_info = (GIRealInfo *) g_info_new (GI_INFO_TYPE_TYPE, + (GIBaseInfo*)info, rinfo->typelib, + rinfo->offset + header->field_blob_size); + type_info->type_is_embedded = TRUE; + } + else + return _g_type_info_new ((GIBaseInfo*)info, rinfo->typelib, rinfo->offset + G_STRUCT_OFFSET (FieldBlob, type)); + + return (GIBaseInfo*)type_info; +} + +/** + * g_field_info_get_field: (skip) + * @field_info: a #GIFieldInfo + * @mem: pointer to a block of memory representing a C structure or union + * @value: a #GIArgument into which to store the value retrieved + * + * Reads a field identified by a #GIFieldInfo from a C structure or + * union. This only handles fields of simple C types. It will fail + * for a field of a composite type like a nested structure or union + * even if that is actually readable. + * + * Returns: %TRUE if reading the field succeeded, otherwise %FALSE + */ +gboolean +g_field_info_get_field (GIFieldInfo *field_info, + gpointer mem, + GIArgument *value) +{ + int offset; + GITypeInfo *type_info; + gboolean result = FALSE; + + g_return_val_if_fail (field_info != NULL, FALSE); + g_return_val_if_fail (GI_IS_FIELD_INFO (field_info), FALSE); + + if ((g_field_info_get_flags (field_info) & GI_FIELD_IS_READABLE) == 0) + return FALSE; + + offset = g_field_info_get_offset (field_info); + type_info = g_field_info_get_type (field_info); + + if (g_type_info_is_pointer (type_info)) + { + value->v_pointer = G_STRUCT_MEMBER (gpointer, mem, offset); + result = TRUE; + } + else + { + switch (g_type_info_get_tag (type_info)) + { + case GI_TYPE_TAG_VOID: + g_warning("Field %s: should not be have void type", + g_base_info_get_name ((GIBaseInfo *)field_info)); + break; + case GI_TYPE_TAG_BOOLEAN: + value->v_boolean = G_STRUCT_MEMBER (gboolean, mem, offset) != FALSE; + result = TRUE; + break; + case GI_TYPE_TAG_INT8: + case GI_TYPE_TAG_UINT8: + value->v_uint8 = G_STRUCT_MEMBER (guint8, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_INT16: + case GI_TYPE_TAG_UINT16: + value->v_uint16 = G_STRUCT_MEMBER (guint16, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_INT32: + case GI_TYPE_TAG_UINT32: + case GI_TYPE_TAG_UNICHAR: + value->v_uint32 = G_STRUCT_MEMBER (guint32, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + value->v_uint64 = G_STRUCT_MEMBER (guint64, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_GTYPE: + value->v_size = G_STRUCT_MEMBER (gsize, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_FLOAT: + value->v_float = G_STRUCT_MEMBER (gfloat, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_DOUBLE: + value->v_double = G_STRUCT_MEMBER (gdouble, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_ARRAY: + /* We don't check the array type and that it is fixed-size, + we trust g-ir-compiler to do the right thing */ + value->v_pointer = G_STRUCT_MEMBER_P (mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + case GI_TYPE_TAG_GHASH: + g_warning("Field %s: type %s should have is_pointer set", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_type_tag_to_string (g_type_info_get_tag (type_info))); + break; + case GI_TYPE_TAG_ERROR: + /* Needs to be handled by the language binding directly */ + break; + case GI_TYPE_TAG_INTERFACE: + { + GIBaseInfo *interface = g_type_info_get_interface (type_info); + switch (g_base_info_get_type (interface)) + { + case GI_INFO_TYPE_STRUCT: + case GI_INFO_TYPE_UNION: + case GI_INFO_TYPE_BOXED: + /* Needs to be handled by the language binding directly */ + break; + case GI_INFO_TYPE_OBJECT: + break; + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + { + /* FIXME: there's a mismatch here between the value->v_int we use + * here and the gint64 result returned from g_value_info_get_value(). + * But to switch this to gint64, we'd have to make g_function_info_invoke() + * translate value->v_int64 to the proper ABI for an enum function + * call parameter, which will usually be int, and then fix up language + * bindings. + */ + GITypeTag storage_type = g_enum_info_get_storage_type ((GIEnumInfo *)interface); + switch (storage_type) + { + case GI_TYPE_TAG_INT8: + case GI_TYPE_TAG_UINT8: + value->v_int = (gint)G_STRUCT_MEMBER (guint8, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_INT16: + case GI_TYPE_TAG_UINT16: + value->v_int = (gint)G_STRUCT_MEMBER (guint16, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_INT32: + case GI_TYPE_TAG_UINT32: + value->v_int = (gint)G_STRUCT_MEMBER (guint32, mem, offset); + result = TRUE; + break; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + value->v_int = (gint)G_STRUCT_MEMBER (guint64, mem, offset); + result = TRUE; + break; + default: + g_warning("Field %s: Unexpected enum storage type %s", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_type_tag_to_string (storage_type)); + break; + } + break; + } + case GI_INFO_TYPE_VFUNC: + case GI_INFO_TYPE_CALLBACK: + g_warning("Field %s: Interface type %d should have is_pointer set", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_base_info_get_type (interface)); + break; + case GI_INFO_TYPE_INVALID: + case GI_INFO_TYPE_INTERFACE: + case GI_INFO_TYPE_FUNCTION: + case GI_INFO_TYPE_CONSTANT: + case GI_INFO_TYPE_INVALID_0: + case GI_INFO_TYPE_VALUE: + case GI_INFO_TYPE_SIGNAL: + case GI_INFO_TYPE_PROPERTY: + case GI_INFO_TYPE_FIELD: + case GI_INFO_TYPE_ARG: + case GI_INFO_TYPE_TYPE: + case GI_INFO_TYPE_UNRESOLVED: + g_warning("Field %s: Interface type %d not expected", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_base_info_get_type (interface)); + break; + default: + break; + } + + g_base_info_unref ((GIBaseInfo *)interface); + break; + } + break; + default: + break; + } + } + + g_base_info_unref ((GIBaseInfo *)type_info); + + return result; +} + +/** + * g_field_info_set_field: (skip) + * @field_info: a #GIFieldInfo + * @mem: pointer to a block of memory representing a C structure or union + * @value: a #GIArgument holding the value to store + * + * Writes a field identified by a #GIFieldInfo to a C structure or + * union. This only handles fields of simple C types. It will fail + * for a field of a composite type like a nested structure or union + * even if that is actually writable. Note also that that it will refuse + * to write fields where memory management would by required. A field + * with a type such as 'char *' must be set with a setter function. + * + * Returns: %TRUE if writing the field succeeded, otherwise %FALSE + */ +gboolean +g_field_info_set_field (GIFieldInfo *field_info, + gpointer mem, + const GIArgument *value) +{ + int offset; + GITypeInfo *type_info; + gboolean result = FALSE; + + g_return_val_if_fail (field_info != NULL, FALSE); + g_return_val_if_fail (GI_IS_FIELD_INFO (field_info), FALSE); + + if ((g_field_info_get_flags (field_info) & GI_FIELD_IS_WRITABLE) == 0) + return FALSE; + + offset = g_field_info_get_offset (field_info); + type_info = g_field_info_get_type (field_info); + + if (!g_type_info_is_pointer (type_info)) + { + switch (g_type_info_get_tag (type_info)) + { + case GI_TYPE_TAG_VOID: + g_warning("Field %s: should not be have void type", + g_base_info_get_name ((GIBaseInfo *)field_info)); + break; + case GI_TYPE_TAG_BOOLEAN: + G_STRUCT_MEMBER (gboolean, mem, offset) = value->v_boolean != FALSE; + result = TRUE; + break; + case GI_TYPE_TAG_INT8: + case GI_TYPE_TAG_UINT8: + G_STRUCT_MEMBER (guint8, mem, offset) = value->v_uint8; + result = TRUE; + break; + case GI_TYPE_TAG_INT16: + case GI_TYPE_TAG_UINT16: + G_STRUCT_MEMBER (guint16, mem, offset) = value->v_uint16; + result = TRUE; + break; + case GI_TYPE_TAG_INT32: + case GI_TYPE_TAG_UINT32: + case GI_TYPE_TAG_UNICHAR: + G_STRUCT_MEMBER (guint32, mem, offset) = value->v_uint32; + result = TRUE; + break; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + G_STRUCT_MEMBER (guint64, mem, offset) = value->v_uint64; + result = TRUE; + break; + case GI_TYPE_TAG_GTYPE: + G_STRUCT_MEMBER (gsize, mem, offset) = value->v_size; + result = TRUE; + break; + case GI_TYPE_TAG_FLOAT: + G_STRUCT_MEMBER (gfloat, mem, offset) = value->v_float; + result = TRUE; + break; + case GI_TYPE_TAG_DOUBLE: + G_STRUCT_MEMBER (gdouble, mem, offset)= value->v_double; + result = TRUE; + break; + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + case GI_TYPE_TAG_ARRAY: + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + case GI_TYPE_TAG_GHASH: + g_warning("Field %s: type %s should have is_pointer set", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_type_tag_to_string (g_type_info_get_tag (type_info))); + break; + case GI_TYPE_TAG_ERROR: + /* Needs to be handled by the language binding directly */ + break; + case GI_TYPE_TAG_INTERFACE: + { + GIBaseInfo *interface = g_type_info_get_interface (type_info); + switch (g_base_info_get_type (interface)) + { + case GI_INFO_TYPE_STRUCT: + case GI_INFO_TYPE_UNION: + case GI_INFO_TYPE_BOXED: + /* Needs to be handled by the language binding directly */ + break; + case GI_INFO_TYPE_OBJECT: + break; + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + { + /* See FIXME above + */ + GITypeTag storage_type = g_enum_info_get_storage_type ((GIEnumInfo *)interface); + switch (storage_type) + { + case GI_TYPE_TAG_INT8: + case GI_TYPE_TAG_UINT8: + G_STRUCT_MEMBER (guint8, mem, offset) = (guint8)value->v_int; + result = TRUE; + break; + case GI_TYPE_TAG_INT16: + case GI_TYPE_TAG_UINT16: + G_STRUCT_MEMBER (guint16, mem, offset) = (guint16)value->v_int; + result = TRUE; + break; + case GI_TYPE_TAG_INT32: + case GI_TYPE_TAG_UINT32: + G_STRUCT_MEMBER (guint32, mem, offset) = (guint32)value->v_int; + result = TRUE; + break; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + G_STRUCT_MEMBER (guint64, mem, offset) = (guint64)value->v_int; + result = TRUE; + break; + default: + g_warning("Field %s: Unexpected enum storage type %s", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_type_tag_to_string (storage_type)); + break; + } + break; + } + break; + case GI_INFO_TYPE_VFUNC: + case GI_INFO_TYPE_CALLBACK: + g_warning("Field%s: Interface type %d should have is_pointer set", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_base_info_get_type (interface)); + break; + case GI_INFO_TYPE_INVALID: + case GI_INFO_TYPE_INTERFACE: + case GI_INFO_TYPE_FUNCTION: + case GI_INFO_TYPE_CONSTANT: + case GI_INFO_TYPE_INVALID_0: + case GI_INFO_TYPE_VALUE: + case GI_INFO_TYPE_SIGNAL: + case GI_INFO_TYPE_PROPERTY: + case GI_INFO_TYPE_FIELD: + case GI_INFO_TYPE_ARG: + case GI_INFO_TYPE_TYPE: + case GI_INFO_TYPE_UNRESOLVED: + g_warning("Field %s: Interface type %d not expected", + g_base_info_get_name ((GIBaseInfo *)field_info), + g_base_info_get_type (interface)); + break; + default: + break; + } + + g_base_info_unref ((GIBaseInfo *)interface); + break; + } + break; + default: + break; + } + } else { + switch (g_type_info_get_tag (type_info)) + { + case GI_TYPE_TAG_INTERFACE: + { + GIBaseInfo *interface = g_type_info_get_interface (type_info); + switch (g_base_info_get_type (interface)) + { + case GI_INFO_TYPE_OBJECT: + case GI_INFO_TYPE_INTERFACE: + G_STRUCT_MEMBER (gpointer, mem, offset) = (gpointer)value->v_pointer; + result = TRUE; + break; + default: + break; + } + g_base_info_unref ((GIBaseInfo *)interface); + } + default: + break; + } + } + + g_base_info_unref ((GIBaseInfo *)type_info); + + return result; +} diff --git a/girepository/gifieldinfo.h b/girepository/gifieldinfo.h new file mode 100644 index 000000000..1b95b719b --- /dev/null +++ b/girepository/gifieldinfo.h @@ -0,0 +1,71 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Field and Field values + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIFIELDINFO_H__ +#define __GIFIELDINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_FIELD_INFO + * @info: an info structure + * + * Checks if @info is a #GIFieldInfo. + * + */ +#define GI_IS_FIELD_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_FIELD) + + +GI_AVAILABLE_IN_ALL +GIFieldInfoFlags g_field_info_get_flags (GIFieldInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_field_info_get_size (GIFieldInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_field_info_get_offset (GIFieldInfo *info); + +GI_AVAILABLE_IN_ALL +GITypeInfo * g_field_info_get_type (GIFieldInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_field_info_get_field (GIFieldInfo *field_info, + gpointer mem, + GIArgument *value); + +GI_AVAILABLE_IN_ALL +gboolean g_field_info_set_field (GIFieldInfo *field_info, + gpointer mem, + const GIArgument *value); + +G_END_DECLS + + +#endif /* __GIFIELDINFO_H__ */ + diff --git a/girepository/gifunctioninfo.c b/girepository/gifunctioninfo.c new file mode 100644 index 000000000..8b38bd5e0 --- /dev/null +++ b/girepository/gifunctioninfo.c @@ -0,0 +1,294 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Function implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" + +/** + * SECTION:gifunctioninfo + * @title: GIFunctionInfo + * @short_description: Struct representing a function + * + * GIFunctionInfo represents a function, method or constructor. + * + * To find out what kind of entity a #GIFunctionInfo represents, call + * g_function_info_get_flags(). + * + * See also #GICallableInfo for information on how to retreive arguments and + * other metadata. + */ + +GIFunctionInfo * +_g_base_info_find_method (GIBaseInfo *base, + guint32 offset, + gint n_methods, + const gchar *name) +{ + /* FIXME hash */ + GIRealInfo *rinfo = (GIRealInfo*)base; + Header *header = (Header *)rinfo->typelib->data; + gint i; + + for (i = 0; i < n_methods; i++) + { + FunctionBlob *fblob = (FunctionBlob *)&rinfo->typelib->data[offset]; + const gchar *fname = (const gchar *)&rinfo->typelib->data[fblob->name]; + + if (strcmp (name, fname) == 0) + return (GIFunctionInfo *) g_info_new (GI_INFO_TYPE_FUNCTION, base, + rinfo->typelib, offset); + + offset += header->function_blob_size; + } + + return NULL; +} + +/** + * g_function_info_get_symbol: + * @info: a #GIFunctionInfo + * + * Obtain the symbol of the function. The symbol is the name of the + * exported function, suitable to be used as an argument to + * g_module_symbol(). + * + * Returns: the symbol + */ +const gchar * +g_function_info_get_symbol (GIFunctionInfo *info) +{ + GIRealInfo *rinfo; + FunctionBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_FUNCTION_INFO (info), NULL); + + rinfo = (GIRealInfo *)info; + blob = (FunctionBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->symbol); +} + +/** + * g_function_info_get_flags: + * @info: a #GIFunctionInfo + * + * Obtain the #GIFunctionInfoFlags for the @info. + * + * Returns: the flags + */ +GIFunctionInfoFlags +g_function_info_get_flags (GIFunctionInfo *info) +{ + GIFunctionInfoFlags flags; + GIRealInfo *rinfo; + FunctionBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_FUNCTION_INFO (info), -1); + + rinfo = (GIRealInfo *)info; + blob = (FunctionBlob *)&rinfo->typelib->data[rinfo->offset]; + + flags = 0; + + /* Make sure we don't flag Constructors as methods */ + if (!blob->constructor && !blob->is_static) + flags = flags | GI_FUNCTION_IS_METHOD; + + if (blob->constructor) + flags = flags | GI_FUNCTION_IS_CONSTRUCTOR; + + if (blob->getter) + flags = flags | GI_FUNCTION_IS_GETTER; + + if (blob->setter) + flags = flags | GI_FUNCTION_IS_SETTER; + + if (blob->wraps_vfunc) + flags = flags | GI_FUNCTION_WRAPS_VFUNC; + + if (blob->throws) + flags = flags | GI_FUNCTION_THROWS; + + return flags; +} + +/** + * g_function_info_get_property: + * @info: a #GIFunctionInfo + * + * Obtain the property associated with this #GIFunctionInfo. + * Only #GIFunctionInfo with the flag %GI_FUNCTION_IS_GETTER or + * %GI_FUNCTION_IS_SETTER have a property set. For other cases, + * %NULL will be returned. + * + * Returns: (transfer full): the property or %NULL if not set. Free it with + * g_base_info_unref() when done. + */ +GIPropertyInfo * +g_function_info_get_property (GIFunctionInfo *info) +{ + GIRealInfo *rinfo, *container_rinfo; + FunctionBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_FUNCTION_INFO (info), NULL); + + rinfo = (GIRealInfo *)info; + blob = (FunctionBlob *)&rinfo->typelib->data[rinfo->offset]; + container_rinfo = (GIRealInfo *)rinfo->container; + + if (container_rinfo->type == GI_INFO_TYPE_INTERFACE) + { + GIInterfaceInfo *container = (GIInterfaceInfo *)rinfo->container; + + return g_interface_info_get_property (container, blob->index); + } + else if (container_rinfo->type == GI_INFO_TYPE_OBJECT) + { + GIObjectInfo *container = (GIObjectInfo *)rinfo->container; + + return g_object_info_get_property (container, blob->index); + } + else + return NULL; +} + +/** + * g_function_info_get_vfunc: + * @info: a #GIFunctionInfo + * + * Obtain the virtual function associated with this #GIFunctionInfo. + * Only #GIFunctionInfo with the flag %GI_FUNCTION_WRAPS_VFUNC has + * a virtual function set. For other cases, %NULL will be returned. + * + * Returns: (transfer full): the virtual function or %NULL if not set. + * Free it by calling g_base_info_unref() when done. + */ +GIVFuncInfo * +g_function_info_get_vfunc (GIFunctionInfo *info) +{ + GIRealInfo *rinfo; + FunctionBlob *blob; + GIInterfaceInfo *container; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_FUNCTION_INFO (info), NULL); + + rinfo = (GIRealInfo *)info; + blob = (FunctionBlob *)&rinfo->typelib->data[rinfo->offset]; + container = (GIInterfaceInfo *)rinfo->container; + + return g_interface_info_get_vfunc (container, blob->index); +} + +/** + * g_invoke_error_quark: + * + * TODO + * + * Returns: TODO + */ +GQuark +g_invoke_error_quark (void) +{ + static GQuark quark = 0; + if (quark == 0) + quark = g_quark_from_static_string ("g-invoke-error-quark"); + return quark; +} + +/** + * g_function_info_invoke: (skip) + * @info: a #GIFunctionInfo describing the function to invoke + * @in_args: (array length=n_in_args): an array of #GIArguments, one for each in + * parameter of @info. If there are no in parameter, @in_args + * can be %NULL + * @n_in_args: the length of the @in_args array + * @out_args: (array length=n_out_args): an array of #GIArguments, one for each out + * parameter of @info. If there are no out parameters, @out_args + * may be %NULL + * @n_out_args: the length of the @out_args array + * @return_value: return location for the return value of the + * function. + * @error: return location for detailed error information, or %NULL + * + * Invokes the function described in @info with the given + * arguments. Note that inout parameters must appear in both + * argument lists. This function uses dlsym() to obtain a pointer + * to the function, so the library or shared object containing the + * described function must either be linked to the caller, or must + * have been g_module_symbol()ed before calling this function. + * + * Returns: %TRUE if the function has been invoked, %FALSE if an + * error occurred. + */ +gboolean +g_function_info_invoke (GIFunctionInfo *info, + const GIArgument *in_args, + int n_in_args, + const GIArgument *out_args, + int n_out_args, + GIArgument *return_value, + GError **error) +{ + const gchar *symbol; + gpointer func; + gboolean is_method; + gboolean throws; + + symbol = g_function_info_get_symbol (info); + + if (!g_typelib_symbol (g_base_info_get_typelib((GIBaseInfo *) info), + symbol, &func)) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_SYMBOL_NOT_FOUND, + "Could not locate %s: %s", symbol, g_module_error ()); + + return FALSE; + } + + is_method = (g_function_info_get_flags (info) & GI_FUNCTION_IS_METHOD) != 0 + && (g_function_info_get_flags (info) & GI_FUNCTION_IS_CONSTRUCTOR) == 0; + throws = g_function_info_get_flags (info) & GI_FUNCTION_THROWS; + + return g_callable_info_invoke ((GICallableInfo*) info, + func, + in_args, + n_in_args, + out_args, + n_out_args, + return_value, + is_method, + throws, + error); +} diff --git a/girepository/gifunctioninfo.h b/girepository/gifunctioninfo.h new file mode 100644 index 000000000..e82eec69a --- /dev/null +++ b/girepository/gifunctioninfo.h @@ -0,0 +1,100 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Function + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIFUNCTIONINFO_H__ +#define __GIFUNCTIONINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_FUNCTION_INFO + * @info: an info structure + * + * Checks if @info is a #GIFunctionInfo. + */ +#define GI_IS_FUNCTION_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_FUNCTION) + + +GI_AVAILABLE_IN_ALL +const gchar * g_function_info_get_symbol (GIFunctionInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfoFlags g_function_info_get_flags (GIFunctionInfo *info); + +GI_AVAILABLE_IN_ALL +GIPropertyInfo * g_function_info_get_property (GIFunctionInfo *info); + +GI_AVAILABLE_IN_ALL +GIVFuncInfo * g_function_info_get_vfunc (GIFunctionInfo *info); + +/** + * G_INVOKE_ERROR: + * + * TODO + */ +#define G_INVOKE_ERROR (g_invoke_error_quark ()) + +GI_AVAILABLE_IN_ALL +GQuark g_invoke_error_quark (void); + +/** + * GInvokeError: + * @G_INVOKE_ERROR_FAILED: invokation failed, unknown error. + * @G_INVOKE_ERROR_SYMBOL_NOT_FOUND: symbol couldn't be found in any of the + * libraries associated with the typelib of the function. + * @G_INVOKE_ERROR_ARGUMENT_MISMATCH: the arguments provided didn't match + * the expected arguments for the functions type signature. + * + * An error occuring while invoking a function via + * g_function_info_invoke(). + */ + +typedef enum +{ + G_INVOKE_ERROR_FAILED, + G_INVOKE_ERROR_SYMBOL_NOT_FOUND, + G_INVOKE_ERROR_ARGUMENT_MISMATCH +} GInvokeError; + + +GI_AVAILABLE_IN_ALL +gboolean g_function_info_invoke (GIFunctionInfo *info, + const GIArgument *in_args, + int n_in_args, + const GIArgument *out_args, + int n_out_args, + GIArgument *return_value, + GError **error); + + +G_END_DECLS + + +#endif /* __GIFUNCTIONINFO_H__ */ + diff --git a/girepository/giinterfaceinfo.c b/girepository/giinterfaceinfo.c new file mode 100644 index 000000000..f0d1d7af0 --- /dev/null +++ b/girepository/giinterfaceinfo.c @@ -0,0 +1,500 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Interface implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" + +/** + * SECTION:giinterfaceinfo + * @title: GIInterfaceInfo + * @short_description: Struct representing a GInterface + * + * GIInterfaceInfo represents a #GInterface type. + * + * A GInterface has methods, fields, properties, signals, interfaces, constants, + * virtual functions and prerequisites. + */ + +/** + * g_interface_info_get_n_prerequisites: + * @info: a #GIInterfaceInfo + * + * Obtain the number of prerequisites for this interface type. + * A prerequisites is another interface that needs to be implemented for + * interface, similar to an base class for GObjects. + * + * Returns: number of prerequisites + */ +gint +g_interface_info_get_n_prerequisites (GIInterfaceInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), 0); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_prerequisites; +} + +/** + * g_interface_info_get_prerequisite: + * @info: a #GIInterfaceInfo + * @n: index of prerequisites to get + * + * Obtain an interface type prerequisites index @n. + * + * Returns: (transfer full): the prerequisites as a #GIBaseInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIBaseInfo * +g_interface_info_get_prerequisite (GIInterfaceInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + return _g_info_from_entry (rinfo->repository, + rinfo->typelib, blob->prerequisites[n]); +} + + +/** + * g_interface_info_get_n_properties: + * @info: a #GIInterfaceInfo + * + * Obtain the number of properties that this interface type has. + * + * Returns: number of properties + */ +gint +g_interface_info_get_n_properties (GIInterfaceInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), 0); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_properties; +} + +/** + * g_interface_info_get_property: + * @info: a #GIInterfaceInfo + * @n: index of property to get + * + * Obtain an interface type property at index @n. + * + * Returns: (transfer full): the #GIPropertyInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIPropertyInfo * +g_interface_info_get_property (GIInterfaceInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->interface_blob_size + + (blob->n_prerequisites + (blob->n_prerequisites % 2)) * 2 + + n * header->property_blob_size; + + return (GIPropertyInfo *) g_info_new (GI_INFO_TYPE_PROPERTY, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_interface_info_get_n_methods: + * @info: a #GIInterfaceInfo + * + * Obtain the number of methods that this interface type has. + * + * Returns: number of methods + */ +gint +g_interface_info_get_n_methods (GIInterfaceInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), 0); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_methods; +} + +/** + * g_interface_info_get_method: + * @info: a #GIInterfaceInfo + * @n: index of method to get + * + * Obtain an interface type method at index @n. + * + * Returns: (transfer full): the #GIFunctionInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIFunctionInfo * +g_interface_info_get_method (GIInterfaceInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->interface_blob_size + + (blob->n_prerequisites + (blob->n_prerequisites % 2)) * 2 + + blob->n_properties * header->property_blob_size + + n * header->function_blob_size; + + return (GIFunctionInfo *) g_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_interface_info_find_method: + * @info: a #GIInterfaceInfo + * @name: name of method to obtain + * + * Obtain a method of the interface type given a @name. %NULL will be + * returned if there's no method available with that name. + * + * Returns: (transfer full): the #GIFunctionInfo or %NULL if none found. + * Free the struct by calling g_base_info_unref() when done. + */ +GIFunctionInfo * +g_interface_info_find_method (GIInterfaceInfo *info, + const gchar *name) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + InterfaceBlob *blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->interface_blob_size + + (blob->n_prerequisites + (blob->n_prerequisites % 2)) * 2 + + blob->n_properties * header->property_blob_size; + + return _g_base_info_find_method ((GIBaseInfo*)info, offset, blob->n_methods, name); +} + +/** + * g_interface_info_get_n_signals: + * @info: a #GIInterfaceInfo + * + * Obtain the number of signals that this interface type has. + * + * Returns: number of signals + */ +gint +g_interface_info_get_n_signals (GIInterfaceInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), 0); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_signals; +} + +/** + * g_interface_info_get_signal: + * @info: a #GIInterfaceInfo + * @n: index of signal to get + * + * Obtain an interface type signal at index @n. + * + * Returns: (transfer full): the #GISignalInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GISignalInfo * +g_interface_info_get_signal (GIInterfaceInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->interface_blob_size + + (blob->n_prerequisites + (blob->n_prerequisites % 2)) * 2 + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + n * header->signal_blob_size; + + return (GISignalInfo *) g_info_new (GI_INFO_TYPE_SIGNAL, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_interface_info_find_signal: + * @info: a #GIInterfaceInfo + * @name: Name of signal + * + * TODO + * + * Returns: (transfer full): Info for the signal with name @name in @info, or + * %NULL on failure. + * Since: 1.34 + */ +GISignalInfo * +g_interface_info_find_signal (GIInterfaceInfo *info, + const gchar *name) +{ + gint n_signals; + gint i; + + n_signals = g_interface_info_get_n_signals (info); + for (i = 0; i < n_signals; i++) + { + GISignalInfo *siginfo = g_interface_info_get_signal (info, i); + + if (g_strcmp0 (g_base_info_get_name (siginfo), name) != 0) + { + g_base_info_unref ((GIBaseInfo*)siginfo); + continue; + } + + return siginfo; + } + return NULL; +} + +/** + * g_interface_info_get_n_vfuncs: + * @info: a #GIInterfaceInfo + * + * Obtain the number of virtual functions that this interface type has. + * + * Returns: number of virtual functions + */ +gint +g_interface_info_get_n_vfuncs (GIInterfaceInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), 0); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_vfuncs; +} + +/** + * g_interface_info_get_vfunc: + * @info: a #GIInterfaceInfo + * @n: index of virtual function to get + * + * Obtain an interface type virtual function at index @n. + * + * Returns: (transfer full): the #GIVFuncInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIVFuncInfo * +g_interface_info_get_vfunc (GIInterfaceInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->interface_blob_size + + (blob->n_prerequisites + (blob->n_prerequisites % 2)) * 2 + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + blob->n_signals * header->signal_blob_size + + n * header->vfunc_blob_size; + + return (GIVFuncInfo *) g_info_new (GI_INFO_TYPE_VFUNC, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_interface_info_find_vfunc: + * @info: a #GIInterfaceInfo + * @name: The name of a virtual function to find. + * + * Locate a virtual function slot with name @name. See the documentation + * for g_object_info_find_vfunc() for more information on virtuals. + * + * Returns: (transfer full): the #GIVFuncInfo, or %NULL. Free it with + * g_base_info_unref() when done. + */ +GIVFuncInfo * +g_interface_info_find_vfunc (GIInterfaceInfo *info, + const gchar *name) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->interface_blob_size + + (blob->n_prerequisites + blob->n_prerequisites % 2) * 2 + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + blob->n_signals * header->signal_blob_size; + + return _g_base_info_find_vfunc (rinfo, offset, blob->n_vfuncs, name); +} + +/** + * g_interface_info_get_n_constants: + * @info: a #GIInterfaceInfo + * + * Obtain the number of constants that this interface type has. + * + * Returns: number of constants + */ +gint +g_interface_info_get_n_constants (GIInterfaceInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), 0); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_constants; +} + +/** + * g_interface_info_get_constant: + * @info: a #GIInterfaceInfo + * @n: index of constant to get + * + * Obtain an interface type constant at index @n. + * + * Returns: (transfer full): the #GIConstantInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIConstantInfo * +g_interface_info_get_constant (GIInterfaceInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->interface_blob_size + + (blob->n_prerequisites + (blob->n_prerequisites % 2)) * 2 + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + blob->n_signals * header->signal_blob_size + + blob->n_vfuncs * header->vfunc_blob_size + + n * header->constant_blob_size; + + return (GIConstantInfo *) g_info_new (GI_INFO_TYPE_CONSTANT, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_interface_info_get_iface_struct: + * @info: a #GIInterfaceInfo + * + * Returns the layout C structure associated with this #GInterface. + * + * Returns: (transfer full): the #GIStructInfo or %NULL. Free it with + * g_base_info_unref() when done. + */ +GIStructInfo * +g_interface_info_get_iface_struct (GIInterfaceInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + InterfaceBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_INTERFACE_INFO (info), NULL); + + blob = (InterfaceBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->gtype_struct) + return (GIStructInfo *) _g_info_from_entry (rinfo->repository, + rinfo->typelib, blob->gtype_struct); + else + return NULL; +} + diff --git a/girepository/giinterfaceinfo.h b/girepository/giinterfaceinfo.h new file mode 100644 index 000000000..c8cb815c7 --- /dev/null +++ b/girepository/giinterfaceinfo.h @@ -0,0 +1,106 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Interface + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIINTERFACEINFO_H__ +#define __GIINTERFACEINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_INTERFACE_INFO + * @info: an info structure + * + * Checks if @info is a #GIInterfaceInfo. + */ +#define GI_IS_INTERFACE_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_INTERFACE) + + +GI_AVAILABLE_IN_ALL +gint g_interface_info_get_n_prerequisites (GIInterfaceInfo *info); + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_interface_info_get_prerequisite (GIInterfaceInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gint g_interface_info_get_n_properties (GIInterfaceInfo *info); + +GI_AVAILABLE_IN_ALL +GIPropertyInfo * g_interface_info_get_property (GIInterfaceInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gint g_interface_info_get_n_methods (GIInterfaceInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_interface_info_get_method (GIInterfaceInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_interface_info_find_method (GIInterfaceInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gint g_interface_info_get_n_signals (GIInterfaceInfo *info); + +GI_AVAILABLE_IN_ALL +GISignalInfo * g_interface_info_get_signal (GIInterfaceInfo *info, + gint n); + +GI_AVAILABLE_IN_1_34 +GISignalInfo * g_interface_info_find_signal (GIInterfaceInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gint g_interface_info_get_n_vfuncs (GIInterfaceInfo *info); + +GI_AVAILABLE_IN_ALL +GIVFuncInfo * g_interface_info_get_vfunc (GIInterfaceInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIVFuncInfo * g_interface_info_find_vfunc (GIInterfaceInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gint g_interface_info_get_n_constants (GIInterfaceInfo *info); + +GI_AVAILABLE_IN_ALL +GIConstantInfo * g_interface_info_get_constant (GIInterfaceInfo *info, + gint n); + + +GI_AVAILABLE_IN_ALL +GIStructInfo * g_interface_info_get_iface_struct (GIInterfaceInfo *info); + +G_END_DECLS + + +#endif /* __GIINTERFACEINFO_H__ */ + diff --git a/girepository/ginvoke.c b/girepository/ginvoke.c new file mode 100644 index 000000000..5d423b1e2 --- /dev/null +++ b/girepository/ginvoke.c @@ -0,0 +1,311 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Invoke functionality + * + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include +#include + +#include +#include "girffi.h" + +/** + * value_to_ffi_type: + * @gvalue: TODO + * @value: TODO + * + * TODO + */ +static ffi_type * +value_to_ffi_type (const GValue *gvalue, gpointer *value) +{ + ffi_type *rettype = NULL; + GType type = g_type_fundamental (G_VALUE_TYPE (gvalue)); + g_assert (type != G_TYPE_INVALID); + + switch (type) + { + case G_TYPE_BOOLEAN: + case G_TYPE_CHAR: + case G_TYPE_INT: + rettype = &ffi_type_sint; + *value = (gpointer)&(gvalue->data[0].v_int); + break; + case G_TYPE_UCHAR: + case G_TYPE_UINT: + rettype = &ffi_type_uint; + *value = (gpointer)&(gvalue->data[0].v_uint); + break; + case G_TYPE_STRING: + case G_TYPE_OBJECT: + case G_TYPE_BOXED: + case G_TYPE_POINTER: + case G_TYPE_PARAM: + rettype = &ffi_type_pointer; + *value = (gpointer)&(gvalue->data[0].v_pointer); + break; + case G_TYPE_FLOAT: + rettype = &ffi_type_float; + *value = (gpointer)&(gvalue->data[0].v_float); + break; + case G_TYPE_DOUBLE: + rettype = &ffi_type_double; + *value = (gpointer)&(gvalue->data[0].v_double); + break; + case G_TYPE_LONG: + rettype = &ffi_type_slong; + *value = (gpointer)&(gvalue->data[0].v_long); + break; + case G_TYPE_ULONG: + rettype = &ffi_type_ulong; + *value = (gpointer)&(gvalue->data[0].v_ulong); + break; + case G_TYPE_INT64: + rettype = &ffi_type_sint64; + *value = (gpointer)&(gvalue->data[0].v_int64); + break; + case G_TYPE_UINT64: + rettype = &ffi_type_uint64; + *value = (gpointer)&(gvalue->data[0].v_uint64); + break; + default: + rettype = &ffi_type_pointer; + *value = NULL; + g_warning ("Unsupported fundamental type: %s", g_type_name (type)); + break; + } + return rettype; +} + +/** + * g_value_to_ffi_return_type: + * @gvalue: TODO + * @ffi_value: TODO + * @value: TODO + * + * TODO + */ +static ffi_type * +g_value_to_ffi_return_type (const GValue *gvalue, + const GIArgument *ffi_value, + gpointer *value) +{ + ffi_type *rettype = NULL; + GType type = g_type_fundamental (G_VALUE_TYPE (gvalue)); + g_assert (type != G_TYPE_INVALID); + + *value = (gpointer)&(ffi_value->v_long); + + switch (type) { + case G_TYPE_CHAR: + rettype = &ffi_type_sint8; + break; + case G_TYPE_UCHAR: + rettype = &ffi_type_uint8; + break; + case G_TYPE_BOOLEAN: + case G_TYPE_INT: + rettype = &ffi_type_sint; + break; + case G_TYPE_UINT: + rettype = &ffi_type_uint; + break; + case G_TYPE_STRING: + case G_TYPE_OBJECT: + case G_TYPE_BOXED: + case G_TYPE_POINTER: + case G_TYPE_PARAM: + rettype = &ffi_type_pointer; + break; + case G_TYPE_FLOAT: + rettype = &ffi_type_float; + *value = (gpointer)&(ffi_value->v_float); + break; + case G_TYPE_DOUBLE: + rettype = &ffi_type_double; + *value = (gpointer)&(ffi_value->v_double); + break; + case G_TYPE_LONG: + rettype = &ffi_type_slong; + break; + case G_TYPE_ULONG: + rettype = &ffi_type_ulong; + break; + case G_TYPE_INT64: + rettype = &ffi_type_sint64; + *value = (gpointer)&(ffi_value->v_int64); + break; + case G_TYPE_UINT64: + rettype = &ffi_type_uint64; + *value = (gpointer)&(ffi_value->v_uint64); + break; + default: + rettype = &ffi_type_pointer; + *value = NULL; + g_warning ("Unsupported fundamental type: %s", g_type_name (type)); + break; + } + return rettype; +} + +/** + * g_value_from_ffi_value: + * @gvalue: TODO + * @value: TODO + * + * TODO + */ +static void +g_value_from_ffi_value (GValue *gvalue, + const GIArgument *value) +{ + switch (g_type_fundamental (G_VALUE_TYPE (gvalue))) { + case G_TYPE_INT: + g_value_set_int (gvalue, (gint)value->v_long); + break; + case G_TYPE_FLOAT: + g_value_set_float (gvalue, (gfloat)value->v_float); + break; + case G_TYPE_DOUBLE: + g_value_set_double (gvalue, (gdouble)value->v_double); + break; + case G_TYPE_BOOLEAN: + g_value_set_boolean (gvalue, (gboolean)value->v_long); + break; + case G_TYPE_STRING: + g_value_set_string (gvalue, (gchar*)value->v_pointer); + break; + case G_TYPE_CHAR: + g_value_set_schar (gvalue, (gchar)value->v_long); + break; + case G_TYPE_UCHAR: + g_value_set_uchar (gvalue, (guchar)value->v_ulong); + break; + case G_TYPE_UINT: + g_value_set_uint (gvalue, (guint)value->v_ulong); + break; + case G_TYPE_POINTER: + g_value_set_pointer (gvalue, (gpointer)value->v_pointer); + break; + case G_TYPE_LONG: + g_value_set_long (gvalue, (glong)value->v_long); + break; + case G_TYPE_ULONG: + g_value_set_ulong (gvalue, (gulong)value->v_ulong); + break; + case G_TYPE_INT64: + g_value_set_int64 (gvalue, (gint64)value->v_int64); + break; + case G_TYPE_UINT64: + g_value_set_uint64 (gvalue, (guint64)value->v_uint64); + break; + case G_TYPE_BOXED: + g_value_set_boxed (gvalue, (gpointer)value->v_pointer); + break; + case G_TYPE_PARAM: + g_value_set_param (gvalue, (gpointer)value->v_pointer); + break; + default: + g_warning ("Unsupported fundamental type: %s", + g_type_name (g_type_fundamental (G_VALUE_TYPE (gvalue)))); + } + +} + +/** + * gi_cclosure_marshal_generic: (skip) + * @closure: TODO + * @return_gvalue: TODO + * @n_param_values: TODO + * @param_values: TODO + * @invocation_hint: TODO + * @marshal_data: TODO + * + * TODO + */ +void +gi_cclosure_marshal_generic (GClosure *closure, + GValue *return_gvalue, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + GIArgument return_ffi_value = { 0, }; + ffi_type *rtype; + void *rvalue; + int n_args; + ffi_type **atypes; + void **args; + int i; + ffi_cif cif; + GCClosure *cc = (GCClosure*) closure; + + if (return_gvalue && G_VALUE_TYPE (return_gvalue)) + { + rtype = g_value_to_ffi_return_type (return_gvalue, &return_ffi_value, + &rvalue); + } + else + { + rtype = &ffi_type_void; + rvalue = &return_ffi_value.v_long; + } + + n_args = n_param_values + 1; + atypes = g_alloca (sizeof (ffi_type *) * n_args); + args = g_alloca (sizeof (gpointer) * n_args); + + if (n_param_values > 0) + { + if (G_CCLOSURE_SWAP_DATA (closure)) + { + atypes[n_args-1] = value_to_ffi_type (param_values + 0, + &args[n_args-1]); + atypes[0] = &ffi_type_pointer; + args[0] = &closure->data; + } + else + { + atypes[0] = value_to_ffi_type (param_values + 0, &args[0]); + atypes[n_args-1] = &ffi_type_pointer; + args[n_args-1] = &closure->data; + } + } + else + { + atypes[0] = &ffi_type_pointer; + args[0] = &closure->data; + } + + for (i = 1; i < n_args - 1; i++) + atypes[i] = value_to_ffi_type (param_values + i, &args[i]); + + if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, n_args, rtype, atypes) != FFI_OK) + return; + + ffi_call (&cif, marshal_data ? marshal_data : cc->callback, rvalue, args); + + if (return_gvalue && G_VALUE_TYPE (return_gvalue)) + g_value_from_ffi_value (return_gvalue, &return_ffi_value); +} diff --git a/girepository/giobjectinfo.c b/girepository/giobjectinfo.c new file mode 100644 index 000000000..82199b843 --- /dev/null +++ b/girepository/giobjectinfo.c @@ -0,0 +1,1096 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Object implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" + +/** + * SECTION:giobjectinfo + * @title: GIObjectInfo + * @short_description: Struct representing a classed type + * + * GIObjectInfo represents a classed type. + * + * Classed types in GType inherit from #GTypeInstance; the most common + * type is #GObject. + * + * A GIObjectInfo doesn't represent a specific instance of a classed type, + * instead this represent the object type (eg class). + * + * A GIObjectInfo has methods, fields, properties, signals, interfaces, + * constants and virtual functions. + */ + +/** + * g_object_info_get_field_offset: + * @info: a #GIObjectInfo + * @n: index of queried field + * + * Obtain the offset of the specified field. + * + * Returns: field offset in bytes + */ +static gint32 +g_object_info_get_field_offset (GIObjectInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + ObjectBlob *blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + guint32 offset; + gint i; + FieldBlob *field_blob; + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2; + + for (i = 0; i < n; i++) + { + field_blob = (FieldBlob *)&rinfo->typelib->data[offset]; + offset += header->field_blob_size; + if (field_blob->has_embedded_type) + offset += header->callback_blob_size; + } + + return offset; +} + +/** + * g_object_info_get_parent: + * @info: a #GIObjectInfo + * + * Obtain the parent of the object type. + * + * Returns: (transfer full) (nullable): the #GIObjectInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIObjectInfo * +g_object_info_get_parent (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->parent) + return (GIObjectInfo *) _g_info_from_entry (rinfo->repository, + rinfo->typelib, blob->parent); + else + return NULL; +} + +/** + * g_object_info_get_abstract: + * @info: a #GIObjectInfo + * + * Obtain if the object type is an abstract type, eg if it cannot be + * instantiated + * + * Returns: %TRUE if the object type is abstract + */ +gboolean +g_object_info_get_abstract (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), FALSE); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->abstract != 0; +} + +/** + * g_object_info_get_final: + * @info: a #GIObjectInfo + * + * Checks whether the object type is a final type, i.e. if it cannot + * be derived + * + * Returns: %TRUE if the object type is final + * + * Since: 1.70 + */ +gboolean +g_object_info_get_final (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *) info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), FALSE); + + blob = (ObjectBlob *) &rinfo->typelib->data[rinfo->offset]; + + return blob->final_ != 0; +} + +/** + * g_object_info_get_fundamental: + * @info: a #GIObjectInfo + * + * Obtain if the object type is of a fundamental type which is not + * G_TYPE_OBJECT. This is mostly for supporting GstMiniObject. + * + * Returns: %TRUE if the object type is a fundamental type + */ +gboolean +g_object_info_get_fundamental (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), FALSE); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->fundamental != 0; +} + +/** + * g_object_info_get_type_name: + * @info: a #GIObjectInfo + * + * Obtain the name of the objects class/type. + * + * Returns: name of the objects type + */ +const gchar * +g_object_info_get_type_name (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->gtype_name); +} + +/** + * g_object_info_get_type_init: + * @info: a #GIObjectInfo + * + * Obtain the function which when called will return the GType + * function for which this object type is registered. + * + * Returns: the type init function + */ +const gchar * +g_object_info_get_type_init (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return g_typelib_get_string (rinfo->typelib, blob->gtype_init); +} + +/** + * g_object_info_get_n_interfaces: + * @info: a #GIObjectInfo + * + * Obtain the number of interfaces that this object type has. + * + * Returns: number of interfaces + */ +gint +g_object_info_get_n_interfaces (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_interfaces; +} + +/** + * g_object_info_get_interface: + * @info: a #GIObjectInfo + * @n: index of interface to get + * + * Obtain an object type interface at index @n. + * + * Returns: (transfer full): the #GIInterfaceInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIInterfaceInfo * +g_object_info_get_interface (GIObjectInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return (GIInterfaceInfo *) _g_info_from_entry (rinfo->repository, + rinfo->typelib, blob->interfaces[n]); +} + +/** + * g_object_info_get_n_fields: + * @info: a #GIObjectInfo + * + * Obtain the number of fields that this object type has. + * + * Returns: number of fields + */ +gint +g_object_info_get_n_fields (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_fields; +} + +/** + * g_object_info_get_field: + * @info: a #GIObjectInfo + * @n: index of field to get + * + * Obtain an object type field at index @n. + * + * Returns: (transfer full): the #GIFieldInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIFieldInfo * +g_object_info_get_field (GIObjectInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + offset = g_object_info_get_field_offset(info, n); + + return (GIFieldInfo *) g_info_new (GI_INFO_TYPE_FIELD, (GIBaseInfo*)info, rinfo->typelib, offset); +} + +/** + * g_object_info_get_n_properties: + * @info: a #GIObjectInfo + * + * Obtain the number of properties that this object type has. + * + * Returns: number of properties + */ +gint +g_object_info_get_n_properties (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + return blob->n_properties; +} + +/** + * g_object_info_get_property: + * @info: a #GIObjectInfo + * @n: index of property to get + * + * Obtain an object type property at index @n. + * + * Returns: (transfer full): the #GIPropertyInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIPropertyInfo * +g_object_info_get_property (GIObjectInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * header->field_blob_size + + blob->n_field_callbacks * header->callback_blob_size + + n * header->property_blob_size; + + return (GIPropertyInfo *) g_info_new (GI_INFO_TYPE_PROPERTY, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_object_info_get_n_methods: + * @info: a #GIObjectInfo + * + * Obtain the number of methods that this object type has. + * + * Returns: number of methods + */ +gint +g_object_info_get_n_methods (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_methods; +} + +/** + * g_object_info_get_method: + * @info: a #GIObjectInfo + * @n: index of method to get + * + * Obtain an object type method at index @n. + * + * Returns: (transfer full): the #GIFunctionInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIFunctionInfo * +g_object_info_get_method (GIObjectInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * header->field_blob_size + + blob->n_field_callbacks * header->callback_blob_size + + blob->n_properties * header->property_blob_size + + n * header->function_blob_size; + + return (GIFunctionInfo *) g_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_object_info_find_method: + * @info: a #GIObjectInfo + * @name: name of method to obtain + * + * Obtain a method of the object type given a @name. %NULL will be + * returned if there's no method available with that name. + * + * Returns: (transfer full) (nullable): the #GIFunctionInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIFunctionInfo * +g_object_info_find_method (GIObjectInfo *info, + const gchar *name) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + header = (Header *)rinfo->typelib->data; + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * header->field_blob_size + + + blob->n_field_callbacks * header->callback_blob_size + + blob->n_properties * header->property_blob_size; + + return _g_base_info_find_method ((GIBaseInfo*)info, offset, blob->n_methods, name); +} + +/** + * g_object_info_find_method_using_interfaces: + * @info: a #GIObjectInfo + * @name: name of method to obtain + * @implementor: (out) (transfer full): The implementor of the interface + * + * Obtain a method of the object given a @name, searching both the + * object @info and any interfaces it implements. %NULL will be + * returned if there's no method available with that name. + * + * Note that this function does *not* search parent classes; you will have + * to chain up if that's desired. + * + * Returns: (transfer full) (nullable): the #GIFunctionInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIFunctionInfo * +g_object_info_find_method_using_interfaces (GIObjectInfo *info, + const gchar *name, + GIObjectInfo **implementor) +{ + GIFunctionInfo *result = NULL; + GIObjectInfo *implementor_result = NULL; + + result = g_object_info_find_method (info, name); + if (result) + implementor_result = g_base_info_ref ((GIBaseInfo*) info); + + if (result == NULL) + { + int n_interfaces; + int i; + + n_interfaces = g_object_info_get_n_interfaces (info); + for (i = 0; i < n_interfaces; ++i) + { + GIInterfaceInfo *iface_info; + + iface_info = g_object_info_get_interface (info, i); + + result = g_interface_info_find_method (iface_info, name); + + if (result != NULL) + { + implementor_result = iface_info; + break; + } + g_base_info_unref ((GIBaseInfo*) iface_info); + } + } + if (implementor) + *implementor = implementor_result; + else if (implementor_result != NULL) + g_base_info_unref ((GIBaseInfo*) implementor_result); + return result; +} + +/** + * g_object_info_get_n_signals: + * @info: a #GIObjectInfo + * + * Obtain the number of signals that this object type has. + * + * Returns: number of signals + */ +gint +g_object_info_get_n_signals (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_signals; +} + +/** + * g_object_info_get_signal: + * @info: a #GIObjectInfo + * @n: index of signal to get + * + * Obtain an object type signal at index @n. + * + * Returns: (transfer full): the #GISignalInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GISignalInfo * +g_object_info_get_signal (GIObjectInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * header->field_blob_size + + blob->n_field_callbacks * header->callback_blob_size + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + n * header->signal_blob_size; + + return (GISignalInfo *) g_info_new (GI_INFO_TYPE_SIGNAL, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_object_info_find_signal: + * @info: a #GIObjectInfo + * @name: Name of signal + * + * TODO + * + * Returns: (transfer full) (nullable): Info for the signal with name @name in @info, or %NULL on failure. + */ +GISignalInfo * +g_object_info_find_signal (GIObjectInfo *info, + const gchar *name) +{ + gint n_signals; + gint i; + + n_signals = g_object_info_get_n_signals (info); + for (i = 0; i < n_signals; i++) + { + GISignalInfo *siginfo = g_object_info_get_signal (info, i); + + if (g_strcmp0 (g_base_info_get_name (siginfo), name) != 0) + { + g_base_info_unref ((GIBaseInfo*)siginfo); + continue; + } + + return siginfo; + } + return NULL; +} + + +/** + * g_object_info_get_n_vfuncs: + * @info: a #GIObjectInfo + * + * Obtain the number of virtual functions that this object type has. + * + * Returns: number of virtual functions + */ +gint +g_object_info_get_n_vfuncs (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_vfuncs; +} + +/** + * g_object_info_get_vfunc: + * @info: a #GIObjectInfo + * @n: index of virtual function to get + * + * Obtain an object type virtual function at index @n. + * + * Returns: (transfer full): the #GIVFuncInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIVFuncInfo * +g_object_info_get_vfunc (GIObjectInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * header->field_blob_size + + blob->n_field_callbacks * header->callback_blob_size + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + blob->n_signals * header->signal_blob_size + + n * header->vfunc_blob_size; + + return (GIVFuncInfo *) g_info_new (GI_INFO_TYPE_VFUNC, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_object_info_find_vfunc: + * @info: a #GIObjectInfo + * @name: The name of a virtual function to find. + * + * Locate a virtual function slot with name @name. Note that the namespace + * for virtuals is distinct from that of methods; there may or may not be + * a concrete method associated for a virtual. If there is one, it may + * be retrieved using g_vfunc_info_get_invoker(), otherwise %NULL will be + * returned. + * See the documentation for g_vfunc_info_get_invoker() for more + * information on invoking virtuals. + * + * Returns: (transfer full) (nullable): the #GIVFuncInfo, or %NULL. Free it with + * g_base_info_unref() when done. + */ +GIVFuncInfo * +g_object_info_find_vfunc (GIObjectInfo *info, + const gchar *name) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * header->field_blob_size + + blob->n_field_callbacks * header->callback_blob_size + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + blob->n_signals * header->signal_blob_size; + + return _g_base_info_find_vfunc (rinfo, offset, blob->n_vfuncs, name); +} + +/** + * g_object_info_find_vfunc_using_interfaces: + * @info: a #GIObjectInfo + * @name: name of vfunc to obtain + * @implementor: (out) (transfer full): The implementor of the interface + * + * Locate a virtual function slot with name @name, searching both the object + * @info and any interfaces it implements. Note that the namespace for + * virtuals is distinct from that of methods; there may or may not be a + * concrete method associated for a virtual. If there is one, it may be + * retrieved using g_vfunc_info_get_invoker(), otherwise %NULL will be + * returned. + * + * Note that this function does *not* search parent classes; you will have + * to chain up if that's desired. + * + * Returns: (transfer full) (nullable): the #GIVFuncInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIVFuncInfo * +g_object_info_find_vfunc_using_interfaces (GIObjectInfo *info, + const gchar *name, + GIObjectInfo **implementor) +{ + GIVFuncInfo *result = NULL; + GIObjectInfo *implementor_result = NULL; + + result = g_object_info_find_vfunc (info, name); + if (result) + implementor_result = g_base_info_ref ((GIBaseInfo*) info); + + if (result == NULL) + { + int n_interfaces; + int i; + + n_interfaces = g_object_info_get_n_interfaces (info); + for (i = 0; i < n_interfaces; ++i) + { + GIInterfaceInfo *iface_info; + + iface_info = g_object_info_get_interface (info, i); + + result = g_interface_info_find_vfunc (iface_info, name); + + if (result != NULL) + { + implementor_result = iface_info; + break; + } + g_base_info_unref ((GIBaseInfo*) iface_info); + } + } + if (implementor) + *implementor = implementor_result; + else if (implementor_result != NULL) + g_base_info_unref ((GIBaseInfo*) implementor_result); + return result; +} + +/** + * g_object_info_get_n_constants: + * @info: a #GIObjectInfo + * + * Obtain the number of constants that this object type has. + * + * Returns: number of constants + */ +gint +g_object_info_get_n_constants (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), 0); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_constants; +} + +/** + * g_object_info_get_constant: + * @info: a #GIObjectInfo + * @n: index of constant to get + * + * Obtain an object type constant at index @n. + * + * Returns: (transfer full): the #GIConstantInfo. Free the struct by calling + * g_base_info_unref() when done. + */ +GIConstantInfo * +g_object_info_get_constant (GIObjectInfo *info, + gint n) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + header = (Header *)rinfo->typelib->data; + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->object_blob_size + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * header->field_blob_size + + blob->n_field_callbacks * header->callback_blob_size + + blob->n_properties * header->property_blob_size + + blob->n_methods * header->function_blob_size + + blob->n_signals * header->signal_blob_size + + blob->n_vfuncs * header->vfunc_blob_size + + n * header->constant_blob_size; + + return (GIConstantInfo *) g_info_new (GI_INFO_TYPE_CONSTANT, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_object_info_get_class_struct: + * @info: a #GIObjectInfo + * + * Every #GObject has two structures; an instance structure and a class + * structure. This function returns the metadata for the class structure. + * + * Returns: (transfer full) (nullable): the #GIStructInfo or %NULL. Free with + * g_base_info_unref() when done. + */ +GIStructInfo * +g_object_info_get_class_struct (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->gtype_struct) + return (GIStructInfo *) _g_info_from_entry (rinfo->repository, + rinfo->typelib, blob->gtype_struct); + else + return NULL; +} + +typedef const char* (*SymbolGetter) (GIObjectInfo *info); + +static void * +_get_func(GIObjectInfo *info, + SymbolGetter getter) +{ + const char* symbol; + GSList *parents = NULL, *l; + GIObjectInfo *parent_info; + gpointer func = NULL; + + parent_info = g_base_info_ref (info); + while (parent_info != NULL) + { + parents = g_slist_prepend (parents, parent_info); + parent_info = g_object_info_get_parent (parent_info); + } + + for (l = parents; l; l = l->next) + { + parent_info = l->data; + symbol = getter (parent_info); + if (symbol == NULL) + continue; + + g_typelib_symbol (((GIRealInfo *)parent_info)->typelib, symbol, (gpointer*) &func); + if (func) + break; + } + + g_slist_free_full (parents, (GDestroyNotify) g_base_info_unref); + return func; +} + +/** + * g_object_info_get_ref_function: + * @info: a #GIObjectInfo + * + * Obtain the symbol name of the function that should be called to ref this + * object type. It's mainly used fundamental types. The type signature for + * the symbol is %GIObjectInfoRefFunction, to fetch the function pointer + * see g_object_info_get_ref_function(). + * + * Returns: (nullable): the symbol or %NULL + */ +const char * +g_object_info_get_ref_function (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->ref_func) + return g_typelib_get_string (rinfo->typelib, blob->ref_func); + + return NULL; +} + +/** + * g_object_info_get_ref_function_pointer: (skip) + * @info: a #GIObjectInfo + * + * Obtain a pointer to a function which can be used to + * increase the reference count an instance of this object type. + * This takes derivation into account and will reversely traverse + * the base classes of this type, starting at the top type. + * + * Returns: (nullable): the function pointer or %NULL + */ +GIObjectInfoRefFunction +g_object_info_get_ref_function_pointer (GIObjectInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + return (GIObjectInfoRefFunction)_get_func(info, (SymbolGetter)g_object_info_get_ref_function); +} + +/** + * g_object_info_get_unref_function: + * @info: a #GIObjectInfo + * + * Obtain the symbol name of the function that should be called to unref this + * object type. It's mainly used fundamental types. The type signature for + * the symbol is %GIObjectInfoUnrefFunction, to fetch the function pointer + * see g_object_info_get_unref_function(). + * + * Returns: (nullable): the symbol or %NULL + */ +const char * +g_object_info_get_unref_function (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->unref_func) + return g_typelib_get_string (rinfo->typelib, blob->unref_func); + + return NULL; +} + +/** + * g_object_info_get_unref_function_pointer: (skip) + * @info: a #GIObjectInfo + * + * Obtain a pointer to a function which can be used to + * decrease the reference count an instance of this object type. + * This takes derivation into account and will reversely traverse + * the base classes of this type, starting at the top type. + * + * Returns: (nullable): the function pointer or %NULL + */ +GIObjectInfoUnrefFunction +g_object_info_get_unref_function_pointer (GIObjectInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + return (GIObjectInfoUnrefFunction)_get_func(info, (SymbolGetter)g_object_info_get_unref_function); +} + +/** + * g_object_info_get_set_value_function: + * @info: a #GIObjectInfo + * + * Obtain the symbol name of the function that should be called to convert + * set a GValue giving an object instance pointer of this object type. + * I's mainly used fundamental types. The type signature for the symbol + * is %GIObjectInfoSetValueFunction, to fetch the function pointer + * see g_object_info_get_set_value_function(). + * + * Returns: (nullable): the symbol or %NULL + */ +const char * +g_object_info_get_set_value_function (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->set_value_func) + return g_typelib_get_string (rinfo->typelib, blob->set_value_func); + + return NULL; +} + +/** + * g_object_info_get_set_value_function_pointer: (skip) + * @info: a #GIObjectInfo + * + * Obtain a pointer to a function which can be used to + * set a GValue given an instance of this object type. + * This takes derivation into account and will reversely traverse + * the base classes of this type, starting at the top type. + * + * Returns: (nullable): the function pointer or %NULL + */ +GIObjectInfoSetValueFunction +g_object_info_get_set_value_function_pointer (GIObjectInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + return (GIObjectInfoSetValueFunction)_get_func(info, (SymbolGetter)g_object_info_get_set_value_function); +} + +/** + * g_object_info_get_get_value_function: + * @info: a #GIObjectInfo + * + * Obtain the symbol name of the function that should be called to convert + * an object instance pointer of this object type to a GValue. + * I's mainly used fundamental types. The type signature for the symbol + * is %GIObjectInfoGetValueFunction, to fetch the function pointer + * see g_object_info_get_get_value_function(). + * + * Returns: (nullable): the symbol or %NULL + */ +const char * +g_object_info_get_get_value_function (GIObjectInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + ObjectBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + blob = (ObjectBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->get_value_func) + return g_typelib_get_string (rinfo->typelib, blob->get_value_func); + + return NULL; +} + +/** + * g_object_info_get_get_value_function_pointer: (skip) + * @info: a #GIObjectInfo + * + * Obtain a pointer to a function which can be used to + * extract an instance of this object type out of a GValue. + * This takes derivation into account and will reversely traverse + * the base classes of this type, starting at the top type. + * + * Returns: (nullable): the function pointer or %NULL + */ +GIObjectInfoGetValueFunction +g_object_info_get_get_value_function_pointer (GIObjectInfo *info) +{ + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_OBJECT_INFO (info), NULL); + + return (GIObjectInfoGetValueFunction)_get_func(info, (SymbolGetter)g_object_info_get_get_value_function); +} diff --git a/girepository/giobjectinfo.h b/girepository/giobjectinfo.h new file mode 100644 index 000000000..aa6f9c9d0 --- /dev/null +++ b/girepository/giobjectinfo.h @@ -0,0 +1,210 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Object + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIOBJECTINFO_H__ +#define __GIOBJECTINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GIObjectInfoRefFunction: (skip) + * @object: object instance pointer + * + * Increases the reference count of an object instance. + * + * Returns: (transfer full): the object instance + */ +typedef void * (*GIObjectInfoRefFunction) (void *object); + +/** + * GIObjectInfoUnrefFunction: (skip) + * @object: object instance pointer + * + * Decreases the reference count of an object instance. + */ +typedef void (*GIObjectInfoUnrefFunction) (void *object); + +/** + * GIObjectInfoSetValueFunction: (skip) + * @value: a #GValue + * @object: object instance pointer + * + * Update @value and attach the object instance pointer @object to it. + */ +typedef void (*GIObjectInfoSetValueFunction) (GValue *value, void *object); + +/** + * GIObjectInfoGetValueFunction: (skip) + * @value: a #GValue + * + * Extract an object instance out of @value + * + * Returns: (transfer full): the object instance + */ +typedef void * (*GIObjectInfoGetValueFunction) (const GValue *value); + +/** + * GI_IS_OBJECT_INFO + * @info: an info structure + * + * Checks if @info is a #GIObjectInfo. + */ +#define GI_IS_OBJECT_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_OBJECT) + + +GI_AVAILABLE_IN_ALL +const gchar * g_object_info_get_type_name (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +const gchar * g_object_info_get_type_init (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_object_info_get_abstract (GIObjectInfo *info); + +GI_AVAILABLE_IN_1_70 +gboolean g_object_info_get_final (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_object_info_get_fundamental (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIObjectInfo * g_object_info_get_parent (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_object_info_get_n_interfaces (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIInterfaceInfo * g_object_info_get_interface (GIObjectInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gint g_object_info_get_n_fields (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIFieldInfo * g_object_info_get_field (GIObjectInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gint g_object_info_get_n_properties (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIPropertyInfo * g_object_info_get_property (GIObjectInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gint g_object_info_get_n_methods (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_object_info_get_method (GIObjectInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_object_info_find_method (GIObjectInfo *info, + const gchar *name); + + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_object_info_find_method_using_interfaces (GIObjectInfo *info, + const gchar *name, + GIObjectInfo **implementor); + + +GI_AVAILABLE_IN_ALL +gint g_object_info_get_n_signals (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GISignalInfo * g_object_info_get_signal (GIObjectInfo *info, + gint n); + + +GI_AVAILABLE_IN_ALL +GISignalInfo * g_object_info_find_signal (GIObjectInfo *info, + const gchar *name); + + +GI_AVAILABLE_IN_ALL +gint g_object_info_get_n_vfuncs (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIVFuncInfo * g_object_info_get_vfunc (GIObjectInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIVFuncInfo * g_object_info_find_vfunc (GIObjectInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_1_32 +GIVFuncInfo * g_object_info_find_vfunc_using_interfaces (GIObjectInfo *info, + const gchar *name, + GIObjectInfo **implementor); + +GI_AVAILABLE_IN_ALL +gint g_object_info_get_n_constants (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIConstantInfo * g_object_info_get_constant (GIObjectInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIStructInfo * g_object_info_get_class_struct (GIObjectInfo *info); + + +GI_AVAILABLE_IN_ALL +const char * g_object_info_get_ref_function (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIObjectInfoRefFunction g_object_info_get_ref_function_pointer (GIObjectInfo *info); + + +GI_AVAILABLE_IN_ALL +const char * g_object_info_get_unref_function (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIObjectInfoUnrefFunction g_object_info_get_unref_function_pointer (GIObjectInfo *info); + + +GI_AVAILABLE_IN_ALL +const char * g_object_info_get_set_value_function (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIObjectInfoSetValueFunction g_object_info_get_set_value_function_pointer (GIObjectInfo *info); + + +GI_AVAILABLE_IN_ALL +const char * g_object_info_get_get_value_function (GIObjectInfo *info); + +GI_AVAILABLE_IN_ALL +GIObjectInfoGetValueFunction g_object_info_get_get_value_function_pointer (GIObjectInfo *info); + + +G_END_DECLS + + +#endif /* __GIOBJECTINFO_H__ */ + diff --git a/girepository/gipropertyinfo.c b/girepository/gipropertyinfo.c new file mode 100644 index 000000000..77bb13391 --- /dev/null +++ b/girepository/gipropertyinfo.c @@ -0,0 +1,206 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Property implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" + +/** + * SECTION:gipropertyinfo + * @title: GIPropertyInfo + * @short_description: Struct representing a property + * + * GIPropertyInfo represents a property in a #GObject. + * + * A property belongs to either a #GIObjectInfo or a #GIInterfaceInfo. + */ + +/** + * g_property_info_get_flags: + * @info: a #GIPropertyInfo + * + * Obtain the flags for this property info. See #GParamFlags for + * more information about possible flag values. + * + * Returns: the flags + */ +GParamFlags +g_property_info_get_flags (GIPropertyInfo *info) +{ + GParamFlags flags; + GIRealInfo *rinfo = (GIRealInfo *)info; + PropertyBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_PROPERTY_INFO (info), 0); + + blob = (PropertyBlob *)&rinfo->typelib->data[rinfo->offset]; + + flags = 0; + + if (blob->readable) + flags = flags | G_PARAM_READABLE; + + if (blob->writable) + flags = flags | G_PARAM_WRITABLE; + + if (blob->construct) + flags = flags | G_PARAM_CONSTRUCT; + + if (blob->construct_only) + flags = flags | G_PARAM_CONSTRUCT_ONLY; + + return flags; +} + +/** + * g_property_info_get_type: + * @info: a #GIPropertyInfo + * + * Obtain the type information for the property @info. + * + * Returns: (transfer full): the #GITypeInfo, free it with + * g_base_info_unref() when done. + */ +GITypeInfo * +g_property_info_get_type (GIPropertyInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_PROPERTY_INFO (info), NULL); + + return _g_type_info_new ((GIBaseInfo*)info, + rinfo->typelib, + rinfo->offset + G_STRUCT_OFFSET (PropertyBlob, type)); +} + +/** + * g_property_info_get_ownership_transfer: + * @info: a #GIPropertyInfo + * + * Obtain the ownership transfer for this property. See #GITransfer for more + * information about transfer values. + * + * Returns: the transfer + */ +GITransfer +g_property_info_get_ownership_transfer (GIPropertyInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + PropertyBlob *blob; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_PROPERTY_INFO (info), -1); + + blob = (PropertyBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->transfer_ownership) + return GI_TRANSFER_EVERYTHING; + else if (blob->transfer_container_ownership) + return GI_TRANSFER_CONTAINER; + else + return GI_TRANSFER_NOTHING; +} + +/** + * g_property_info_get_setter: + * @info: a #GIPropertyInfo + * + * Obtains the setter function associated with this #GIPropertyInfo. + * + * The setter is only available for %G_PARAM_WRITABLE properties that + * are also not %G_PARAM_CONSTRUCT_ONLY. + * + * Returns: (transfer full) (nullable): the function info or %NULL if not set. + * Free it with g_base_info_unref() when done. + */ +GIFunctionInfo * +g_property_info_get_setter (GIPropertyInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + PropertyBlob *blob; + GIBaseInfo *container; + GIInfoType parent_type; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_PROPERTY_INFO (info), NULL); + + blob = (PropertyBlob *)&rinfo->typelib->data[rinfo->offset]; + if (!blob->writable || blob->construct_only) + return NULL; + + if (blob->setter == ACCESSOR_SENTINEL) + return NULL; + + container = rinfo->container; + parent_type = g_base_info_get_type (container); + if (parent_type == GI_INFO_TYPE_OBJECT) + return g_object_info_get_method ((GIObjectInfo *) container, blob->setter); + else if (parent_type == GI_INFO_TYPE_INTERFACE) + return g_interface_info_get_method ((GIInterfaceInfo *) container, blob->setter); + else + return NULL; +} + +/** + * g_property_info_get_getter: + * @info: a #GIPropertyInfo + * + * Obtains the getter function associated with this #GIPropertyInfo. + * + * The setter is only available for %G_PARAM_READABLE properties. + * + * Returns: (transfer full) (nullable): the function info or %NULL if not set. + * Free it with g_base_info_unref() when done. + */ +GIFunctionInfo * +g_property_info_get_getter (GIPropertyInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + PropertyBlob *blob; + GIBaseInfo *container; + GIInfoType parent_type; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_PROPERTY_INFO (info), NULL); + + blob = (PropertyBlob *)&rinfo->typelib->data[rinfo->offset]; + if (!blob->readable) + return NULL; + + if (blob->getter == ACCESSOR_SENTINEL) + return NULL; + + container = rinfo->container; + parent_type = g_base_info_get_type (container); + if (parent_type == GI_INFO_TYPE_OBJECT) + return g_object_info_get_method ((GIObjectInfo *) container, blob->getter); + else if (parent_type == GI_INFO_TYPE_INTERFACE) + return g_interface_info_get_method ((GIInterfaceInfo *) container, blob->getter); + else + return NULL; +} diff --git a/girepository/gipropertyinfo.h b/girepository/gipropertyinfo.h new file mode 100644 index 000000000..4889f42eb --- /dev/null +++ b/girepository/gipropertyinfo.h @@ -0,0 +1,62 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Property + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIPROPERTYINFO_H__ +#define __GIPROPERTYINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_PROPERTY_INFO + * @info: an info structure + * + * Checks if @info is a #GIPropertyInfo. + */ +#define GI_IS_PROPERTY_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_PROPERTY) + + +GI_AVAILABLE_IN_ALL +GParamFlags g_property_info_get_flags (GIPropertyInfo *info); + +GI_AVAILABLE_IN_ALL +GITypeInfo * g_property_info_get_type (GIPropertyInfo *info); + +GI_AVAILABLE_IN_ALL +GITransfer g_property_info_get_ownership_transfer (GIPropertyInfo *info); + +GI_AVAILABLE_IN_1_70 +GIFunctionInfo *g_property_info_get_setter (GIPropertyInfo *info); + +GI_AVAILABLE_IN_1_70 +GIFunctionInfo *g_property_info_get_getter (GIPropertyInfo *info); + +G_END_DECLS + +#endif /* __GIPROPERTYINFO_H__ */ + diff --git a/girepository/giregisteredtypeinfo.c b/girepository/giregisteredtypeinfo.c new file mode 100644 index 000000000..653befec6 --- /dev/null +++ b/girepository/giregisteredtypeinfo.c @@ -0,0 +1,143 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Registered Type implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" + +/** + * SECTION:giregisteredtypeinfo + * @title: GIRegisteredTypeInfo + * @short_description: Struct representing a struct with a GType + * + * GIRegisteredTypeInfo represents an entity with a GType associated. + * + * Could be either a #GIEnumInfo, #GIInterfaceInfo, #GIObjectInfo, + * #GIStructInfo or a #GIUnionInfo. + * + * A registered type info struct has a name and a type function. + * + * To get the name call g_registered_type_info_get_type_name(). + * Most users want to call g_registered_type_info_get_g_type() and don't worry + * about the rest of the details. + */ + +/** + * g_registered_type_info_get_type_name: + * @info: a #GIRegisteredTypeInfo + * + * Obtain the type name of the struct within the GObject type system. + * This type can be passed to g_type_name() to get a #GType. + * + * Returns: the type name + */ +const gchar * +g_registered_type_info_get_type_name (GIRegisteredTypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + RegisteredTypeBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_REGISTERED_TYPE_INFO (info), NULL); + + blob = (RegisteredTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->gtype_name) + return g_typelib_get_string (rinfo->typelib, blob->gtype_name); + + return NULL; +} + +/** + * g_registered_type_info_get_type_init: + * @info: a #GIRegisteredTypeInfo + * + * Obtain the type init function for @info. The type init function is the + * function which will register the GType within the GObject type system. + * Usually this is not called by langauge bindings or applications, use + * g_registered_type_info_get_g_type() directly instead. + * + * Returns: the symbol name of the type init function, suitable for + * passing into g_module_symbol(). + */ +const gchar * +g_registered_type_info_get_type_init (GIRegisteredTypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + RegisteredTypeBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_REGISTERED_TYPE_INFO (info), NULL); + + blob = (RegisteredTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->gtype_init) + return g_typelib_get_string (rinfo->typelib, blob->gtype_init); + + return NULL; +} + +/** + * g_registered_type_info_get_g_type: + * @info: a #GIRegisteredTypeInfo + * + * Obtain the #GType for this registered type or G_TYPE_NONE which a special meaning. + * It means that either there is no type information associated with this @info or + * that the shared library which provides the type_init function for this + * @info cannot be called. + * + * Returns: the #GType. + */ +GType +g_registered_type_info_get_g_type (GIRegisteredTypeInfo *info) +{ + const char *type_init; + GType (* get_type_func) (void); + GIRealInfo *rinfo = (GIRealInfo*)info; + + g_return_val_if_fail (info != NULL, G_TYPE_INVALID); + g_return_val_if_fail (GI_IS_REGISTERED_TYPE_INFO (info), G_TYPE_INVALID); + + type_init = g_registered_type_info_get_type_init (info); + + if (type_init == NULL) + return G_TYPE_NONE; + else if (!strcmp (type_init, "intern")) + /* The special string "intern" is used for some types exposed by libgobject + (that therefore should be always available) */ + return g_type_from_name (g_registered_type_info_get_type_name (info)); + + get_type_func = NULL; + if (!g_typelib_symbol (rinfo->typelib, + type_init, + (void**) &get_type_func)) + return G_TYPE_NONE; + + return (* get_type_func) (); +} + diff --git a/girepository/giregisteredtypeinfo.h b/girepository/giregisteredtypeinfo.h new file mode 100644 index 000000000..efc4a7739 --- /dev/null +++ b/girepository/giregisteredtypeinfo.h @@ -0,0 +1,64 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Registered Type + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIREGISTEREDTYPEINFO_H__ +#define __GIREGISTEREDTYPEINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +/** + * GI_IS_REGISTERED_TYPE_INFO + * @info: an info structure + * + * Checks if @info is a #GIRegisteredTypeInfo or derived from it. + */ +#define GI_IS_REGISTERED_TYPE_INFO(info) \ + ((g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_BOXED) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_ENUM) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_FLAGS) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_INTERFACE) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_OBJECT) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_STRUCT) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_UNION) || \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_BOXED)) + +GI_AVAILABLE_IN_ALL +const gchar * g_registered_type_info_get_type_name (GIRegisteredTypeInfo *info); + +GI_AVAILABLE_IN_ALL +const gchar * g_registered_type_info_get_type_init (GIRegisteredTypeInfo *info); + +GI_AVAILABLE_IN_ALL +GType g_registered_type_info_get_g_type (GIRegisteredTypeInfo *info); + +G_END_DECLS + + +#endif /* __GIREGISTEREDTYPEINFO_H__ */ + diff --git a/girepository/girepository-private.h b/girepository/girepository-private.h new file mode 100644 index 000000000..de0b04e6e --- /dev/null +++ b/girepository/girepository-private.h @@ -0,0 +1,116 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Private headers + * + * Copyright (C) 2010 Johan Dahlin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIREPOSITORY_PRIVATE_H__ +#define __GIREPOSITORY_PRIVATE_H__ + +#include +#include + +#define __GIREPOSITORY_H_INSIDE__ + +#include +#include +#include + +typedef struct _GIRealInfo GIRealInfo; + +/* We changed a gint32 -> gint in the structure below, which should be + * valid everywhere we care about. + */ +G_STATIC_ASSERT (sizeof (int) == sizeof (gint32)); + +/* + * We just use one structure for all of the info object + * types; in general, we should be reading data directly + * from the typelib, and not having computed data in + * per-type structures. + */ +struct _GIRealInfo +{ + /* Keep this part in sync with GIUnresolvedInfo below */ + gint32 type; + volatile gint ref_count; + GIRepository *repository; + GIBaseInfo *container; + + /* Resolved specific */ + + GITypelib *typelib; + guint32 offset; + + guint32 type_is_embedded : 1; /* Used by GITypeInfo */ + guint32 reserved : 31; + + gpointer reserved2[4]; +}; + +struct _GIUnresolvedInfo +{ + /* Keep this part in sync with GIBaseInfo above */ + gint32 type; + volatile gint ref_count; + GIRepository *repository; + GIBaseInfo *container; + + /* Unresolved specific */ + + const gchar *name; + const gchar *namespace; +}; + +void _g_info_init (GIRealInfo *info, + GIInfoType type, + GIRepository *repository, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset); + +GIBaseInfo * _g_info_from_entry (GIRepository *repository, + GITypelib *typelib, + guint16 index); + +GIBaseInfo * _g_info_new_full (GIInfoType type, + GIRepository *repository, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset); + +GITypeInfo * _g_type_info_new (GIBaseInfo *container, + GITypelib *typelib, + guint32 offset); + +void _g_type_info_init (GIBaseInfo *info, + GIBaseInfo *container, + GITypelib *typelib, + guint32 offset); + +GIFunctionInfo * _g_base_info_find_method (GIBaseInfo *base, + guint32 offset, + gint n_methods, + const gchar *name); + +GIVFuncInfo * _g_base_info_find_vfunc (GIRealInfo *rinfo, + guint32 offset, + gint n_vfuncs, + const gchar *name); + +#endif /* __GIREPOSITORY_PRIVATE_H__ */ diff --git a/girepository/girepository.c b/girepository/girepository.c new file mode 100644 index 000000000..a0754f456 --- /dev/null +++ b/girepository/girepository.c @@ -0,0 +1,1880 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Repository implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008 Colin Walters + * Copyright (C) 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include + +#include +#include +#include +#include "girepository.h" +#include "gitypelib-internal.h" +#include "girepository-private.h" + +/** + * SECTION:girepository + * @short_description: GObject Introspection repository manager + * @include: girepository.h + * + * #GIRepository is used to manage repositories of namespaces. Namespaces + * are represented on disk by type libraries (.typelib files). + * + * ### Discovery of type libraries + * + * #GIRepository will typically look for a `girepository-1.0` directory + * under the library directory used when compiling gobject-introspection. + * + * It is possible to control the search paths programmatically, using + * g_irepository_prepend_search_path(). It is also possible to modify + * the search paths by using the `GI_TYPELIB_PATH` environment variable. + * The environment variable takes precedence over the default search path + * and the g_irepository_prepend_search_path() calls. + */ + + +static GIRepository *default_repository = NULL; +static GSList *typelib_search_path = NULL; + +typedef struct { + guint n_interfaces; + GIBaseInfo *interfaces[]; +} GTypeInterfaceCache; + +static void +gtype_interface_cache_free (gpointer data) +{ + GTypeInterfaceCache *cache = data; + guint i; + + for (i = 0; i < cache->n_interfaces; i++) + g_base_info_unref ((GIBaseInfo*) cache->interfaces[i]); + g_free (cache); +} + +struct _GIRepositoryPrivate +{ + GHashTable *typelibs; /* (string) namespace -> GITypelib */ + GHashTable *lazy_typelibs; /* (string) namespace-version -> GITypelib */ + GHashTable *info_by_gtype; /* GType -> GIBaseInfo */ + GHashTable *info_by_error_domain; /* GQuark -> GIBaseInfo */ + GHashTable *interfaces_for_gtype; /* GType -> GTypeInterfaceCache */ + GHashTable *unknown_gtypes; /* hashset of GType */ +}; + +G_DEFINE_TYPE_WITH_CODE (GIRepository, g_irepository, G_TYPE_OBJECT, G_ADD_PRIVATE (GIRepository)); + +#ifdef G_PLATFORM_WIN32 + +#include + +static HMODULE girepository_dll = NULL; + +#ifdef DLL_EXPORT + +BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved); + +BOOL WINAPI +DllMain (HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved) +{ + if (fdwReason == DLL_PROCESS_ATTACH) + girepository_dll = hinstDLL; + + return TRUE; +} + +#endif + +#undef GOBJECT_INTROSPECTION_LIBDIR + +/* GOBJECT_INTROSPECTION_LIBDIR is used only in code called just once, + * so no problem leaking this + */ +#define GOBJECT_INTROSPECTION_LIBDIR \ + g_build_filename (g_win32_get_package_installation_directory_of_module (girepository_dll), \ + "lib", \ + NULL) + +#endif + +static void +g_irepository_init (GIRepository *repository) +{ + repository->priv = g_irepository_get_instance_private (repository); + repository->priv->typelibs + = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) g_typelib_free); + repository->priv->lazy_typelibs + = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) NULL); + repository->priv->info_by_gtype + = g_hash_table_new_full (g_direct_hash, g_direct_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) g_base_info_unref); + repository->priv->info_by_error_domain + = g_hash_table_new_full (g_direct_hash, g_direct_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) g_base_info_unref); + repository->priv->interfaces_for_gtype + = g_hash_table_new_full (g_direct_hash, g_direct_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) gtype_interface_cache_free); + repository->priv->unknown_gtypes = g_hash_table_new (NULL, NULL); +} + +static void +g_irepository_finalize (GObject *object) +{ + GIRepository *repository = G_IREPOSITORY (object); + + g_hash_table_destroy (repository->priv->typelibs); + g_hash_table_destroy (repository->priv->lazy_typelibs); + g_hash_table_destroy (repository->priv->info_by_gtype); + g_hash_table_destroy (repository->priv->info_by_error_domain); + g_hash_table_destroy (repository->priv->interfaces_for_gtype); + g_hash_table_destroy (repository->priv->unknown_gtypes); + + (* G_OBJECT_CLASS (g_irepository_parent_class)->finalize) (G_OBJECT (repository)); +} + +static void +g_irepository_class_init (GIRepositoryClass *class) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (class); + + gobject_class->finalize = g_irepository_finalize; +} + +static void +init_globals (void) +{ + static gsize initialized = 0; + + if (!g_once_init_enter (&initialized)) + return; + + if (default_repository == NULL) + default_repository = g_object_new (G_TYPE_IREPOSITORY, NULL); + + if (typelib_search_path == NULL) + { + const char *libdir; + char *typelib_dir; + const gchar *type_lib_path_env; + + /* This variable is intended to take precedence over both: + * - the default search path; + * - all g_irepository_prepend_search_path() calls. + */ + type_lib_path_env = g_getenv ("GI_TYPELIB_PATH"); + + typelib_search_path = NULL; + if (type_lib_path_env) + { + gchar **custom_dirs; + gchar **d; + + custom_dirs = g_strsplit (type_lib_path_env, G_SEARCHPATH_SEPARATOR_S, 0); + + d = custom_dirs; + while (*d) + { + typelib_search_path = g_slist_prepend (typelib_search_path, *d); + d++; + } + + /* ownership of the array content was passed to the list */ + g_free (custom_dirs); + } + + libdir = GOBJECT_INTROSPECTION_LIBDIR; + + typelib_dir = g_build_filename (libdir, "girepository-1.0", NULL); + + typelib_search_path = g_slist_prepend (typelib_search_path, typelib_dir); + + typelib_search_path = g_slist_reverse (typelib_search_path); + } + + g_once_init_leave (&initialized, 1); +} + +/** + * g_irepository_prepend_search_path: + * @directory: (type filename): directory name to prepend to the typelib + * search path + * + * Prepends @directory to the typelib search path. + * + * See also: g_irepository_get_search_path(). + */ +void +g_irepository_prepend_search_path (const char *directory) +{ + init_globals (); + typelib_search_path = g_slist_prepend (typelib_search_path, g_strdup (directory)); +} + +/** + * g_irepository_get_search_path: + * + * Returns the current search path #GIRepository will use when loading + * typelib files. The list is internal to #GIRepository and should not + * be freed, nor should its string elements. + * + * Returns: (element-type filename) (transfer none): #GSList of strings + */ +GSList * +g_irepository_get_search_path (void) +{ + return typelib_search_path; +} + +static char * +build_typelib_key (const char *name, const char *source) +{ + GString *str = g_string_new (name); + g_string_append_c (str, '\0'); + g_string_append (str, source); + return g_string_free (str, FALSE); +} + +/* Note: Returns %NULL (not an empty %NULL-terminated array) if there are no + * dependencies. */ +static char ** +get_typelib_dependencies (GITypelib *typelib) +{ + Header *header; + const char *dependencies_glob; + + header = (Header *)typelib->data; + + if (header->dependencies == 0) + return NULL; + + dependencies_glob = g_typelib_get_string (typelib, header->dependencies); + return g_strsplit (dependencies_glob, "|", 0); +} + +static GIRepository * +get_repository (GIRepository *repository) +{ + init_globals (); + + if (repository != NULL) + return repository; + else + return default_repository; +} + +static GITypelib * +check_version_conflict (GITypelib *typelib, + const gchar *namespace, + const gchar *expected_version, + char **version_conflict) +{ + Header *header; + const char *loaded_version; + + if (expected_version == NULL) + { + if (version_conflict) + *version_conflict = NULL; + return typelib; + } + + header = (Header*)typelib->data; + loaded_version = g_typelib_get_string (typelib, header->nsversion); + g_assert (loaded_version != NULL); + + if (strcmp (expected_version, loaded_version) != 0) + { + if (version_conflict) + *version_conflict = (char*)loaded_version; + return NULL; + } + if (version_conflict) + *version_conflict = NULL; + return typelib; +} + +static GITypelib * +get_registered_status (GIRepository *repository, + const char *namespace, + const char *version, + gboolean allow_lazy, + gboolean *lazy_status, + char **version_conflict) +{ + GITypelib *typelib; + repository = get_repository (repository); + if (lazy_status) + *lazy_status = FALSE; + typelib = g_hash_table_lookup (repository->priv->typelibs, namespace); + if (typelib) + return check_version_conflict (typelib, namespace, version, version_conflict); + typelib = g_hash_table_lookup (repository->priv->lazy_typelibs, namespace); + if (!typelib) + return NULL; + if (lazy_status) + *lazy_status = TRUE; + if (!allow_lazy) + return NULL; + return check_version_conflict (typelib, namespace, version, version_conflict); +} + +static GITypelib * +get_registered (GIRepository *repository, + const char *namespace, + const char *version) +{ + return get_registered_status (repository, namespace, version, TRUE, NULL, NULL); +} + +static gboolean +load_dependencies_recurse (GIRepository *repository, + GITypelib *typelib, + GError **error) +{ + char **dependencies; + + dependencies = get_typelib_dependencies (typelib); + + if (dependencies != NULL) + { + int i; + + for (i = 0; dependencies[i]; i++) + { + char *dependency = dependencies[i]; + const char *last_dash; + char *dependency_namespace; + const char *dependency_version; + + last_dash = strrchr (dependency, '-'); + dependency_namespace = g_strndup (dependency, last_dash - dependency); + dependency_version = last_dash+1; + + if (!g_irepository_require (repository, dependency_namespace, dependency_version, + 0, error)) + { + g_free (dependency_namespace); + g_strfreev (dependencies); + return FALSE; + } + g_free (dependency_namespace); + } + g_strfreev (dependencies); + } + return TRUE; +} + +static const char * +register_internal (GIRepository *repository, + const char *source, + gboolean lazy, + GITypelib *typelib, + GError **error) +{ + Header *header; + const gchar *namespace; + + g_return_val_if_fail (typelib != NULL, FALSE); + + header = (Header *)typelib->data; + + g_return_val_if_fail (header != NULL, FALSE); + + namespace = g_typelib_get_string (typelib, header->namespace); + + if (lazy) + { + g_assert (!g_hash_table_lookup (repository->priv->lazy_typelibs, + namespace)); + g_hash_table_insert (repository->priv->lazy_typelibs, + build_typelib_key (namespace, source), (void *)typelib); + } + else + { + gpointer value; + char *key; + + /* First, try loading all the dependencies */ + if (!load_dependencies_recurse (repository, typelib, error)) + return NULL; + + /* Check if we are transitioning from lazily loaded state */ + if (g_hash_table_lookup_extended (repository->priv->lazy_typelibs, + namespace, + (gpointer)&key, &value)) + g_hash_table_remove (repository->priv->lazy_typelibs, key); + else + key = build_typelib_key (namespace, source); + + g_hash_table_insert (repository->priv->typelibs, key, (void *)typelib); + } + + /* These types might be resolved now, clear the cache */ + g_hash_table_remove_all (repository->priv->unknown_gtypes); + + return namespace; +} + +/** + * g_irepository_get_immediate_dependencies: + * @repository: (nullable): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace of interest + * + * Return an array of the immediate versioned dependencies for @namespace_. + * Returned strings are of the form `namespace-version`. + * + * Note: @namespace_ must have already been loaded using a function + * such as g_irepository_require() before calling this function. + * + * To get the transitive closure of dependencies for @namespace_, use + * g_irepository_get_dependencies(). + * + * Returns: (transfer full): Zero-terminated string array of immediate versioned + * dependencies + * + * Since: 1.44 + */ +char ** +g_irepository_get_immediate_dependencies (GIRepository *repository, + const char *namespace) +{ + GITypelib *typelib; + gchar **deps; + + g_return_val_if_fail (namespace != NULL, NULL); + + repository = get_repository (repository); + + typelib = get_registered (repository, namespace, NULL); + g_return_val_if_fail (typelib != NULL, NULL); + + /* Ensure we always return a non-%NULL vector. */ + deps = get_typelib_dependencies (typelib); + if (deps == NULL) + deps = g_strsplit ("", "|", 0); + + return deps; +} + +/* Load the transitive closure of dependency namespace-version strings for the + * given @typelib. @repository must be non-%NULL. @transitive_dependencies must + * be a pre-existing GHashTable set for storing the + * dependencies. */ +static void +get_typelib_dependencies_transitive (GIRepository *repository, + GITypelib *typelib, + GHashTable *transitive_dependencies) +{ + gchar **immediate_dependencies; + guint i; + + immediate_dependencies = get_typelib_dependencies (typelib); + + for (i = 0; immediate_dependencies != NULL && immediate_dependencies[i]; i++) + { + gchar *dependency; + const gchar *last_dash; + gchar *dependency_namespace; + + dependency = immediate_dependencies[i]; + + /* Steal from the strv. */ + g_hash_table_add (transitive_dependencies, dependency); + immediate_dependencies[i] = NULL; + + /* Recurse for this namespace. */ + last_dash = strrchr (dependency, '-'); + dependency_namespace = g_strndup (dependency, last_dash - dependency); + + typelib = get_registered (repository, dependency_namespace, NULL); + g_return_if_fail (typelib != NULL); + get_typelib_dependencies_transitive (repository, typelib, + transitive_dependencies); + + g_free (dependency_namespace); + } + + g_free (immediate_dependencies); +} + +/** + * g_irepository_get_dependencies: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace of interest + * + * Retrieves all (transitive) versioned dependencies for + * @namespace_. + * + * The strings are of the form `namespace-version`. + * + * Note: @namespace_ must have already been loaded using a function + * such as g_irepository_require() before calling this function. + * + * To get only the immediate dependencies for @namespace_, use + * g_irepository_get_immediate_dependencies(). + * + * Returns: (transfer full) (array zero-terminated=1): all versioned + * dependencies + */ +char ** +g_irepository_get_dependencies (GIRepository *repository, + const char *namespace) +{ + GITypelib *typelib; + GHashTable *transitive_dependencies; /* set of owned utf8 */ + GHashTableIter iter; + gchar *dependency; + GPtrArray *out; /* owned utf8 elements */ + + g_return_val_if_fail (namespace != NULL, NULL); + + repository = get_repository (repository); + + typelib = get_registered (repository, namespace, NULL); + g_return_val_if_fail (typelib != NULL, NULL); + + /* Load the dependencies. */ + transitive_dependencies = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + get_typelib_dependencies_transitive (repository, typelib, + transitive_dependencies); + + /* Convert to a string array. */ + out = g_ptr_array_new_full (g_hash_table_size (transitive_dependencies), + g_free); + g_hash_table_iter_init (&iter, transitive_dependencies); + + while (g_hash_table_iter_next (&iter, (gpointer) &dependency, NULL)) + { + g_ptr_array_add (out, dependency); + g_hash_table_iter_steal (&iter); + } + + g_hash_table_unref (transitive_dependencies); + + /* Add a NULL terminator. */ + g_ptr_array_add (out, NULL); + + return (gchar **) g_ptr_array_free (out, FALSE); +} + +/** + * g_irepository_load_typelib: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @typelib: TODO + * @flags: TODO + * @error: TODO + * + * TODO + */ +const char * +g_irepository_load_typelib (GIRepository *repository, + GITypelib *typelib, + GIRepositoryLoadFlags flags, + GError **error) +{ + Header *header; + const char *namespace; + const char *nsversion; + gboolean allow_lazy = flags & G_IREPOSITORY_LOAD_FLAG_LAZY; + gboolean is_lazy; + char *version_conflict; + + repository = get_repository (repository); + + header = (Header *) typelib->data; + namespace = g_typelib_get_string (typelib, header->namespace); + nsversion = g_typelib_get_string (typelib, header->nsversion); + + if (get_registered_status (repository, namespace, nsversion, allow_lazy, + &is_lazy, &version_conflict)) + { + if (version_conflict != NULL) + { + g_set_error (error, G_IREPOSITORY_ERROR, + G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT, + "Attempting to load namespace '%s', version '%s', but '%s' is already loaded", + namespace, nsversion, version_conflict); + return NULL; + } + return namespace; + } + return register_internal (repository, "", + allow_lazy, typelib, error); +} + +/** + * g_irepository_is_registered: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace of interest + * @version: (allow-none): Required version, may be %NULL for latest + * + * Check whether a particular namespace (and optionally, a specific + * version thereof) is currently loaded. This function is likely to + * only be useful in unusual circumstances; in order to act upon + * metadata in the namespace, you should call g_irepository_require() + * instead which will ensure the namespace is loaded, and return as + * quickly as this function will if it has already been loaded. + * + * Returns: %TRUE if namespace-version is loaded, %FALSE otherwise + */ +gboolean +g_irepository_is_registered (GIRepository *repository, + const gchar *namespace, + const gchar *version) +{ + repository = get_repository (repository); + return get_registered (repository, namespace, version) != NULL; +} + +/** + * g_irepository_get_default: + * + * Returns the singleton process-global default #GIRepository. It is + * not currently supported to have multiple repositories in a + * particular process, but this function is provided in the unlikely + * eventuality that it would become possible, and as a convenience for + * higher level language bindings to conform to the GObject method + * call conventions. + * + * All methods on #GIRepository also accept %NULL as an instance + * parameter to mean this default repository, which is usually more + * convenient for C. + * + * Returns: (transfer none): The global singleton #GIRepository + */ +GIRepository * +g_irepository_get_default (void) +{ + return get_repository (NULL); +} + +/** + * g_irepository_get_n_infos: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace to inspect + * + * This function returns the number of metadata entries in + * given namespace @namespace_. The namespace must have + * already been loaded before calling this function. + * + * Returns: number of metadata entries + */ +gint +g_irepository_get_n_infos (GIRepository *repository, + const gchar *namespace) +{ + GITypelib *typelib; + gint n_interfaces = 0; + + g_return_val_if_fail (namespace != NULL, -1); + + repository = get_repository (repository); + + typelib = get_registered (repository, namespace, NULL); + + g_return_val_if_fail (typelib != NULL, -1); + + n_interfaces = ((Header *)typelib->data)->n_local_entries; + + return n_interfaces; +} + +/** + * g_irepository_get_info: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace to inspect + * @index: 0-based offset into namespace metadata for entry + * + * This function returns a particular metadata entry in the + * given namespace @namespace_. The namespace must have + * already been loaded before calling this function. + * See g_irepository_get_n_infos() to find the maximum number of + * entries. + * + * Returns: (transfer full): #GIBaseInfo containing metadata + */ +GIBaseInfo * +g_irepository_get_info (GIRepository *repository, + const gchar *namespace, + gint index) +{ + GITypelib *typelib; + DirEntry *entry; + + g_return_val_if_fail (namespace != NULL, NULL); + + repository = get_repository (repository); + + typelib = get_registered (repository, namespace, NULL); + + g_return_val_if_fail (typelib != NULL, NULL); + + entry = g_typelib_get_dir_entry (typelib, index + 1); + if (entry == NULL) + return NULL; + return _g_info_new_full (entry->blob_type, + repository, + NULL, typelib, entry->offset); +} + +typedef struct { + const gchar *gtype_name; + GITypelib *result_typelib; +} FindByGTypeData; + +static DirEntry * +find_by_gtype (GHashTable *table, FindByGTypeData *data, gboolean check_prefix) +{ + GHashTableIter iter; + gpointer key, value; + DirEntry *ret; + + g_hash_table_iter_init (&iter, table); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + GITypelib *typelib = (GITypelib*)value; + if (check_prefix) + { + if (!g_typelib_matches_gtype_name_prefix (typelib, data->gtype_name)) + continue; + } + + ret = g_typelib_get_dir_entry_by_gtype_name (typelib, data->gtype_name); + if (ret) + { + data->result_typelib = typelib; + return ret; + } + } + + return NULL; +} + +/** + * g_irepository_find_by_gtype: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @gtype: GType to search for + * + * Searches all loaded namespaces for a particular #GType. Note that + * in order to locate the metadata, the namespace corresponding to + * the type must first have been loaded. There is currently no + * mechanism for determining the namespace which corresponds to an + * arbitrary GType - thus, this function will operate most reliably + * when you know the GType to originate from be from a loaded namespace. + * + * Returns: (transfer full): #GIBaseInfo representing metadata about @type, or %NULL + */ +GIBaseInfo * +g_irepository_find_by_gtype (GIRepository *repository, + GType gtype) +{ + FindByGTypeData data; + GIBaseInfo *cached; + DirEntry *entry; + + g_return_val_if_fail (gtype != G_TYPE_INVALID, NULL); + + repository = get_repository (repository); + + cached = g_hash_table_lookup (repository->priv->info_by_gtype, + (gpointer)gtype); + + if (cached != NULL) + return g_base_info_ref (cached); + + if (g_hash_table_contains (repository->priv->unknown_gtypes, (gpointer)gtype)) + return NULL; + + data.gtype_name = g_type_name (gtype); + data.result_typelib = NULL; + + /* Inside each typelib, we include the "C prefix" which acts as + * a namespace mechanism. For GtkTreeView, the C prefix is Gtk. + * Given the assumption that GTypes for a library also use the + * C prefix, we know we can skip examining a typelib if our + * target type does not have this typelib's C prefix. Use this + * assumption as our first attempt at locating the DirEntry. + */ + entry = find_by_gtype (repository->priv->typelibs, &data, TRUE); + if (entry == NULL) + entry = find_by_gtype (repository->priv->lazy_typelibs, &data, TRUE); + + /* Not ever class library necessarily specifies a correct c_prefix, + * so take a second pass. This time we will try a global lookup, + * ignoring prefixes. + * See http://bugzilla.gnome.org/show_bug.cgi?id=564016 + */ + if (entry == NULL) + entry = find_by_gtype (repository->priv->typelibs, &data, FALSE); + if (entry == NULL) + entry = find_by_gtype (repository->priv->lazy_typelibs, &data, FALSE); + + if (entry != NULL) + { + cached = _g_info_new_full (entry->blob_type, + repository, + NULL, data.result_typelib, entry->offset); + + g_hash_table_insert (repository->priv->info_by_gtype, + (gpointer) gtype, + g_base_info_ref (cached)); + return cached; + } + else + { + g_hash_table_add (repository->priv->unknown_gtypes, (gpointer) gtype); + return NULL; + } +} + +/** + * g_irepository_find_by_name: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace which will be searched + * @name: Entry name to find + * + * Searches for a particular entry in a namespace. Before calling + * this function for a particular namespace, you must call + * g_irepository_require() once to load the namespace, or otherwise + * ensure the namespace has already been loaded. + * + * Returns: (transfer full): #GIBaseInfo representing metadata about @name, or %NULL + */ +GIBaseInfo * +g_irepository_find_by_name (GIRepository *repository, + const gchar *namespace, + const gchar *name) +{ + GITypelib *typelib; + DirEntry *entry; + + g_return_val_if_fail (namespace != NULL, NULL); + + repository = get_repository (repository); + typelib = get_registered (repository, namespace, NULL); + g_return_val_if_fail (typelib != NULL, NULL); + + entry = g_typelib_get_dir_entry_by_name (typelib, name); + if (entry == NULL) + return NULL; + return _g_info_new_full (entry->blob_type, + repository, + NULL, typelib, entry->offset); +} + +typedef struct { + GIRepository *repository; + GQuark domain; + + GITypelib *result_typelib; + DirEntry *result; +} FindByErrorDomainData; + +static void +find_by_error_domain_foreach (gpointer key, + gpointer value, + gpointer datap) +{ + GITypelib *typelib = (GITypelib*)value; + FindByErrorDomainData *data = datap; + + if (data->result != NULL) + return; + + data->result = g_typelib_get_dir_entry_by_error_domain (typelib, data->domain); + if (data->result) + data->result_typelib = typelib; +} + +/** + * g_irepository_find_by_error_domain: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @domain: a #GError domain + * + * Searches for the enum type corresponding to the given #GError + * domain. Before calling this function for a particular namespace, + * you must call g_irepository_require() once to load the namespace, or + * otherwise ensure the namespace has already been loaded. + * + * Returns: (transfer full): #GIEnumInfo representing metadata about @domain's + * enum type, or %NULL + * Since: 1.30 + */ +GIEnumInfo * +g_irepository_find_by_error_domain (GIRepository *repository, + GQuark domain) +{ + FindByErrorDomainData data; + GIEnumInfo *cached; + + repository = get_repository (repository); + + cached = g_hash_table_lookup (repository->priv->info_by_error_domain, + GUINT_TO_POINTER (domain)); + + if (cached != NULL) + return g_base_info_ref ((GIBaseInfo *)cached); + + data.repository = repository; + data.domain = domain; + data.result_typelib = NULL; + data.result = NULL; + + g_hash_table_foreach (repository->priv->typelibs, find_by_error_domain_foreach, &data); + if (data.result == NULL) + g_hash_table_foreach (repository->priv->lazy_typelibs, find_by_error_domain_foreach, &data); + + if (data.result != NULL) + { + cached = _g_info_new_full (data.result->blob_type, + repository, + NULL, data.result_typelib, data.result->offset); + + g_hash_table_insert (repository->priv->info_by_error_domain, + GUINT_TO_POINTER (domain), + g_base_info_ref (cached)); + return cached; + } + return NULL; +} + +/** + * g_irepository_get_object_gtype_interfaces: + * @repository: (nullable): a #GIRepository, or %NULL for the default repository + * @gtype: a #GType whose fundamental type is G_TYPE_OBJECT + * @n_interfaces_out: (out): Number of interfaces + * @interfaces_out: (out) (transfer none) (array length=n_interfaces_out): Interfaces for @gtype + * + * Look up the implemented interfaces for @gtype. This function + * cannot fail per se; but for a totally "unknown" #GType, it may + * return 0 implemented interfaces. + * + * The semantics of this function are designed for a dynamic binding, + * where in certain cases (such as a function which returns an + * interface which may have "hidden" implementation classes), not all + * data may be statically known, and will have to be determined from + * the #GType of the object. An example is g_file_new_for_path() + * returning a concrete class of #GLocalFile, which is a #GType we + * see at runtime, but not statically. + * + * Since: 1.62 + */ +void +g_irepository_get_object_gtype_interfaces (GIRepository *repository, + GType gtype, + guint *n_interfaces_out, + GIInterfaceInfo ***interfaces_out) +{ + GTypeInterfaceCache *cache; + + g_return_if_fail (g_type_fundamental (gtype) == G_TYPE_OBJECT); + + repository = get_repository (repository); + + cache = g_hash_table_lookup (repository->priv->interfaces_for_gtype, + (gpointer) gtype); + if (cache == NULL) + { + GType *interfaces; + guint n_interfaces; + guint i; + GList *interface_infos = NULL, *iter; + + interfaces = g_type_interfaces (gtype, &n_interfaces); + for (i = 0; i < n_interfaces; i++) + { + GIBaseInfo *base_info; + + base_info = g_irepository_find_by_gtype (repository, interfaces[i]); + if (base_info == NULL) + continue; + + if (g_base_info_get_type (base_info) != GI_INFO_TYPE_INTERFACE) + { + /* FIXME - could this really happen? */ + g_base_info_unref (base_info); + continue; + } + + if (!g_list_find (interface_infos, base_info)) + interface_infos = g_list_prepend (interface_infos, base_info); + } + + cache = g_malloc (sizeof (GTypeInterfaceCache) + + sizeof (GIBaseInfo*) * g_list_length (interface_infos)); + cache->n_interfaces = g_list_length (interface_infos); + for (iter = interface_infos, i = 0; iter; iter = iter->next, i++) + cache->interfaces[i] = iter->data; + g_list_free (interface_infos); + + g_hash_table_insert (repository->priv->interfaces_for_gtype, (gpointer) gtype, + cache); + + g_free (interfaces); + } + + *n_interfaces_out = cache->n_interfaces; + *interfaces_out = (GIInterfaceInfo**)&cache->interfaces[0]; +} + +static void +collect_namespaces (gpointer key, + gpointer value, + gpointer data) +{ + GList **list = data; + + *list = g_list_append (*list, key); +} + +/** + * g_irepository_get_loaded_namespaces: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * + * Return the list of currently loaded namespaces. + * + * Returns: (element-type utf8) (transfer full): List of namespaces + */ +gchar ** +g_irepository_get_loaded_namespaces (GIRepository *repository) +{ + GList *l, *list = NULL; + gchar **names; + gint i; + + repository = get_repository (repository); + + g_hash_table_foreach (repository->priv->typelibs, collect_namespaces, &list); + g_hash_table_foreach (repository->priv->lazy_typelibs, collect_namespaces, &list); + + names = g_malloc0 (sizeof (gchar *) * (g_list_length (list) + 1)); + i = 0; + for (l = list; l; l = l->next) + names[i++] = g_strdup (l->data); + g_list_free (list); + + return names; +} + +/** + * g_irepository_get_version: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace to inspect + * + * This function returns the loaded version associated with the given + * namespace @namespace_. + * + * Note: The namespace must have already been loaded using a function + * such as g_irepository_require() before calling this function. + * + * Returns: Loaded version + */ +const gchar * +g_irepository_get_version (GIRepository *repository, + const gchar *namespace) +{ + GITypelib *typelib; + Header *header; + + g_return_val_if_fail (namespace != NULL, NULL); + + repository = get_repository (repository); + + typelib = get_registered (repository, namespace, NULL); + + g_return_val_if_fail (typelib != NULL, NULL); + + header = (Header *) typelib->data; + return g_typelib_get_string (typelib, header->nsversion); +} + +/** + * g_irepository_get_shared_library: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace to inspect + * + * This function returns a comma-separated list of paths to the + * shared C libraries associated with the given namespace @namespace_. + * There may be no shared library path associated, in which case this + * function will return %NULL. + * + * Note: The namespace must have already been loaded using a function + * such as g_irepository_require() before calling this function. + * + * Returns: (nullable): Comma-separated list of paths to shared libraries, + * or %NULL if none are associated + */ +const gchar * +g_irepository_get_shared_library (GIRepository *repository, + const gchar *namespace) +{ + GITypelib *typelib; + Header *header; + + g_return_val_if_fail (namespace != NULL, NULL); + + repository = get_repository (repository); + + typelib = get_registered (repository, namespace, NULL); + + g_return_val_if_fail (typelib != NULL, NULL); + + header = (Header *) typelib->data; + if (header->shared_library) + return g_typelib_get_string (typelib, header->shared_library); + else + return NULL; +} + +/** + * g_irepository_get_c_prefix: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: Namespace to inspect + * + * This function returns the "C prefix", or the C level namespace + * associated with the given introspection namespace. Each C symbol + * starts with this prefix, as well each #GType in the library. + * + * Note: The namespace must have already been loaded using a function + * such as g_irepository_require() before calling this function. + * + * Returns: C namespace prefix, or %NULL if none associated + */ +const gchar * +g_irepository_get_c_prefix (GIRepository *repository, + const gchar *namespace_) +{ + GITypelib *typelib; + Header *header; + + g_return_val_if_fail (namespace_ != NULL, NULL); + + repository = get_repository (repository); + + typelib = get_registered (repository, namespace_, NULL); + + g_return_val_if_fail (typelib != NULL, NULL); + + header = (Header *) typelib->data; + if (header->c_prefix) + return g_typelib_get_string (typelib, header->c_prefix); + else + return NULL; +} + +/** + * g_irepository_get_typelib_path: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: GI namespace to use, e.g. "Gtk" + * + * If namespace @namespace_ is loaded, return the full path to the + * .typelib file it was loaded from. If the typelib for + * namespace @namespace_ was included in a shared library, return + * the special string "<builtin>". + * + * Returns: Filesystem path (or $lt;builtin$gt;) if successful, %NULL if namespace is not loaded + */ + +const gchar * +g_irepository_get_typelib_path (GIRepository *repository, + const gchar *namespace) +{ + gpointer orig_key, value; + + repository = get_repository (repository); + + if (!g_hash_table_lookup_extended (repository->priv->typelibs, namespace, + &orig_key, &value)) + { + if (!g_hash_table_lookup_extended (repository->priv->lazy_typelibs, namespace, + &orig_key, &value)) + + return NULL; + } + return ((char*)orig_key) + strlen ((char *) orig_key) + 1; +} + +/* This simple search function looks for a specified namespace-version; + it's faster than the full directory listing required for latest version. */ +static GMappedFile * +find_namespace_version (const gchar *namespace, + const gchar *version, + GSList *search_path, + gchar **path_ret) +{ + GSList *ldir; + GError *error = NULL; + GMappedFile *mfile = NULL; + char *fname; + + fname = g_strdup_printf ("%s-%s.typelib", namespace, version); + + for (ldir = search_path; ldir; ldir = ldir->next) + { + char *path = g_build_filename (ldir->data, fname, NULL); + + mfile = g_mapped_file_new (path, FALSE, &error); + if (error) + { + g_free (path); + g_clear_error (&error); + continue; + } + *path_ret = path; + break; + } + g_free (fname); + return mfile; +} + +static gboolean +parse_version (const char *version, + int *major, + int *minor) +{ + const char *dot; + char *end; + + *major = strtol (version, &end, 10); + dot = strchr (version, '.'); + if (dot == NULL) + { + *minor = 0; + return TRUE; + } + if (dot != end) + return FALSE; + *minor = strtol (dot+1, &end, 10); + if (end != (version + strlen (version))) + return FALSE; + return TRUE; +} + +static int +compare_version (const char *v1, + const char *v2) +{ + gboolean success; + int v1_major, v1_minor; + int v2_major, v2_minor; + + success = parse_version (v1, &v1_major, &v1_minor); + g_assert (success); + + success = parse_version (v2, &v2_major, &v2_minor); + g_assert (success); + + if (v1_major > v2_major) + return 1; + else if (v2_major > v1_major) + return -1; + else if (v1_minor > v2_minor) + return 1; + else if (v2_minor > v1_minor) + return -1; + return 0; +} + +struct NamespaceVersionCandidadate +{ + GMappedFile *mfile; + int path_index; + char *path; + char *version; +}; + +static int +compare_candidate_reverse (struct NamespaceVersionCandidadate *c1, + struct NamespaceVersionCandidadate *c2) +{ + int result = compare_version (c1->version, c2->version); + /* First, check the version */ + if (result > 0) + return -1; + else if (result < 0) + return 1; + else + { + /* Now check the path index, which says how early in the search path + * we found it. This ensures that of equal version targets, we + * pick the earlier one. + */ + if (c1->path_index == c2->path_index) + return 0; + else if (c1->path_index > c2->path_index) + return 1; + else + return -1; + } +} + +static void +free_candidate (struct NamespaceVersionCandidadate *candidate) +{ + g_mapped_file_unref (candidate->mfile); + g_free (candidate->path); + g_free (candidate->version); + g_slice_free (struct NamespaceVersionCandidadate, candidate); +} + +static GSList * +enumerate_namespace_versions (const gchar *namespace, + GSList *search_path) +{ + GSList *candidates = NULL; + GHashTable *found_versions = g_hash_table_new (g_str_hash, g_str_equal); + char *namespace_dash; + char *namespace_typelib; + GSList *ldir; + GError *error = NULL; + int index; + + namespace_dash = g_strdup_printf ("%s-", namespace); + namespace_typelib = g_strdup_printf ("%s.typelib", namespace); + + index = 0; + for (ldir = search_path; ldir; ldir = ldir->next) + { + GDir *dir; + const char *dirname; + const char *entry; + + dirname = (const char*)ldir->data; + dir = g_dir_open (dirname, 0, NULL); + if (dir == NULL) + continue; + while ((entry = g_dir_read_name (dir)) != NULL) + { + GMappedFile *mfile; + char *path, *version; + struct NamespaceVersionCandidadate *candidate; + + if (!g_str_has_suffix (entry, ".typelib")) + continue; + + if (g_str_has_prefix (entry, namespace_dash)) + { + const char *last_dash; + const char *name_end; + int major, minor; + + name_end = strrchr (entry, '.'); + last_dash = strrchr (entry, '-'); + version = g_strndup (last_dash+1, name_end-(last_dash+1)); + if (!parse_version (version, &major, &minor)) + { + g_free (version); + continue; + } + } + else + continue; + + if (g_hash_table_lookup (found_versions, version) != NULL) + { + g_free (version); + continue; + } + + path = g_build_filename (dirname, entry, NULL); + mfile = g_mapped_file_new (path, FALSE, &error); + if (mfile == NULL) + { + g_free (path); + g_free (version); + g_clear_error (&error); + continue; + } + candidate = g_slice_new0 (struct NamespaceVersionCandidadate); + candidate->mfile = mfile; + candidate->path_index = index; + candidate->path = path; + candidate->version = version; + candidates = g_slist_prepend (candidates, candidate); + g_hash_table_add (found_versions, version); + } + g_dir_close (dir); + index++; + } + + g_free (namespace_dash); + g_free (namespace_typelib); + g_hash_table_destroy (found_versions); + + return candidates; +} + +static GMappedFile * +find_namespace_latest (const gchar *namespace, + GSList *search_path, + gchar **version_ret, + gchar **path_ret) +{ + GSList *candidates; + GMappedFile *result = NULL; + + *version_ret = NULL; + *path_ret = NULL; + + candidates = enumerate_namespace_versions (namespace, search_path); + + if (candidates != NULL) + { + struct NamespaceVersionCandidadate *elected; + candidates = g_slist_sort (candidates, (GCompareFunc) compare_candidate_reverse); + + elected = (struct NamespaceVersionCandidadate *) candidates->data; + /* Remove the elected one so we don't try to free its contents */ + candidates = g_slist_delete_link (candidates, candidates); + + result = elected->mfile; + *path_ret = elected->path; + *version_ret = elected->version; + g_slice_free (struct NamespaceVersionCandidadate, elected); /* just free the container */ + g_slist_foreach (candidates, (GFunc) (void *) free_candidate, NULL); + g_slist_free (candidates); + } + return result; +} + +/** + * g_irepository_enumerate_versions: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: GI namespace, e.g. "Gtk" + * + * Obtain an unordered list of versions (either currently loaded or + * available) for @namespace_ in this @repository. + * + * Returns: (element-type utf8) (transfer full): the array of versions. + */ +GList * +g_irepository_enumerate_versions (GIRepository *repository, + const gchar *namespace_) +{ + GList *ret = NULL; + GSList *candidates, *link; + const gchar *loaded_version; + + init_globals (); + candidates = enumerate_namespace_versions (namespace_, typelib_search_path); + + for (link = candidates; link; link = link->next) + { + struct NamespaceVersionCandidadate *candidate = link->data; + ret = g_list_prepend (ret, g_strdup (candidate->version)); + free_candidate (candidate); + } + g_slist_free (candidates); + + /* The currently loaded version of a namespace is also part of the + * available versions, as it could have been loaded using + * require_private(). + */ + if (g_irepository_is_registered (repository, namespace_, NULL)) + { + loaded_version = g_irepository_get_version (repository, namespace_); + if (loaded_version && !g_list_find_custom (ret, loaded_version, g_str_equal)) + ret = g_list_prepend (ret, g_strdup (loaded_version)); + } + + return ret; +} + +static GITypelib * +require_internal (GIRepository *repository, + const gchar *namespace, + const gchar *version, + GIRepositoryLoadFlags flags, + GSList *search_path, + GError **error) +{ + GMappedFile *mfile; + GITypelib *ret = NULL; + Header *header; + GITypelib *typelib = NULL; + const gchar *typelib_namespace, *typelib_version; + gboolean allow_lazy = (flags & G_IREPOSITORY_LOAD_FLAG_LAZY) > 0; + gboolean is_lazy; + char *version_conflict = NULL; + char *path = NULL; + char *tmp_version = NULL; + + g_return_val_if_fail (namespace != NULL, FALSE); + + repository = get_repository (repository); + + typelib = get_registered_status (repository, namespace, version, allow_lazy, + &is_lazy, &version_conflict); + if (typelib) + return typelib; + + if (version_conflict != NULL) + { + g_set_error (error, G_IREPOSITORY_ERROR, + G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT, + "Requiring namespace '%s' version '%s', but '%s' is already loaded", + namespace, version, version_conflict); + return NULL; + } + + if (version != NULL) + { + mfile = find_namespace_version (namespace, version, + search_path, &path); + tmp_version = g_strdup (version); + } + else + { + mfile = find_namespace_latest (namespace, search_path, + &tmp_version, &path); + } + + if (mfile == NULL) + { + if (version != NULL) + g_set_error (error, G_IREPOSITORY_ERROR, + G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND, + "Typelib file for namespace '%s', version '%s' not found", + namespace, version); + else + g_set_error (error, G_IREPOSITORY_ERROR, + G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND, + "Typelib file for namespace '%s' (any version) not found", + namespace); + goto out; + } + + { + GError *temp_error = NULL; + typelib = g_typelib_new_from_mapped_file (mfile, &temp_error); + if (!typelib) + { + g_set_error (error, G_IREPOSITORY_ERROR, + G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND, + "Failed to load typelib file '%s' for namespace '%s': %s", + path, namespace, temp_error->message); + g_clear_error (&temp_error); + goto out; + } + } + header = (Header *) typelib->data; + typelib_namespace = g_typelib_get_string (typelib, header->namespace); + typelib_version = g_typelib_get_string (typelib, header->nsversion); + + if (strcmp (typelib_namespace, namespace) != 0) + { + g_set_error (error, G_IREPOSITORY_ERROR, + G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH, + "Typelib file %s for namespace '%s' contains " + "namespace '%s' which doesn't match the file name", + path, namespace, typelib_namespace); + g_typelib_free (typelib); + goto out; + } + if (version != NULL && strcmp (typelib_version, version) != 0) + { + g_set_error (error, G_IREPOSITORY_ERROR, + G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH, + "Typelib file %s for namespace '%s' contains " + "version '%s' which doesn't match the expected version '%s'", + path, namespace, typelib_version, version); + g_typelib_free (typelib); + goto out; + } + + if (!register_internal (repository, path, allow_lazy, + typelib, error)) + { + g_typelib_free (typelib); + goto out; + } + ret = typelib; + out: + g_free (tmp_version); + g_free (path); + return ret; +} + +/** + * g_irepository_require: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @namespace_: GI namespace to use, e.g. "Gtk" + * @version: (allow-none): Version of namespace, may be %NULL for latest + * @flags: Set of %GIRepositoryLoadFlags, may be 0 + * @error: a #GError. + * + * Force the namespace @namespace_ to be loaded if it isn't already. + * If @namespace_ is not loaded, this function will search for a + * ".typelib" file using the repository search path. In addition, a + * version @version of namespace may be specified. If @version is + * not specified, the latest will be used. + * + * Returns: (transfer none): a pointer to the #GITypelib if successful, %NULL otherwise + */ +GITypelib * +g_irepository_require (GIRepository *repository, + const gchar *namespace, + const gchar *version, + GIRepositoryLoadFlags flags, + GError **error) +{ + GITypelib *typelib; + + init_globals (); + typelib = require_internal (repository, namespace, version, flags, + typelib_search_path, error); + + return typelib; +} + +/** + * g_irepository_require_private: + * @repository: (allow-none): A #GIRepository or %NULL for the singleton + * process-global default #GIRepository + * @typelib_dir: Private directory where to find the requested typelib + * @namespace_: GI namespace to use, e.g. "Gtk" + * @version: (allow-none): Version of namespace, may be %NULL for latest + * @flags: Set of %GIRepositoryLoadFlags, may be 0 + * @error: a #GError. + * + * Force the namespace @namespace_ to be loaded if it isn't already. + * If @namespace_ is not loaded, this function will search for a + * ".typelib" file within the private directory only. In addition, a + * version @version of namespace should be specified. If @version is + * not specified, the latest will be used. + * + * Returns: (transfer none): a pointer to the #GITypelib if successful, %NULL otherwise + */ +GITypelib * +g_irepository_require_private (GIRepository *repository, + const gchar *typelib_dir, + const gchar *namespace, + const gchar *version, + GIRepositoryLoadFlags flags, + GError **error) +{ + GSList search_path = { (gpointer) typelib_dir, NULL }; + + return require_internal (repository, namespace, version, flags, + &search_path, error); +} + +static gboolean +g_irepository_introspect_cb (const char *option_name, + const char *value, + gpointer data, + GError **error) +{ + GError *tmp_error = NULL; + gboolean ret = g_irepository_dump (value, &tmp_error); + if (!ret) + { + g_error ("Failed to extract GType data: %s", + tmp_error->message); + exit (1); + } + exit (0); +} + +static const GOptionEntry introspection_args[] = { + { "introspect-dump", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, + g_irepository_introspect_cb, "Dump introspection information", + "infile.txt,outfile.xml" }, + { NULL } +}; + +/** + * g_irepository_get_option_group: + * + * Obtain the option group for girepository, it's used + * by the dumper and for programs that wants to provide + * introspection information + * + * Returns: (transfer full): the option group + */ +GOptionGroup * +g_irepository_get_option_group (void) +{ + GOptionGroup *group; + group = g_option_group_new ("girepository", "Introspection Options", "Show Introspection Options", NULL, NULL); + + g_option_group_add_entries (group, introspection_args); + return group; +} + +GQuark +g_irepository_error_quark (void) +{ + static GQuark quark = 0; + if (quark == 0) + quark = g_quark_from_static_string ("g-irepository-error-quark"); + return quark; +} + +/** + * g_type_tag_to_string: + * @type: the type_tag + * + * Obtain a string representation of @type + * + * Returns: the string + */ +const gchar* +g_type_tag_to_string (GITypeTag type) +{ + switch (type) + { + case GI_TYPE_TAG_VOID: + return "void"; + case GI_TYPE_TAG_BOOLEAN: + return "gboolean"; + case GI_TYPE_TAG_INT8: + return "gint8"; + case GI_TYPE_TAG_UINT8: + return "guint8"; + case GI_TYPE_TAG_INT16: + return "gint16"; + case GI_TYPE_TAG_UINT16: + return "guint16"; + case GI_TYPE_TAG_INT32: + return "gint32"; + case GI_TYPE_TAG_UINT32: + return "guint32"; + case GI_TYPE_TAG_INT64: + return "gint64"; + case GI_TYPE_TAG_UINT64: + return "guint64"; + case GI_TYPE_TAG_FLOAT: + return "gfloat"; + case GI_TYPE_TAG_DOUBLE: + return "gdouble"; + case GI_TYPE_TAG_UNICHAR: + return "gunichar"; + case GI_TYPE_TAG_GTYPE: + return "GType"; + case GI_TYPE_TAG_UTF8: + return "utf8"; + case GI_TYPE_TAG_FILENAME: + return "filename"; + case GI_TYPE_TAG_ARRAY: + return "array"; + case GI_TYPE_TAG_INTERFACE: + return "interface"; + case GI_TYPE_TAG_GLIST: + return "glist"; + case GI_TYPE_TAG_GSLIST: + return "gslist"; + case GI_TYPE_TAG_GHASH: + return "ghash"; + case GI_TYPE_TAG_ERROR: + return "error"; + default: + return "unknown"; + } +} + +/** + * g_info_type_to_string: + * @type: the info type + * + * Obtain a string representation of @type + * + * Returns: the string + */ +const gchar* +g_info_type_to_string (GIInfoType type) +{ + switch (type) + { + case GI_INFO_TYPE_INVALID: + return "invalid"; + case GI_INFO_TYPE_FUNCTION: + return "function"; + case GI_INFO_TYPE_CALLBACK: + return "callback"; + case GI_INFO_TYPE_STRUCT: + return "struct"; + case GI_INFO_TYPE_BOXED: + return "boxed"; + case GI_INFO_TYPE_ENUM: + return "enum"; + case GI_INFO_TYPE_FLAGS: + return "flags"; + case GI_INFO_TYPE_OBJECT: + return "object"; + case GI_INFO_TYPE_INTERFACE: + return "interface"; + case GI_INFO_TYPE_CONSTANT: + return "constant"; + case GI_INFO_TYPE_UNION: + return "union"; + case GI_INFO_TYPE_VALUE: + return "value"; + case GI_INFO_TYPE_SIGNAL: + return "signal"; + case GI_INFO_TYPE_VFUNC: + return "vfunc"; + case GI_INFO_TYPE_PROPERTY: + return "property"; + case GI_INFO_TYPE_FIELD: + return "field"; + case GI_INFO_TYPE_ARG: + return "arg"; + case GI_INFO_TYPE_TYPE: + return "type"; + case GI_INFO_TYPE_UNRESOLVED: + return "unresolved"; + default: + return "unknown"; + } +} diff --git a/girepository/girepository.h b/girepository/girepository.h new file mode 100644 index 000000000..073985b6e --- /dev/null +++ b/girepository/girepository.h @@ -0,0 +1,250 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Repository + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __G_IREPOSITORY_H__ +#define __G_IREPOSITORY_H__ + +#include +#include + +#define __GIREPOSITORY_H_INSIDE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define G_TYPE_IREPOSITORY (g_irepository_get_type ()) +#define G_IREPOSITORY(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), G_TYPE_IREPOSITORY, GIRepository)) +#define G_IREPOSITORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_IREPOSITORY, GIRepositoryClass)) +#define G_IS_IREPOSITORY(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), G_TYPE_IREPOSITORY)) +#define G_IS_IREPOSITORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_IREPOSITORY)) +#define G_IREPOSITORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_IREPOSITORY, GIRepositoryClass)) + +/** + * GIRepository: + * + * The GIRepository structure contains private data and should only be + * accessed using the provided API. + */ +typedef struct _GIRepository GIRepository; +typedef struct _GIRepositoryClass GIRepositoryClass; +typedef struct _GIRepositoryPrivate GIRepositoryPrivate; + +struct _GIRepository +{ + /*< private >*/ + GObject parent; + GIRepositoryPrivate *priv; +}; + +struct _GIRepositoryClass +{ + /*< private >*/ + GObjectClass parent; +}; + +/** + * GIRepositoryLoadFlags: + * @G_IREPOSITORY_LOAD_FLAG_LAZY: Lazily load the typelib. + * + * Flags that control how a typelib is loaded. + */ +typedef enum +{ + G_IREPOSITORY_LOAD_FLAG_LAZY = 1 << 0 +} GIRepositoryLoadFlags; + +/* Repository */ + +GI_AVAILABLE_IN_ALL +GType g_irepository_get_type (void) G_GNUC_CONST; + +GI_AVAILABLE_IN_ALL +GIRepository *g_irepository_get_default (void); + +GI_AVAILABLE_IN_ALL +void g_irepository_prepend_search_path (const char *directory); + +GI_AVAILABLE_IN_1_36 +void g_irepository_prepend_library_path (const char *directory); + +GI_AVAILABLE_IN_ALL +GSList * g_irepository_get_search_path (void); + +GI_AVAILABLE_IN_ALL +const char * g_irepository_load_typelib (GIRepository *repository, + GITypelib *typelib, + GIRepositoryLoadFlags flags, + GError **error); + +GI_AVAILABLE_IN_ALL +gboolean g_irepository_is_registered (GIRepository *repository, + const gchar *namespace_, + const gchar *version); + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_irepository_find_by_name (GIRepository *repository, + const gchar *namespace_, + const gchar *name); + +GI_AVAILABLE_IN_ALL +GList * g_irepository_enumerate_versions (GIRepository *repository, + const gchar *namespace_); + +GI_AVAILABLE_IN_ALL +GITypelib * g_irepository_require (GIRepository *repository, + const gchar *namespace_, + const gchar *version, + GIRepositoryLoadFlags flags, + GError **error); + +GI_AVAILABLE_IN_ALL +GITypelib * g_irepository_require_private (GIRepository *repository, + const gchar *typelib_dir, + const gchar *namespace_, + const gchar *version, + GIRepositoryLoadFlags flags, + GError **error); + +GI_AVAILABLE_IN_1_44 +gchar ** g_irepository_get_immediate_dependencies (GIRepository *repository, + const gchar *namespace_); + +GI_AVAILABLE_IN_ALL +gchar ** g_irepository_get_dependencies (GIRepository *repository, + const gchar *namespace_); + +GI_AVAILABLE_IN_ALL +gchar ** g_irepository_get_loaded_namespaces (GIRepository *repository); + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_irepository_find_by_gtype (GIRepository *repository, + GType gtype); + +GI_AVAILABLE_IN_1_60 +void g_irepository_get_object_gtype_interfaces (GIRepository *repository, + GType gtype, + guint *n_interfaces_out, + GIInterfaceInfo ***interfaces_out); + +GI_AVAILABLE_IN_ALL +gint g_irepository_get_n_infos (GIRepository *repository, + const gchar *namespace_); + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_irepository_get_info (GIRepository *repository, + const gchar *namespace_, + gint index); + +GI_AVAILABLE_IN_ALL +GIEnumInfo * g_irepository_find_by_error_domain (GIRepository *repository, + GQuark domain); + +GI_AVAILABLE_IN_ALL +const gchar * g_irepository_get_typelib_path (GIRepository *repository, + const gchar *namespace_); +GI_AVAILABLE_IN_ALL +const gchar * g_irepository_get_shared_library (GIRepository *repository, + const gchar *namespace_); +GI_AVAILABLE_IN_ALL +const gchar * g_irepository_get_c_prefix (GIRepository *repository, + const gchar *namespace_); +GI_AVAILABLE_IN_ALL +const gchar * g_irepository_get_version (GIRepository *repository, + const gchar *namespace_); + + +GI_AVAILABLE_IN_ALL +GOptionGroup * g_irepository_get_option_group (void); + + +GI_AVAILABLE_IN_ALL +gboolean g_irepository_dump (const char *arg, GError **error); + +/** + * GIRepositoryError: + * @G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND: the typelib could not be found. + * @G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH: the namespace does not match the + * requested namespace. + * @G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT: the version of the + * typelib does not match the requested version. + * @G_IREPOSITORY_ERROR_LIBRARY_NOT_FOUND: the library used by the typelib + * could not be found. + * + * An error code used with #G_IREPOSITORY_ERROR in a #GError returned + * from a #GIRepository routine. + */ +typedef enum +{ + G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND, + G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH, + G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT, + G_IREPOSITORY_ERROR_LIBRARY_NOT_FOUND +} GIRepositoryError; + +/** + * G_IREPOSITORY_ERROR: + * + * Error domain for #GIRepository. Errors in this domain will be from the + * #GIRepositoryError enumeration. See #GError for more information on + * error domains. + */ +#define G_IREPOSITORY_ERROR (g_irepository_error_quark ()) + +GI_AVAILABLE_IN_ALL +GQuark g_irepository_error_quark (void); + + +/* Global utility functions */ + +GI_AVAILABLE_IN_ALL +void gi_cclosure_marshal_generic (GClosure *closure, + GValue *return_gvalue, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +G_END_DECLS + + +#endif /* __G_IREPOSITORY_H__ */ + diff --git a/girepository/girffi.c b/girepository/girffi.c new file mode 100644 index 000000000..43d3f18f1 --- /dev/null +++ b/girepository/girffi.c @@ -0,0 +1,502 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Helper functions for ffi integration + * + * Copyright (C) 2008 Red Hat, Inc + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include "girffi.h" +#include "girepository.h" +#include "girepository-private.h" + +/** + * SECTION:girffi + * @short_description: TODO + * @title: girffi + * + * TODO + */ + +static ffi_type * +gi_type_tag_get_ffi_type_internal (GITypeTag tag, + gboolean is_pointer, + gboolean is_enum) +{ + switch (tag) + { + case GI_TYPE_TAG_BOOLEAN: + return &ffi_type_uint; + case GI_TYPE_TAG_INT8: + return &ffi_type_sint8; + case GI_TYPE_TAG_UINT8: + return &ffi_type_uint8; + case GI_TYPE_TAG_INT16: + return &ffi_type_sint16; + case GI_TYPE_TAG_UINT16: + return &ffi_type_uint16; + case GI_TYPE_TAG_INT32: + return &ffi_type_sint32; + case GI_TYPE_TAG_UINT32: + case GI_TYPE_TAG_UNICHAR: + return &ffi_type_uint32; + case GI_TYPE_TAG_INT64: + return &ffi_type_sint64; + case GI_TYPE_TAG_UINT64: + return &ffi_type_uint64; + case GI_TYPE_TAG_GTYPE: +#if GLIB_SIZEOF_SIZE_T == 4 + return &ffi_type_uint32; +#elif GLIB_SIZEOF_SIZE_T == 8 + return &ffi_type_uint64; +#else +# error "Unexpected size for size_t: not 4 or 8" +#endif + case GI_TYPE_TAG_FLOAT: + return &ffi_type_float; + case GI_TYPE_TAG_DOUBLE: + return &ffi_type_double; + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + case GI_TYPE_TAG_ARRAY: + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + case GI_TYPE_TAG_GHASH: + case GI_TYPE_TAG_ERROR: + return &ffi_type_pointer; + case GI_TYPE_TAG_INTERFACE: + { + /* We need to handle enums specially: + * https://bugzilla.gnome.org/show_bug.cgi?id=665150 + */ + if (!is_enum) + return &ffi_type_pointer; + else + return &ffi_type_sint32; + } + case GI_TYPE_TAG_VOID: + if (is_pointer) + return &ffi_type_pointer; + else + return &ffi_type_void; + default: + break; + } + + g_assert_not_reached (); + + return NULL; +} + +/** + * gi_type_tag_get_ffi_type: + * @type_tag: A #GITypeTag + * @is_pointer: Whether or not this is a pointer type + * + * TODO + * + * Returns: A #ffi_type corresponding to the platform default C ABI for @tag and @is_pointer. + */ +ffi_type * +gi_type_tag_get_ffi_type (GITypeTag type_tag, + gboolean is_pointer) +{ + return gi_type_tag_get_ffi_type_internal (type_tag, is_pointer, FALSE); +} + +/** + * g_type_info_get_ffi_type: + * @info: A #GITypeInfo + * + * TODO + * + * Returns: A #ffi_type corresponding to the platform default C ABI for @info. + */ +ffi_type * +g_type_info_get_ffi_type (GITypeInfo *info) +{ + gboolean is_enum = FALSE; + GIBaseInfo *iinfo; + + if (g_type_info_get_tag (info) == GI_TYPE_TAG_INTERFACE) + { + iinfo = g_type_info_get_interface (info); + switch (g_base_info_get_type (iinfo)) + { + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + is_enum = TRUE; + break; + default: + break; + } + g_base_info_unref (iinfo); + } + + return gi_type_tag_get_ffi_type_internal (g_type_info_get_tag (info), g_type_info_is_pointer (info), is_enum); +} + +/** + * g_callable_info_get_ffi_arg_types: + * @callable_info: a callable info from a typelib + * @n_args_p: (out): The number of arguments + * + * TODO + * + * Returns: an array of ffi_type*. The array itself + * should be freed using g_free() after use. + */ +static ffi_type ** +g_callable_info_get_ffi_arg_types (GICallableInfo *callable_info, + int *n_args_p) +{ + ffi_type **arg_types; + gboolean is_method, throws; + gint n_args, n_invoke_args, i, offset; + + g_return_val_if_fail (callable_info != NULL, NULL); + + n_args = g_callable_info_get_n_args (callable_info); + is_method = g_callable_info_is_method (callable_info); + throws = g_callable_info_can_throw_gerror (callable_info); + offset = is_method ? 1 : 0; + + n_invoke_args = n_args; + + if (is_method) + n_invoke_args++; + if (throws) + n_invoke_args++; + + if (n_args_p) + *n_args_p = n_invoke_args; + + arg_types = (ffi_type **) g_new0 (ffi_type *, n_invoke_args + 1); + + if (is_method) + arg_types[0] = &ffi_type_pointer; + if (throws) + arg_types[n_invoke_args - 1] = &ffi_type_pointer; + + for (i = 0; i < n_args; ++i) + { + GIArgInfo arg_info; + GITypeInfo arg_type; + + g_callable_info_load_arg (callable_info, i, &arg_info); + g_arg_info_load_type (&arg_info, &arg_type); + switch (g_arg_info_get_direction (&arg_info)) + { + case GI_DIRECTION_IN: + arg_types[i + offset] = g_type_info_get_ffi_type (&arg_type); + break; + case GI_DIRECTION_OUT: + case GI_DIRECTION_INOUT: + arg_types[i + offset] = &ffi_type_pointer; + break; + default: + g_assert_not_reached (); + } + } + + arg_types[n_invoke_args] = NULL; + + return arg_types; +} + +/** + * g_callable_info_get_ffi_return_type: + * @callable_info: a callable info from a typelib + * + * Fetches the ffi_type for a corresponding return value of + * a #GICallableInfo + * + * Returns: the ffi_type for the return value + */ +static ffi_type * +g_callable_info_get_ffi_return_type (GICallableInfo *callable_info) +{ + GITypeInfo *return_type; + ffi_type *return_ffi_type; + + g_return_val_if_fail (callable_info != NULL, NULL); + + return_type = g_callable_info_get_return_type (callable_info); + return_ffi_type = g_type_info_get_ffi_type (return_type); + g_base_info_unref((GIBaseInfo*)return_type); + + return return_ffi_type; +} + +/** + * g_function_info_prep_invoker: + * @info: A #GIFunctionInfo + * @invoker: Output invoker structure + * @error: A #GError + * + * Initialize the caller-allocated @invoker structure with a cache + * of information needed to invoke the C function corresponding to + * @info with the platform's default ABI. + * + * A primary intent of this function is that a dynamic structure allocated + * by a language binding could contain a #GIFunctionInvoker structure + * inside the binding's function mapping. + * + * Returns: %TRUE on success, %FALSE otherwise with @error set. + */ +gboolean +g_function_info_prep_invoker (GIFunctionInfo *info, + GIFunctionInvoker *invoker, + GError **error) +{ + const char *symbol; + gpointer addr; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (invoker != NULL, FALSE); + + symbol = g_function_info_get_symbol ((GIFunctionInfo*) info); + + if (!g_typelib_symbol (g_base_info_get_typelib((GIBaseInfo *) info), + symbol, &addr)) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_SYMBOL_NOT_FOUND, + "Could not locate %s: %s", symbol, g_module_error ()); + + return FALSE; + } + + return g_function_invoker_new_for_address (addr, info, invoker, error); +} + +/** + * g_function_invoker_new_for_address: + * @addr: The address + * @info: A #GICallableInfo + * @invoker: Output invoker structure + * @error: A #GError + * + * Initialize the caller-allocated @invoker structure with a cache + * of information needed to invoke the C function corresponding to + * @info with the platform's default ABI. + * + * A primary intent of this function is that a dynamic structure allocated + * by a language binding could contain a #GIFunctionInvoker structure + * inside the binding's function mapping. + * + * Returns: %TRUE on success, %FALSE otherwise with @error set. + */ +gboolean +g_function_invoker_new_for_address (gpointer addr, + GICallableInfo *info, + GIFunctionInvoker *invoker, + GError **error) +{ + ffi_type **atypes; + gint n_args; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (invoker != NULL, FALSE); + + invoker->native_address = addr; + + atypes = g_callable_info_get_ffi_arg_types (info, &n_args); + + return ffi_prep_cif (&(invoker->cif), FFI_DEFAULT_ABI, n_args, + g_callable_info_get_ffi_return_type (info), + atypes) == FFI_OK; +} + +/** + * g_function_invoker_destroy: + * @invoker: A #GIFunctionInvoker + * + * Release all resources allocated for the internals of @invoker; callers + * are responsible for freeing any resources allocated for the structure + * itself however. + */ +void +g_function_invoker_destroy (GIFunctionInvoker *invoker) +{ + g_free (invoker->cif.arg_types); +} + +typedef struct { + ffi_closure ffi_closure; + gpointer writable_self; + gpointer native_address; +} GIClosureWrapper; + +/** + * g_callable_info_create_closure: + * @callable_info: a callable info from a typelib + * @cif: a ffi_cif structure + * @callback: the ffi callback + * @user_data: data to be passed into the callback + * + * Prepares a callback for ffi invocation. + * + * Returns: the ffi_closure or NULL on error. The return value + * should be freed by calling g_callable_info_destroy_closure(). + */ +ffi_closure * +g_callable_info_create_closure (GICallableInfo *callable_info, + ffi_cif *cif, + GIFFIClosureCallback callback, + gpointer user_data) +{ + gpointer exec_ptr; + int n_args; + ffi_type **atypes; + GIClosureWrapper *closure; + ffi_status status; + + g_return_val_if_fail (callable_info != NULL, FALSE); + g_return_val_if_fail (cif != NULL, FALSE); + g_return_val_if_fail (callback != NULL, FALSE); + + closure = ffi_closure_alloc (sizeof (GIClosureWrapper), &exec_ptr); + if (!closure) + { + g_warning ("could not allocate closure\n"); + return NULL; + } + closure->writable_self = closure; + closure->native_address = exec_ptr; + + + atypes = g_callable_info_get_ffi_arg_types (callable_info, &n_args); + status = ffi_prep_cif (cif, FFI_DEFAULT_ABI, n_args, + g_callable_info_get_ffi_return_type (callable_info), + atypes); + if (status != FFI_OK) + { + g_warning ("ffi_prep_cif failed: %d\n", status); + ffi_closure_free (closure); + return NULL; + } + + status = ffi_prep_closure_loc (&closure->ffi_closure, cif, callback, user_data, exec_ptr); + if (status != FFI_OK) + { + g_warning ("ffi_prep_closure failed: %d\n", status); + ffi_closure_free (closure); + return NULL; + } + + return &closure->ffi_closure; +} + +/** + * g_callable_info_get_closure_native_address: + * @callable_info: a callable info from a typelib + * @closure: ffi closure + * + * Gets callable code from ffi_closure prepared by g_callable_info_create_closure() + */ +gpointer * +g_callable_info_get_closure_native_address (GICallableInfo *callable_info, + ffi_closure *closure) +{ + GIClosureWrapper *wrapper = (GIClosureWrapper *)closure; + return wrapper->native_address; +} + +/** + * g_callable_info_destroy_closure: + * @callable_info: a callable info from a typelib + * @closure: ffi closure + * + * Frees a ffi_closure returned from g_callable_info_create_closure() + */ +void +g_callable_info_destroy_closure (GICallableInfo *callable_info, + ffi_closure *closure) +{ + GIClosureWrapper *wrapper = (GIClosureWrapper *)closure; + + g_free (wrapper->ffi_closure.cif->arg_types); + ffi_closure_free (wrapper->writable_self); +} + +/** + * g_callable_info_prepare_closure: + * @callable_info: a callable info from a typelib + * @cif: a ffi_cif structure + * @callback: the ffi callback + * @user_data: data to be passed into the callback + * + * Prepares a callback for ffi invocation. + * + * Deprecated: 1.72: Use g_callable_info_create_closure() instead + * + * Returns: the native address of the closure or `NULL` on error. The return value + * should be freed by calling g_callable_info_free_closure(). + */ +ffi_closure * +g_callable_info_prepare_closure (GICallableInfo *callable_info, + ffi_cif *cif, + GIFFIClosureCallback callback, + gpointer user_data) +{ + ffi_closure * closure; + + closure = g_callable_info_create_closure (callable_info, cif, callback, user_data); + if (!closure) + { + return NULL; + } + + /* Return the native pointer which, on some systems and ffi versions without static exec trampolines, + * points to the same underlying memory as closure, but via an executable-non-writable mapping. + * Deprecated, and kept for backwards compatibility only. Risks segfaults on freeing the closure. + */ + return (ffi_closure *) g_callable_info_get_closure_native_address(callable_info, closure); +} + +/** + * g_callable_info_free_closure: + * @callable_info: a callable info from a typelib + * @closure: ffi closure + * + * Deprecated: 1.72: Use g_callable_info_destroy_closure() instead, in + * conjunction with g_callable_info_create_closure(). + * + * Should free a ffi_closure returned from g_callable_info_prepare_closure(), + * which may cause a segfault because the native address is returned instead of + * the closure address. May do nothing and leak memory instead of freeing to + * avoid segfaults. + */ +void +g_callable_info_free_closure (GICallableInfo *callable_info, + ffi_closure *closure) +{ +#ifdef LEGACY_GIRFFI_FREE + g_callable_info_destroy_closure(callable_info, closure); +#endif +} diff --git a/girepository/girffi.h b/girepository/girffi.h new file mode 100644 index 000000000..dbe4c1ecb --- /dev/null +++ b/girepository/girffi.h @@ -0,0 +1,126 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Helper functions for ffi integration + * + * Copyright (C) 2008 Red Hat, Inc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIRFFI_H__ +#define __GIRFFI_H__ + +#include +#include "girepository.h" + +G_BEGIN_DECLS + +/** + * GIFFIClosureCallback: + * @Param1: TODO + * @Param2: TODO + * @Param3: TODO + * @Param4: TODO + * + * TODO + */ +typedef void (*GIFFIClosureCallback) (ffi_cif *, + void *, + void **, + void *); + +/** + * GIFunctionInvoker: + * @cif: the cif + * @native_address: the native address + * + * TODO + */ +typedef struct _GIFunctionInvoker GIFunctionInvoker; + +struct _GIFunctionInvoker { + ffi_cif cif; + gpointer native_address; + /*< private >*/ + gpointer padding[3]; +}; + +/** + * GIFFIReturnValue: + * + * TODO + */ +typedef GIArgument GIFFIReturnValue; + +GI_AVAILABLE_IN_ALL +ffi_type * gi_type_tag_get_ffi_type (GITypeTag type_tag, gboolean is_pointer); + +GI_AVAILABLE_IN_ALL +ffi_type * g_type_info_get_ffi_type (GITypeInfo *info); + +GI_AVAILABLE_IN_1_32 +void gi_type_info_extract_ffi_return_value (GITypeInfo *return_info, + GIFFIReturnValue *ffi_value, + GIArgument *arg); + +GI_AVAILABLE_IN_1_72 +void gi_type_tag_extract_ffi_return_value (GITypeTag return_tag, + GIInfoType interface_type, + GIFFIReturnValue *ffi_value, + GIArgument *arg); + +GI_AVAILABLE_IN_ALL +gboolean g_function_info_prep_invoker (GIFunctionInfo *info, + GIFunctionInvoker *invoker, + GError **error); + +GI_AVAILABLE_IN_1_32 +gboolean g_function_invoker_new_for_address (gpointer addr, + GICallableInfo *info, + GIFunctionInvoker *invoker, + GError **error); + +GI_AVAILABLE_IN_ALL +void g_function_invoker_destroy (GIFunctionInvoker *invoker); + + +GI_DEPRECATED_IN_1_72_FOR(g_callable_info_create_closure) +ffi_closure * g_callable_info_prepare_closure (GICallableInfo *callable_info, + ffi_cif *cif, + GIFFIClosureCallback callback, + gpointer user_data); + +GI_DEPRECATED_IN_1_72_FOR(g_callable_info_destroy_closure) +void g_callable_info_free_closure (GICallableInfo *callable_info, + ffi_closure *closure); + + +GI_AVAILABLE_IN_1_72 +ffi_closure * g_callable_info_create_closure (GICallableInfo *callable_info, + ffi_cif *cif, + GIFFIClosureCallback callback, + gpointer user_data); + +GI_AVAILABLE_IN_1_72 +gpointer * g_callable_info_get_closure_native_address (GICallableInfo *callable_info, + ffi_closure *closure); + +GI_AVAILABLE_IN_1_72 +void g_callable_info_destroy_closure (GICallableInfo *callable_info, + ffi_closure *closure); + +G_END_DECLS + +#endif /* __GIRFFI_H__ */ diff --git a/girepository/girmodule.c b/girepository/girmodule.c new file mode 100644 index 000000000..943db971d --- /dev/null +++ b/girepository/girmodule.c @@ -0,0 +1,577 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Typelib creation + * + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#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, + const gchar *version, + const gchar *shared_library, + const gchar *c_prefix) +{ + GIrModule *module; + + module = g_slice_new0 (GIrModule); + + module->name = g_strdup (name); + module->version = g_strdup (version); + if (shared_library) + module->shared_library = g_strdup (shared_library); + else + module->shared_library = NULL; + module->c_prefix = g_strdup (c_prefix); + module->dependencies = NULL; + module->entries = NULL; + + module->include_modules = NULL; + module->aliases = NULL; + + return module; +} + +void +_g_ir_module_free (GIrModule *module) +{ + GList *e; + + g_free (module->name); + + for (e = module->entries; e; e = e->next) + _g_ir_node_free ((GIrNode *)e->data); + + g_list_free (module->entries); + /* Don't free dependencies, we inherit that from the parser */ + + g_list_free (module->include_modules); + + g_hash_table_destroy (module->aliases); + g_hash_table_destroy (module->pointer_structures); + g_hash_table_destroy (module->disguised_structures); + + g_slice_free (GIrModule, module); +} + +/** + * _g_ir_module_fatal: + * @build: Current build + * @line: Origin line number, or 0 if unknown + * @msg: printf-format string + * @args: Remaining arguments + * + * Report a fatal error, then exit. + */ +void +_g_ir_module_fatal (GIrTypelibBuild *build, + guint line, + const char *msg, + ...) +{ + GString *context; + char *formatted; + GList *link; + + va_list args; + + va_start (args, msg); + + formatted = g_strdup_vprintf (msg, args); + + context = g_string_new (""); + if (line > 0) + g_string_append_printf (context, "%d: ", line); + if (build->stack) + g_string_append (context, "In "); + for (link = g_list_last (build->stack); link; link = link->prev) + { + GIrNode *node = link->data; + const char *name = node->name; + if (name) + g_string_append (context, name); + if (link->prev) + g_string_append (context, "."); + } + if (build->stack) + g_string_append (context, ": "); + + g_printerr ("%s-%s.gir:%serror: %s\n", build->module->name, + build->module->version, + context->str, formatted); + g_string_free (context, TRUE); + + exit (1); + + va_end (args); +} + +static void +add_alias_foreach (gpointer key, + gpointer value, + gpointer data) +{ + GIrModule *module = data; + + g_hash_table_replace (module->aliases, g_strdup (key), g_strdup (value)); +} + +static void +add_pointer_structure_foreach (gpointer key, + gpointer value, + gpointer data) +{ + GIrModule *module = data; + + g_hash_table_replace (module->pointer_structures, g_strdup (key), value); +} + +static void +add_disguised_structure_foreach (gpointer key, + gpointer value, + gpointer data) +{ + GIrModule *module = data; + + g_hash_table_replace (module->disguised_structures, g_strdup (key), value); +} + +void +_g_ir_module_add_include_module (GIrModule *module, + GIrModule *include_module) +{ + module->include_modules = g_list_prepend (module->include_modules, + include_module); + + g_hash_table_foreach (include_module->aliases, + add_alias_foreach, + module); + + g_hash_table_foreach (include_module->pointer_structures, + add_pointer_structure_foreach, + module); + g_hash_table_foreach (include_module->disguised_structures, + add_disguised_structure_foreach, + module); +} + +struct AttributeWriteData +{ + guint count; + guchar *databuf; + GIrNode *node; + GHashTable *strings; + guint32 *offset; + guint32 *offset2; +}; + +static void +write_attribute (gpointer key, gpointer value, gpointer datap) +{ + struct AttributeWriteData *data = datap; + guint32 old_offset = *(data->offset); + AttributeBlob *blob = (AttributeBlob*)&(data->databuf[old_offset]); + + *(data->offset) += sizeof (AttributeBlob); + + blob->offset = data->node->offset; + blob->name = _g_ir_write_string ((const char*) key, data->strings, data->databuf, data->offset2); + blob->value = _g_ir_write_string ((const char*) value, data->strings, data->databuf, data->offset2); + + data->count++; +} + +static guint +write_attributes (GIrModule *module, + GIrNode *node, + GHashTable *strings, + guchar *data, + guint32 *offset, + guint32 *offset2) +{ + struct AttributeWriteData wdata; + wdata.count = 0; + wdata.databuf = data; + wdata.node = node; + wdata.offset = offset; + wdata.offset2 = offset2; + wdata.strings = strings; + + g_hash_table_foreach (node->attributes, write_attribute, &wdata); + + return wdata.count; +} + +static gint +node_cmp_offset_func (gconstpointer a, + gconstpointer b) +{ + const GIrNode *na = a; + const GIrNode *nb = b; + 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++) + { + const char *str; + entry = (DirEntry *)&data[header->directory + (i * header->entry_blob_size)]; + 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); + required_size = ALIGN_VALUE (required_size, 4); + + new_offset = *offset2 + required_size; + + 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) +{ + GError *error = NULL; + GITypelib *typelib; + gsize length; + guint i; + GList *e; + Header *header; + DirEntry *entry; + guint32 header_size; + guint32 dir_size; + guint32 n_entries; + guint32 n_local_entries; + guint32 size, offset, offset2, old_offset; + GHashTable *strings; + GHashTable *types; + 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); + + /* Serialize dependencies into one string; this is convenient + * and not a major change to the typelib format. */ + { + GString *dependencies_str = g_string_new (""); + GList *link; + for (link = module->dependencies; link; link = link->next) + { + const char *dependency = link->data; + if (!strcmp (dependency, module->name)) + continue; + g_string_append (dependencies_str, dependency); + if (link->next) + g_string_append_c (dependencies_str, '|'); + } + dependencies = g_string_free (dependencies_str, FALSE); + if (!dependencies[0]) + { + g_free (dependencies); + dependencies = NULL; + } + } + + restart: + _g_irnode_init_stats (); + strings = g_hash_table_new (g_str_hash, g_str_equal); + types = g_hash_table_new (g_str_hash, g_str_equal); + nodes_with_attributes = NULL; + n_entries = g_list_length (module->entries); + + g_message ("%d entries (%d local), %d dependencies\n", n_entries, n_local_entries, + g_list_length (module->dependencies)); + + dir_size = n_entries * sizeof (DirEntry); + size = header_size + dir_size; + + size += ALIGN_VALUE (strlen (module->name) + 1, 4); + + for (e = module->entries; e; e = e->next) + { + GIrNode *node = e->data; + + size += _g_ir_node_get_full_size (node); + + /* Also reset the offset here */ + node->offset = 0; + } + + /* Adjust size for strings allocated in header below specially */ + size += ALIGN_VALUE (strlen (module->name) + 1, 4); + if (module->shared_library) + size += ALIGN_VALUE (strlen (module->shared_library) + 1, 4); + if (dependencies != NULL) + size += ALIGN_VALUE (strlen (dependencies) + 1, 4); + 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); + + data = g_malloc0 (size); + + /* fill in header */ + header = (Header *)data; + memcpy (header, G_IR_MAGIC, 16); + header->major_version = 4; + header->minor_version = 0; + header->reserved = 0; + header->n_entries = n_entries; + header->n_local_entries = n_local_entries; + header->n_attributes = 0; + header->attributes = 0; /* filled in later */ + /* NOTE: When writing strings to the typelib here, you should also update + * the size calculations above. + */ + if (dependencies != NULL) + header->dependencies = _g_ir_write_string (dependencies, strings, data, &header_size); + else + header->dependencies = 0; + header->size = 0; /* filled in later */ + header->namespace = _g_ir_write_string (module->name, strings, data, &header_size); + header->nsversion = _g_ir_write_string (module->version, strings, data, &header_size); + header->shared_library = (module->shared_library? + _g_ir_write_string (module->shared_library, strings, data, &header_size) + : 0); + if (module->c_prefix != NULL) + header->c_prefix = _g_ir_write_string (module->c_prefix, strings, data, &header_size); + else + header->c_prefix = 0; + header->entry_blob_size = sizeof (DirEntry); + header->function_blob_size = sizeof (FunctionBlob); + header->callback_blob_size = sizeof (CallbackBlob); + header->signal_blob_size = sizeof (SignalBlob); + header->vfunc_blob_size = sizeof (VFuncBlob); + header->arg_blob_size = sizeof (ArgBlob); + header->property_blob_size = sizeof (PropertyBlob); + header->field_blob_size = sizeof (FieldBlob); + header->value_blob_size = sizeof (ValueBlob); + header->constant_blob_size = sizeof (ConstantBlob); + header->error_domain_blob_size = 16; /* No longer used */ + header->attribute_blob_size = sizeof (AttributeBlob); + header->signature_blob_size = sizeof (SignatureBlob); + header->enum_blob_size = sizeof (EnumBlob); + header->struct_blob_size = sizeof (StructBlob); + header->object_blob_size = sizeof(ObjectBlob); + 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 += dir_size; + + for (e = module->entries, i = 0; e; e = e->next, i++) + { + GIrTypelibBuild build; + GIrNode *node = e->data; + + if (strchr (node->name, '.')) + { + g_error ("Names may not contain '.'"); + } + + /* we picked up implicit xref nodes, start over */ + if (i == n_entries) + { + GList *link; + g_message ("Found implicit cross references, starting over"); + + g_hash_table_destroy (strings); + g_hash_table_destroy (types); + + /* Reset the cached offsets */ + for (link = nodes_with_attributes; link; link = link->next) + ((GIrNode *) link->data)->offset = 0; + + g_list_free (nodes_with_attributes); + strings = NULL; + + g_free (data); + data = NULL; + + goto restart; + } + + offset = offset2; + + if (node->type == G_IR_NODE_XREF) + { + const char *namespace = ((GIrNodeXRef*)node)->namespace; + + entry->blob_type = 0; + entry->local = FALSE; + entry->offset = _g_ir_write_string (namespace, strings, data, &offset2); + entry->name = _g_ir_write_string (node->name, strings, data, &offset2); + } + else + { + old_offset = offset; + offset2 = offset + _g_ir_node_get_size (node); + + entry->blob_type = node->type; + entry->local = TRUE; + entry->offset = offset; + entry->name = _g_ir_write_string (node->name, strings, data, &offset2); + + memset (&build, 0, sizeof (build)); + build.module = module; + build.strings = strings; + build.types = types; + build.nodes_with_attributes = nodes_with_attributes; + build.n_attributes = header->n_attributes; + build.data = data; + _g_ir_node_build_typelib (node, NULL, &build, &offset, &offset2, NULL); + + nodes_with_attributes = build.nodes_with_attributes; + header->n_attributes = build.n_attributes; + + if (offset2 > old_offset + _g_ir_node_get_full_size (node)) + g_error ("left a hole of %d bytes\n", offset2 - old_offset - _g_ir_node_get_full_size (node)); + } + + entry++; + } + + /* GIBaseInfo expects the AttributeBlob array to be sorted on the field (offset) */ + nodes_with_attributes = g_list_sort (nodes_with_attributes, node_cmp_offset_func); + + g_message ("header: %d entries, %d attributes", header->n_entries, header->n_attributes); + + _g_irnode_dump_stats (); + + /* Write attributes after the blobs */ + offset = offset2; + header->attributes = offset; + offset2 = offset + header->n_attributes * header->attribute_blob_size; + + for (e = nodes_with_attributes; e; e = e->next) + { + GIrNode *node = e->data; + write_attributes (module, node, strings, data, &offset, &offset2); + } + + g_message ("reallocating to %d bytes", offset2); + + 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) + { + g_error ("error building typelib: %s", + error->message); + } + + g_hash_table_destroy (strings); + g_hash_table_destroy (types); + g_list_free (nodes_with_attributes); + + return typelib; +} + diff --git a/girepository/girmodule.h b/girepository/girmodule.h new file mode 100644 index 000000000..fca7ebbf6 --- /dev/null +++ b/girepository/girmodule.h @@ -0,0 +1,86 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Parsed IDL + * + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __G_IR_MODULE_H__ +#define __G_IR_MODULE_H__ + +#include +#include "gitypelib-internal.h" + +G_BEGIN_DECLS + +typedef struct _GIrTypelibBuild GIrTypelibBuild; +typedef struct _GIrModule GIrModule; + +struct _GIrTypelibBuild { + GIrModule *module; + GHashTable *strings; + GHashTable *types; + GList *nodes_with_attributes; + guint32 n_attributes; + guchar *data; + GList *stack; +}; + +struct _GIrModule +{ + gchar *name; + gchar *version; + gchar *shared_library; + gchar *c_prefix; + GList *dependencies; + GList *entries; + + /* All modules that are included directly or indirectly */ + GList *include_modules; + + /* Aliases defined in the module or in included modules */ + GHashTable *aliases; + + /* Structures with the 'pointer' flag (typedef struct _X *X) + * in the module or in included modules + */ + GHashTable *pointer_structures; + /* Same as 'pointer' structures, but with the deprecated + * 'disguised' flag + */ + GHashTable *disguised_structures; +}; + +GIrModule *_g_ir_module_new (const gchar *name, + const gchar *nsversion, + const gchar *module_filename, + const gchar *c_prefix); +void _g_ir_module_free (GIrModule *module); + +void _g_ir_module_add_include_module (GIrModule *module, + GIrModule *include_module); + +GITypelib * _g_ir_module_build_typelib (GIrModule *module); + +void _g_ir_module_fatal (GIrTypelibBuild *build, guint line, const char *msg, ...) G_GNUC_PRINTF (3, 4) G_GNUC_NORETURN; + +void _g_irnode_init_stats (void); +void _g_irnode_dump_stats (void); + +G_END_DECLS + +#endif /* __G_IR_MODULE_H__ */ diff --git a/girepository/girnode.c b/girepository/girnode.c new file mode 100644 index 000000000..4a8d78b93 --- /dev/null +++ b/girepository/girnode.c @@ -0,0 +1,2441 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Typelib creation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "girmodule.h" +#include "girnode.h" +#include "gitypelib-internal.h" + +#ifdef _MSC_VER +#define strtoll _strtoi64 +#define strtoull _strtoui64 +#endif + +static gulong string_count = 0; +static gulong unique_string_count = 0; +static gulong string_size = 0; +static gulong unique_string_size = 0; +static gulong types_count = 0; +static gulong unique_types_count = 0; + +void +_g_irnode_init_stats (void) +{ + string_count = 0; + unique_string_count = 0; + string_size = 0; + unique_string_size = 0; + types_count = 0; + unique_types_count = 0; +} + +void +_g_irnode_dump_stats (void) +{ + g_message ("%lu strings (%lu before sharing), %lu bytes (%lu before sharing)", + unique_string_count, string_count, unique_string_size, string_size); + g_message ("%lu types (%lu before sharing)", unique_types_count, types_count); +} + +#define DO_ALIGNED_COPY(dest_addr, value, type) \ +do { \ + type tmp_var; \ + tmp_var = value; \ + memcpy(dest_addr, &tmp_var, sizeof(type)); \ +} while(0) + +#define ALIGN_VALUE(this, boundary) \ + (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1))) + + +const gchar * +_g_ir_node_type_to_string (GIrNodeTypeId type) +{ + switch (type) + { + case G_IR_NODE_FUNCTION: + return "function"; + case G_IR_NODE_CALLBACK: + return "callback"; + case G_IR_NODE_PARAM: + return "param"; + case G_IR_NODE_TYPE: + return "type"; + case G_IR_NODE_OBJECT: + return "object"; + case G_IR_NODE_INTERFACE: + return "interface"; + case G_IR_NODE_SIGNAL: + return "signal"; + case G_IR_NODE_PROPERTY: + return "property"; + case G_IR_NODE_VFUNC: + return "vfunc"; + case G_IR_NODE_FIELD: + return "field"; + case G_IR_NODE_ENUM: + return "enum"; + case G_IR_NODE_FLAGS: + return "flags"; + case G_IR_NODE_BOXED: + return "boxed"; + case G_IR_NODE_STRUCT: + return "struct"; + case G_IR_NODE_VALUE: + return "value"; + case G_IR_NODE_CONSTANT: + return "constant"; + case G_IR_NODE_XREF: + return "xref"; + case G_IR_NODE_UNION: + return "union"; + default: + return "unknown"; + } +} + +GIrNode * +_g_ir_node_new (GIrNodeTypeId type, + GIrModule *module) +{ + GIrNode *node = NULL; + + switch (type) + { + case G_IR_NODE_FUNCTION: + case G_IR_NODE_CALLBACK: + node = g_malloc0 (sizeof (GIrNodeFunction)); + break; + + case G_IR_NODE_PARAM: + node = g_malloc0 (sizeof (GIrNodeParam)); + break; + + case G_IR_NODE_TYPE: + node = g_malloc0 (sizeof (GIrNodeType)); + break; + + case G_IR_NODE_OBJECT: + case G_IR_NODE_INTERFACE: + node = g_malloc0 (sizeof (GIrNodeInterface)); + break; + + case G_IR_NODE_SIGNAL: + node = g_malloc0 (sizeof (GIrNodeSignal)); + break; + + case G_IR_NODE_PROPERTY: + node = g_malloc0 (sizeof (GIrNodeProperty)); + break; + + case G_IR_NODE_VFUNC: + node = g_malloc0 (sizeof (GIrNodeFunction)); + break; + + case G_IR_NODE_FIELD: + node = g_malloc0 (sizeof (GIrNodeField)); + break; + + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + node = g_malloc0 (sizeof (GIrNodeEnum)); + break; + + case G_IR_NODE_BOXED: + node = g_malloc0 (sizeof (GIrNodeBoxed)); + break; + + case G_IR_NODE_STRUCT: + node = g_malloc0 (sizeof (GIrNodeStruct)); + break; + + case G_IR_NODE_VALUE: + node = g_malloc0 (sizeof (GIrNodeValue)); + break; + + case G_IR_NODE_CONSTANT: + node = g_malloc0 (sizeof (GIrNodeConstant)); + break; + + case G_IR_NODE_XREF: + node = g_malloc0 (sizeof (GIrNodeXRef)); + break; + + case G_IR_NODE_UNION: + node = g_malloc0 (sizeof (GIrNodeUnion)); + break; + + default: + g_error ("Unhandled node type %d\n", type); + break; + } + + node->type = type; + node->module = module; + node->offset = 0; + node->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, g_free); + + return node; +} + +void +_g_ir_node_free (GIrNode *node) +{ + GList *l; + + if (node == NULL) + return; + + switch (node->type) + { + case G_IR_NODE_FUNCTION: + case G_IR_NODE_CALLBACK: + { + GIrNodeFunction *function = (GIrNodeFunction *)node; + + g_free (node->name); + g_free (function->symbol); + g_free (function->property); + _g_ir_node_free ((GIrNode *)function->result); + for (l = function->parameters; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (function->parameters); + } + break; + + case G_IR_NODE_TYPE: + { + GIrNodeType *type = (GIrNodeType *)node; + + g_free (node->name); + _g_ir_node_free ((GIrNode *)type->parameter_type1); + _g_ir_node_free ((GIrNode *)type->parameter_type2); + + g_free (type->giinterface); + g_strfreev (type->errors); + + } + break; + + case G_IR_NODE_PARAM: + { + GIrNodeParam *param = (GIrNodeParam *)node; + + g_free (node->name); + _g_ir_node_free ((GIrNode *)param->type); + } + break; + + case G_IR_NODE_PROPERTY: + { + GIrNodeProperty *property = (GIrNodeProperty *)node; + + g_free (node->name); + g_free (property->setter); + g_free (property->getter); + _g_ir_node_free ((GIrNode *)property->type); + } + break; + + case G_IR_NODE_SIGNAL: + { + GIrNodeSignal *signal = (GIrNodeSignal *)node; + + g_free (node->name); + for (l = signal->parameters; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (signal->parameters); + _g_ir_node_free ((GIrNode *)signal->result); + } + break; + + case G_IR_NODE_VFUNC: + { + GIrNodeVFunc *vfunc = (GIrNodeVFunc *)node; + + g_free (node->name); + g_free (vfunc->invoker); + for (l = vfunc->parameters; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (vfunc->parameters); + _g_ir_node_free ((GIrNode *)vfunc->result); + } + break; + + case G_IR_NODE_FIELD: + { + GIrNodeField *field = (GIrNodeField *)node; + + g_free (node->name); + _g_ir_node_free ((GIrNode *)field->type); + _g_ir_node_free ((GIrNode *)field->callback); + } + break; + + case G_IR_NODE_OBJECT: + case G_IR_NODE_INTERFACE: + { + GIrNodeInterface *iface = (GIrNodeInterface *)node; + + g_free (node->name); + g_free (iface->gtype_name); + g_free (iface->gtype_init); + g_free (iface->ref_func); + g_free (iface->unref_func); + g_free (iface->set_value_func); + g_free (iface->get_value_func); + + + g_free (iface->glib_type_struct); + g_free (iface->parent); + + for (l = iface->interfaces; l; l = l->next) + g_free ((GIrNode *)l->data); + g_list_free (iface->interfaces); + + for (l = iface->members; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (iface->members); + + } + break; + + case G_IR_NODE_VALUE: + { + g_free (node->name); + } + break; + + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + { + GIrNodeEnum *enum_ = (GIrNodeEnum *)node; + + g_free (node->name); + g_free (enum_->gtype_name); + g_free (enum_->gtype_init); + g_free (enum_->error_domain); + + for (l = enum_->values; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (enum_->values); + + for (l = enum_->methods; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (enum_->methods); + } + break; + + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed = (GIrNodeBoxed *)node; + + g_free (node->name); + g_free (boxed->gtype_name); + g_free (boxed->gtype_init); + + for (l = boxed->members; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (boxed->members); + } + break; + + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_ = (GIrNodeStruct *)node; + + g_free (node->name); + g_free (struct_->gtype_name); + g_free (struct_->gtype_init); + g_free (struct_->copy_func); + g_free (struct_->free_func); + + for (l = struct_->members; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + g_list_free (struct_->members); + } + break; + + case G_IR_NODE_CONSTANT: + { + GIrNodeConstant *constant = (GIrNodeConstant *)node; + + g_free (node->name); + g_free (constant->value); + _g_ir_node_free ((GIrNode *)constant->type); + } + break; + + case G_IR_NODE_XREF: + { + GIrNodeXRef *xref = (GIrNodeXRef *)node; + + g_free (node->name); + g_free (xref->namespace); + } + break; + + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_ = (GIrNodeUnion *)node; + + g_free (node->name); + g_free (union_->gtype_name); + g_free (union_->gtype_init); + g_free (union_->copy_func); + g_free (union_->free_func); + + _g_ir_node_free ((GIrNode *)union_->discriminator_type); + for (l = union_->members; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + for (l = union_->discriminators; l; l = l->next) + _g_ir_node_free ((GIrNode *)l->data); + } + break; + + default: + g_error ("Unhandled node type %d\n", node->type); + break; + } + + g_hash_table_destroy (node->attributes); + + g_free (node); +} + +/* returns the fixed size of the blob */ +guint32 +_g_ir_node_get_size (GIrNode *node) +{ + GList *l; + gint size, n; + + switch (node->type) + { + case G_IR_NODE_CALLBACK: + size = sizeof (CallbackBlob); + break; + + case G_IR_NODE_FUNCTION: + size = sizeof (FunctionBlob); + break; + + case G_IR_NODE_PARAM: + /* See the comment in the G_IR_NODE_PARAM/ArgBlob writing below */ + size = sizeof (ArgBlob) - sizeof (SimpleTypeBlob); + break; + + case G_IR_NODE_TYPE: + size = sizeof (SimpleTypeBlob); + break; + + case G_IR_NODE_OBJECT: + { + GIrNodeInterface *iface = (GIrNodeInterface *)node; + + n = g_list_length (iface->interfaces); + size = sizeof (ObjectBlob) + 2 * (n + (n % 2)); + + for (l = iface->members; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + } + break; + + case G_IR_NODE_INTERFACE: + { + GIrNodeInterface *iface = (GIrNodeInterface *)node; + + n = g_list_length (iface->prerequisites); + size = sizeof (InterfaceBlob) + 2 * (n + (n % 2)); + + for (l = iface->members; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + } + break; + + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + { + GIrNodeEnum *enum_ = (GIrNodeEnum *)node; + + size = sizeof (EnumBlob); + for (l = enum_->values; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + for (l = enum_->methods; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + } + break; + + case G_IR_NODE_VALUE: + size = sizeof (ValueBlob); + break; + + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_ = (GIrNodeStruct *)node; + + size = sizeof (StructBlob); + for (l = struct_->members; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + } + break; + + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed = (GIrNodeBoxed *)node; + + size = sizeof (StructBlob); + for (l = boxed->members; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + } + break; + + case G_IR_NODE_PROPERTY: + size = sizeof (PropertyBlob); + break; + + case G_IR_NODE_SIGNAL: + size = sizeof (SignalBlob); + break; + + case G_IR_NODE_VFUNC: + size = sizeof (VFuncBlob); + break; + + case G_IR_NODE_FIELD: + { + GIrNodeField *field = (GIrNodeField *)node; + + size = sizeof (FieldBlob); + if (field->callback) + size += _g_ir_node_get_size ((GIrNode *)field->callback); + } + break; + + case G_IR_NODE_CONSTANT: + size = sizeof (ConstantBlob); + break; + + case G_IR_NODE_XREF: + size = 0; + break; + + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_ = (GIrNodeUnion *)node; + + size = sizeof (UnionBlob); + for (l = union_->members; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + for (l = union_->discriminators; l; l = l->next) + size += _g_ir_node_get_size ((GIrNode *)l->data); + } + break; + + default: + g_error ("Unhandled node type '%s'\n", + _g_ir_node_type_to_string (node->type)); + size = 0; + } + + g_debug ("node %p type '%s' size %d", node, + _g_ir_node_type_to_string (node->type), size); + + return size; +} + +static void +add_attribute_size (gpointer key, gpointer value, gpointer data) +{ + const gchar *key_str = key; + const gchar *value_str = value; + gint *size_p = data; + + *size_p += sizeof (AttributeBlob); + *size_p += ALIGN_VALUE (strlen (key_str) + 1, 4); + *size_p += ALIGN_VALUE (strlen (value_str) + 1, 4); +} + +/* returns the full size of the blob including variable-size parts (including attributes) */ +static guint32 +_g_ir_node_get_full_size_internal (GIrNode *parent, + GIrNode *node) +{ + GList *l; + gint size, n; + + if (node == NULL && parent != NULL) + g_error ("Caught NULL node, parent=%s", parent->name); + + g_debug ("node %p type '%s'", node, + _g_ir_node_type_to_string (node->type)); + + switch (node->type) + { + case G_IR_NODE_CALLBACK: + { + GIrNodeFunction *function = (GIrNodeFunction *)node; + size = sizeof (CallbackBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + for (l = function->parameters; l; l = l->next) + { + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + } + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)function->result); + } + break; + + case G_IR_NODE_FUNCTION: + { + GIrNodeFunction *function = (GIrNodeFunction *)node; + size = sizeof (FunctionBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += ALIGN_VALUE (strlen (function->symbol) + 1, 4); + for (l = function->parameters; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)function->result); + } + break; + + case G_IR_NODE_PARAM: + { + GIrNodeParam *param = (GIrNodeParam *)node; + + /* See the comment in the G_IR_NODE_PARAM/ArgBlob writing below */ + size = sizeof (ArgBlob) - sizeof (SimpleTypeBlob); + if (node->name) + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)param->type); + } + break; + + case G_IR_NODE_TYPE: + { + GIrNodeType *type = (GIrNodeType *)node; + size = sizeof (SimpleTypeBlob); + if (!GI_TYPE_TAG_IS_BASIC (type->tag)) + { + g_debug ("node %p type tag '%s'", node, + g_type_tag_to_string (type->tag)); + + switch (type->tag) + { + case GI_TYPE_TAG_ARRAY: + size = sizeof (ArrayTypeBlob); + if (type->parameter_type1) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)type->parameter_type1); + break; + case GI_TYPE_TAG_INTERFACE: + size += sizeof (InterfaceTypeBlob); + break; + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + size += sizeof (ParamTypeBlob); + if (type->parameter_type1) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)type->parameter_type1); + break; + case GI_TYPE_TAG_GHASH: + size += sizeof (ParamTypeBlob) * 2; + if (type->parameter_type1) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)type->parameter_type1); + if (type->parameter_type2) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)type->parameter_type2); + break; + case GI_TYPE_TAG_ERROR: + size += sizeof (ErrorTypeBlob); + break; + default: + g_error ("Unknown type tag %d\n", type->tag); + break; + } + } + } + break; + + case G_IR_NODE_OBJECT: + { + GIrNodeInterface *iface = (GIrNodeInterface *)node; + + n = g_list_length (iface->interfaces); + size = sizeof(ObjectBlob); + if (iface->parent) + size += ALIGN_VALUE (strlen (iface->parent) + 1, 4); + if (iface->glib_type_struct) + size += ALIGN_VALUE (strlen (iface->glib_type_struct) + 1, 4); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += ALIGN_VALUE (strlen (iface->gtype_name) + 1, 4); + if (iface->gtype_init) + size += ALIGN_VALUE (strlen (iface->gtype_init) + 1, 4); + if (iface->ref_func) + size += ALIGN_VALUE (strlen (iface->ref_func) + 1, 4); + if (iface->unref_func) + size += ALIGN_VALUE (strlen (iface->unref_func) + 1, 4); + if (iface->set_value_func) + size += ALIGN_VALUE (strlen (iface->set_value_func) + 1, 4); + if (iface->get_value_func) + size += ALIGN_VALUE (strlen (iface->get_value_func) + 1, 4); + size += 2 * (n + (n % 2)); + + for (l = iface->members; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + } + break; + + case G_IR_NODE_INTERFACE: + { + GIrNodeInterface *iface = (GIrNodeInterface *)node; + + n = g_list_length (iface->prerequisites); + size = sizeof (InterfaceBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += ALIGN_VALUE (strlen (iface->gtype_name) + 1, 4); + size += ALIGN_VALUE (strlen (iface->gtype_init) + 1, 4); + size += 2 * (n + (n % 2)); + + for (l = iface->members; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + } + break; + + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + { + GIrNodeEnum *enum_ = (GIrNodeEnum *)node; + + size = sizeof (EnumBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + if (enum_->gtype_name) + { + size += ALIGN_VALUE (strlen (enum_->gtype_name) + 1, 4); + size += ALIGN_VALUE (strlen (enum_->gtype_init) + 1, 4); + } + if (enum_->error_domain) + size += ALIGN_VALUE (strlen (enum_->error_domain) + 1, 4); + + for (l = enum_->values; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + for (l = enum_->methods; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + } + break; + + case G_IR_NODE_VALUE: + { + size = sizeof (ValueBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + } + break; + + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_ = (GIrNodeStruct *)node; + + size = sizeof (StructBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + if (struct_->gtype_name) + size += ALIGN_VALUE (strlen (struct_->gtype_name) + 1, 4); + if (struct_->gtype_init) + size += ALIGN_VALUE (strlen (struct_->gtype_init) + 1, 4); + if (struct_->copy_func) + size += ALIGN_VALUE (strlen (struct_->copy_func) + 1, 4); + if (struct_->free_func) + size += ALIGN_VALUE (strlen (struct_->free_func) + 1, 4); + for (l = struct_->members; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + } + break; + + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed = (GIrNodeBoxed *)node; + + size = sizeof (StructBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + if (boxed->gtype_name) + { + size += ALIGN_VALUE (strlen (boxed->gtype_name) + 1, 4); + size += ALIGN_VALUE (strlen (boxed->gtype_init) + 1, 4); + } + for (l = boxed->members; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + } + break; + + case G_IR_NODE_PROPERTY: + { + GIrNodeProperty *prop = (GIrNodeProperty *)node; + + size = sizeof (PropertyBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)prop->type); + } + break; + + case G_IR_NODE_SIGNAL: + { + GIrNodeSignal *signal = (GIrNodeSignal *)node; + + size = sizeof (SignalBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + for (l = signal->parameters; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)signal->result); + } + break; + + case G_IR_NODE_VFUNC: + { + GIrNodeVFunc *vfunc = (GIrNodeVFunc *)node; + + size = sizeof (VFuncBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + for (l = vfunc->parameters; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)vfunc->result); + } + break; + + case G_IR_NODE_FIELD: + { + GIrNodeField *field = (GIrNodeField *)node; + + size = sizeof (FieldBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + if (field->callback) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)field->callback); + else + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)field->type); + } + break; + + case G_IR_NODE_CONSTANT: + { + GIrNodeConstant *constant = (GIrNodeConstant *)node; + + size = sizeof (ConstantBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + /* FIXME non-string values */ + size += ALIGN_VALUE (strlen (constant->value) + 1, 4); + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)constant->type); + } + break; + + case G_IR_NODE_XREF: + { + GIrNodeXRef *xref = (GIrNodeXRef *)node; + + size = 0; + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + size += ALIGN_VALUE (strlen (xref->namespace) + 1, 4); + } + break; + + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_ = (GIrNodeUnion *)node; + + size = sizeof (UnionBlob); + size += ALIGN_VALUE (strlen (node->name) + 1, 4); + if (union_->gtype_name) + size += ALIGN_VALUE (strlen (union_->gtype_name) + 1, 4); + if (union_->gtype_init) + size += ALIGN_VALUE (strlen (union_->gtype_init) + 1, 4); + if (union_->copy_func) + size += ALIGN_VALUE (strlen (union_->copy_func) + 1, 4); + if (union_->free_func) + size += ALIGN_VALUE (strlen (union_->free_func) + 1, 4); + for (l = union_->members; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + for (l = union_->discriminators; l; l = l->next) + size += _g_ir_node_get_full_size_internal (node, (GIrNode *)l->data); + } + break; + + default: + g_error ("Unknown type tag %d\n", node->type); + size = 0; + } + + g_debug ("node %s%s%s%p type '%s' full size %d", + node->name ? "'" : "", + node->name ? node->name : "", + node->name ? "' " : "", + node, _g_ir_node_type_to_string (node->type), size); + + g_hash_table_foreach (node->attributes, add_attribute_size, &size); + + return size; +} + +guint32 +_g_ir_node_get_full_size (GIrNode *node) +{ + return _g_ir_node_get_full_size_internal (NULL, node); +} + +int +_g_ir_node_cmp (GIrNode *node, + GIrNode *other) +{ + if (node->type < other->type) + return -1; + else if (node->type > other->type) + return 1; + else + return strcmp (node->name, other->name); +} + +gboolean +_g_ir_node_can_have_member (GIrNode *node) +{ + switch (node->type) + { + case G_IR_NODE_OBJECT: + case G_IR_NODE_INTERFACE: + case G_IR_NODE_BOXED: + case G_IR_NODE_STRUCT: + case G_IR_NODE_UNION: + return TRUE; + /* list others individually rather than with default: so that compiler + * warns if new node types are added without adding them to the switch + */ + case G_IR_NODE_INVALID: + case G_IR_NODE_FUNCTION: + case G_IR_NODE_CALLBACK: + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + case G_IR_NODE_CONSTANT: + case G_IR_NODE_INVALID_0: + case G_IR_NODE_PARAM: + case G_IR_NODE_TYPE: + case G_IR_NODE_PROPERTY: + case G_IR_NODE_SIGNAL: + case G_IR_NODE_VALUE: + case G_IR_NODE_VFUNC: + case G_IR_NODE_FIELD: + case G_IR_NODE_XREF: + return FALSE; + default: + g_assert_not_reached (); + }; + return FALSE; +} + +void +_g_ir_node_add_member (GIrNode *node, + GIrNodeFunction *member) +{ + g_return_if_fail (node != NULL); + g_return_if_fail (member != NULL); + + switch (node->type) + { + case G_IR_NODE_OBJECT: + case G_IR_NODE_INTERFACE: + { + GIrNodeInterface *iface = (GIrNodeInterface *)node; + iface->members = + g_list_insert_sorted (iface->members, member, + (GCompareFunc) _g_ir_node_cmp); + break; + } + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed = (GIrNodeBoxed *)node; + boxed->members = + g_list_insert_sorted (boxed->members, member, + (GCompareFunc) _g_ir_node_cmp); + break; + } + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_ = (GIrNodeStruct *)node; + struct_->members = + g_list_insert_sorted (struct_->members, member, + (GCompareFunc) _g_ir_node_cmp); + break; + } + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_ = (GIrNodeUnion *)node; + union_->members = + g_list_insert_sorted (union_->members, member, + (GCompareFunc) _g_ir_node_cmp); + break; + } + default: + g_error ("Cannot add a member to unknown type tag type %d\n", + node->type); + break; + } +} + +const gchar * +_g_ir_node_param_direction_string (GIrNodeParam * node) +{ + if (node->out) + { + if (node->in) + return "in-out"; + else + return "out"; + } + return "in"; +} + +static gint64 +parse_int_value (const gchar *str) +{ + return g_ascii_strtoll (str, NULL, 0); +} + +static guint64 +parse_uint_value (const gchar *str) +{ + return g_ascii_strtoull (str, NULL, 0); +} + +static gdouble +parse_float_value (const gchar *str) +{ + return g_ascii_strtod (str, NULL); +} + +static gboolean +parse_boolean_value (const gchar *str) +{ + if (g_ascii_strcasecmp (str, "TRUE") == 0) + return TRUE; + + if (g_ascii_strcasecmp (str, "FALSE") == 0) + return FALSE; + + return parse_int_value (str) ? TRUE : FALSE; +} + +static GIrNode * +find_entry_node (GIrTypelibBuild *build, + const gchar *name, + guint16 *idx) + +{ + GIrModule *module = build->module; + GList *l; + gint i; + gchar **names; + gint n_names; + GIrNode *result = NULL; + + g_assert (name != NULL); + g_assert (strlen (name) > 0); + + names = g_strsplit (name, ".", 0); + n_names = g_strv_length (names); + if (n_names > 2) + g_error ("Too many name parts"); + + for (l = module->entries, i = 1; l; l = l->next, i++) + { + GIrNode *node = (GIrNode *)l->data; + + if (n_names > 1) + { + if (node->type != G_IR_NODE_XREF) + continue; + + if (((GIrNodeXRef *)node)->namespace == NULL || + strcmp (((GIrNodeXRef *)node)->namespace, names[0]) != 0) + continue; + } + + if (strcmp (node->name, names[n_names - 1]) == 0) + { + if (idx) + *idx = i; + + result = node; + goto out; + } + } + + if (n_names > 1) + { + GIrNode *node = _g_ir_node_new (G_IR_NODE_XREF, module); + + ((GIrNodeXRef *)node)->namespace = g_strdup (names[0]); + node->name = g_strdup (names[1]); + + module->entries = g_list_append (module->entries, node); + + if (idx) + *idx = g_list_length (module->entries); + + result = node; + + g_debug ("Creating XREF: %s %s", names[0], names[1]); + + goto out; + } + + + _g_ir_module_fatal (build, -1, "type reference '%s' not found", + name); + out: + + g_strfreev (names); + + return result; +} + +static guint16 +find_entry (GIrTypelibBuild *build, + const gchar *name) +{ + guint16 idx = 0; + + find_entry_node (build, name, &idx); + + return idx; +} + +static GIrModule * +find_namespace (GIrModule *module, + const char *name) +{ + GIrModule *target; + GList *l; + + if (strcmp (module->name, name) == 0) + return module; + + for (l = module->include_modules; l; l = l->next) + { + GIrModule *submodule = l->data; + + if (strcmp (submodule->name, name) == 0) + return submodule; + + target = find_namespace (submodule, name); + if (target) + return target; + } + return NULL; +} + +GIrNode * +_g_ir_find_node (GIrTypelibBuild *build, + GIrModule *src_module, + const char *name) +{ + GList *l; + GIrNode *return_node = NULL; + char **names = g_strsplit (name, ".", 0); + gint n_names = g_strv_length (names); + const char *target_name; + GIrModule *target_module; + + if (n_names == 1) + { + target_module = src_module; + target_name = name; + } + else + { + target_module = find_namespace (build->module, names[0]); + target_name = names[1]; + } + + /* find_namespace() may return NULL. */ + if (target_module == NULL) + goto done; + + for (l = target_module->entries; l; l = l->next) + { + GIrNode *node = (GIrNode *)l->data; + + if (strcmp (node->name, target_name) == 0) + { + return_node = node; + break; + } + } + +done: + g_strfreev (names); + + return return_node; +} + +static int +get_index_of_member_type (GIrNodeInterface *node, + GIrNodeTypeId type, + const char *name) +{ + guint index = -1; + GList *l; + + for (l = node->members; l; l = l->next) + { + GIrNode *member_node = l->data; + + if (member_node->type != type) + continue; + + index++; + + if (strcmp (member_node->name, name) == 0) + break; + } + + return index; +} + +static void +serialize_type (GIrTypelibBuild *build, + GIrNodeType *node, + GString *str) +{ + gint i; + + if (GI_TYPE_TAG_IS_BASIC (node->tag)) + { + g_string_append_printf (str, "%s%s", g_type_tag_to_string (node->tag), + node->is_pointer ? "*" : ""); + } + else if (node->tag == GI_TYPE_TAG_ARRAY) + { + if (node->array_type == GI_ARRAY_TYPE_C) + { + serialize_type (build, node->parameter_type1, str); + g_string_append (str, "["); + + if (node->has_length) + g_string_append_printf (str, "length=%d", node->length); + else if (node->has_size) + g_string_append_printf (str, "fixed-size=%d", node->size); + + if (node->zero_terminated) + g_string_append_printf (str, "%szero-terminated=1", + node->has_length ? "," : ""); + + g_string_append (str, "]"); + if (node->is_pointer) + g_string_append (str, "*"); + } + else if (node->array_type == GI_ARRAY_TYPE_BYTE_ARRAY) + { + /* We on purpose skip serializing parameter_type1, which should + always be void* + */ + g_string_append (str, "GByteArray"); + } + else + { + if (node->array_type == GI_ARRAY_TYPE_ARRAY) + g_string_append (str, "GArray"); + else + g_string_append (str, "GPtrArray"); + if (node->parameter_type1) + { + g_string_append (str, "<"); + serialize_type (build, node->parameter_type1, str); + g_string_append (str, ">"); + } + } + } + else if (node->tag == GI_TYPE_TAG_INTERFACE) + { + GIrNode *iface; + gchar *name; + + iface = find_entry_node (build, node->giinterface, NULL); + if (iface) + { + if (iface->type == G_IR_NODE_XREF) + g_string_append_printf (str, "%s.", ((GIrNodeXRef *)iface)->namespace); + name = iface->name; + } + else + { + g_warning ("Interface for type reference %s not found", node->giinterface); + name = node->giinterface; + } + + g_string_append_printf (str, "%s%s", name, + node->is_pointer ? "*" : ""); + } + else if (node->tag == GI_TYPE_TAG_GLIST) + { + g_string_append (str, "GList"); + if (node->parameter_type1) + { + g_string_append (str, "<"); + serialize_type (build, node->parameter_type1, str); + g_string_append (str, ">"); + } + } + else if (node->tag == GI_TYPE_TAG_GSLIST) + { + g_string_append (str, "GSList"); + if (node->parameter_type1) + { + g_string_append (str, "<"); + serialize_type (build, node->parameter_type1, str); + g_string_append (str, ">"); + } + } + else if (node->tag == GI_TYPE_TAG_GHASH) + { + g_string_append (str, "GHashTable"); + if (node->parameter_type1) + { + g_string_append (str, "<"); + serialize_type (build, node->parameter_type1, str); + g_string_append (str, ","); + serialize_type (build, node->parameter_type2, str); + g_string_append (str, ">"); + } + } + else if (node->tag == GI_TYPE_TAG_ERROR) + { + g_string_append (str, "GError"); + if (node->errors) + { + g_string_append (str, "<"); + for (i = 0; node->errors[i]; i++) + { + if (i > 0) + g_string_append (str, ","); + g_string_append (str, node->errors[i]); + } + g_string_append (str, ">"); + } + } +} + +static void +_g_ir_node_build_members (GList **members, + GIrNodeTypeId type, + guint16 *count, + GIrNode *parent, + GIrTypelibBuild *build, + guint32 *offset, + guint32 *offset2, + guint16 *count2) +{ + GList *l = *members; + + while (l) + { + GIrNode *member = (GIrNode *)l->data; + GList *next = l->next; + + if (member->type == type) + { + (*count)++; + _g_ir_node_build_typelib (member, parent, build, offset, offset2, count2); + *members = g_list_delete_link (*members, l); + } + l = next; + } +} + +static void +_g_ir_node_check_unhandled_members (GList **members, + GIrNodeTypeId container_type) +{ +#if 0 + if (*members) + { + GList *l; + + for (l = *members; l; l = l->next) + { + GIrNode *member = (GIrNode *)l->data; + g_printerr ("Unhandled '%s' member '%s' type '%s'\n", + _g_ir_node_type_to_string (container_type), + member->name, + _g_ir_node_type_to_string (member->type)); + } + + g_list_free (*members); + *members = NULL; + + g_error ("Unhandled members. Aborting."); + } +#else + g_list_free (*members); + *members = NULL; +#endif +} + +void +_g_ir_node_build_typelib (GIrNode *node, + GIrNode *parent, + GIrTypelibBuild *build, + guint32 *offset, + guint32 *offset2, + guint16 *count2) +{ + gboolean appended_stack; + GHashTable *strings = build->strings; + GHashTable *types = build->types; + guchar *data = build->data; + GList *l; + guint32 old_offset = *offset; + guint32 old_offset2 = *offset2; + + g_assert (node != NULL); + + g_debug ("build_typelib: %s%s(%s)", + node->name ? node->name : "", + node->name ? " " : "", + _g_ir_node_type_to_string (node->type)); + + if (build->stack) + appended_stack = node != (GIrNode*)build->stack->data; + else + appended_stack = TRUE; + if (appended_stack) + build->stack = g_list_prepend (build->stack, node); + + _g_ir_node_compute_offsets (build, node); + + /* We should only be building each node once. If we do a typelib expansion, we also + * reset the offset in girmodule.c. + */ + g_assert (node->offset == 0); + node->offset = *offset; + build->nodes_with_attributes = g_list_prepend (build->nodes_with_attributes, node); + + build->n_attributes += g_hash_table_size (node->attributes); + + switch (node->type) + { + case G_IR_NODE_TYPE: + { + GIrNodeType *type = (GIrNodeType *)node; + SimpleTypeBlob *blob = (SimpleTypeBlob *)&data[*offset]; + + *offset += sizeof (SimpleTypeBlob); + + if (GI_TYPE_TAG_IS_BASIC (type->tag)) + { + blob->flags.reserved = 0; + blob->flags.reserved2 = 0; + blob->flags.pointer = type->is_pointer; + blob->flags.reserved3 = 0; + blob->flags.tag = type->tag; + } + else + { + GString *str; + gchar *s; + gpointer value; + + str = g_string_new (0); + serialize_type (build, type, str); + s = g_string_free (str, FALSE); + + types_count += 1; + value = g_hash_table_lookup (types, s); + if (value) + { + blob->offset = GPOINTER_TO_UINT (value); + g_free (s); + } + else + { + unique_types_count += 1; + g_hash_table_insert (types, s, GUINT_TO_POINTER(*offset2)); + + blob->offset = *offset2; + switch (type->tag) + { + case GI_TYPE_TAG_ARRAY: + { + ArrayTypeBlob *array = (ArrayTypeBlob *)&data[*offset2]; + guint32 pos; + + array->pointer = type->is_pointer; + array->reserved = 0; + array->tag = type->tag; + array->zero_terminated = type->zero_terminated; + array->has_length = type->has_length; + array->has_size = type->has_size; + array->array_type = type->array_type; + array->reserved2 = 0; + if (array->has_length) + array->dimensions.length = type->length; + else if (array->has_size) + array->dimensions.size = type->size; + else + array->dimensions.length = -1; + + pos = *offset2 + G_STRUCT_OFFSET (ArrayTypeBlob, type); + *offset2 += sizeof (ArrayTypeBlob); + + _g_ir_node_build_typelib ((GIrNode *)type->parameter_type1, + node, build, &pos, offset2, NULL); + } + break; + + case GI_TYPE_TAG_INTERFACE: + { + InterfaceTypeBlob *iface = (InterfaceTypeBlob *)&data[*offset2]; + *offset2 += sizeof (InterfaceTypeBlob); + + iface->pointer = type->is_pointer; + iface->reserved = 0; + iface->tag = type->tag; + iface->reserved2 = 0; + iface->interface = find_entry (build, type->giinterface); + + } + break; + + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + { + ParamTypeBlob *param = (ParamTypeBlob *)&data[*offset2]; + guint32 pos; + + param->pointer = 1; + param->reserved = 0; + param->tag = type->tag; + param->reserved2 = 0; + param->n_types = 1; + + pos = *offset2 + G_STRUCT_OFFSET (ParamTypeBlob, type); + *offset2 += sizeof (ParamTypeBlob) + sizeof (SimpleTypeBlob); + + _g_ir_node_build_typelib ((GIrNode *)type->parameter_type1, + node, build, &pos, offset2, NULL); + } + break; + + case GI_TYPE_TAG_GHASH: + { + ParamTypeBlob *param = (ParamTypeBlob *)&data[*offset2]; + guint32 pos; + + param->pointer = 1; + param->reserved = 0; + param->tag = type->tag; + param->reserved2 = 0; + param->n_types = 2; + + pos = *offset2 + G_STRUCT_OFFSET (ParamTypeBlob, type); + *offset2 += sizeof (ParamTypeBlob) + sizeof (SimpleTypeBlob)*2; + + _g_ir_node_build_typelib ((GIrNode *)type->parameter_type1, + node, build, &pos, offset2, NULL); + _g_ir_node_build_typelib ((GIrNode *)type->parameter_type2, + node, build, &pos, offset2, NULL); + } + break; + + case GI_TYPE_TAG_ERROR: + { + ErrorTypeBlob *error_blob = (ErrorTypeBlob *)&data[*offset2]; + + error_blob->pointer = 1; + error_blob->reserved = 0; + error_blob->tag = type->tag; + error_blob->reserved2 = 0; + error_blob->n_domains = 0; + + *offset2 += sizeof (ErrorTypeBlob); + } + break; + + default: + g_error ("Unknown type tag %d\n", type->tag); + break; + } + } + } + } + break; + + case G_IR_NODE_FIELD: + { + GIrNodeField *field = (GIrNodeField *)node; + FieldBlob *blob; + + blob = (FieldBlob *)&data[*offset]; + + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->readable = field->readable; + blob->writable = field->writable; + blob->reserved = 0; + blob->bits = 0; + if (field->offset >= 0) + blob->struct_offset = field->offset; + else + blob->struct_offset = 0xFFFF; /* mark as unknown */ + + if (field->callback) + { + blob->has_embedded_type = TRUE; + blob->type.offset = GI_INFO_TYPE_CALLBACK; + *offset += sizeof (FieldBlob); + _g_ir_node_build_typelib ((GIrNode *)field->callback, + node, build, offset, offset2, NULL); + /* Fields with callbacks are bigger than normal, update count2 + * as an extra hint which represents the number of fields which are + * callbacks. This allows us to gain constant time performance in the + * repository for skipping over the fields section. + */ + if (count2) + (*count2)++; + } + else + { + blob->has_embedded_type = FALSE; + /* We handle the size member specially below, so subtract it */ + *offset += sizeof (FieldBlob) - sizeof (SimpleTypeBlob); + _g_ir_node_build_typelib ((GIrNode *)field->type, + node, build, offset, offset2, NULL); + } + } + break; + + case G_IR_NODE_PROPERTY: + { + GIrNodeProperty *prop = (GIrNodeProperty *)node; + PropertyBlob *blob = (PropertyBlob *)&data[*offset]; + /* We handle the size member specially below, so subtract it */ + *offset += sizeof (PropertyBlob) - sizeof (SimpleTypeBlob); + + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->deprecated = prop->deprecated; + blob->readable = prop->readable; + blob->writable = prop->writable; + blob->construct = prop->construct; + blob->construct_only = prop->construct_only; + blob->transfer_ownership = prop->transfer; + blob->transfer_container_ownership = prop->shallow_transfer; + blob->reserved = 0; + + if (prop->setter != NULL) + { + int index = get_index_of_member_type ((GIrNodeInterface*)parent, + G_IR_NODE_FUNCTION, + prop->setter); + if (index == -1) + { + g_error ("Unknown setter %s for property %s:%s", prop->setter, parent->name, node->name); + } + + blob->setter = (guint) index; + } + else + blob->setter = ACCESSOR_SENTINEL; + + if (prop->getter != NULL) + { + int index = get_index_of_member_type ((GIrNodeInterface*)parent, + G_IR_NODE_FUNCTION, + prop->getter); + if (index == -1) + { + g_error ("Unknown getter %s for property %s:%s", prop->getter, parent->name, node->name); + } + + blob->getter = (guint) index; + } + else + blob->getter = ACCESSOR_SENTINEL; + + _g_ir_node_build_typelib ((GIrNode *)prop->type, + node, build, offset, offset2, NULL); + } + break; + + case G_IR_NODE_FUNCTION: + { + FunctionBlob *blob = (FunctionBlob *)&data[*offset]; + SignatureBlob *blob2 = (SignatureBlob *)&data[*offset2]; + GIrNodeFunction *function = (GIrNodeFunction *)node; + guint32 signature; + gint n; + + signature = *offset2; + n = g_list_length (function->parameters); + + *offset += sizeof (FunctionBlob); + *offset2 += sizeof (SignatureBlob) + n * sizeof (ArgBlob); + + blob->blob_type = BLOB_TYPE_FUNCTION; + blob->deprecated = function->deprecated; + blob->is_static = !function->is_method; + blob->setter = FALSE; + blob->getter = FALSE; + blob->constructor = function->is_constructor; + blob->wraps_vfunc = function->wraps_vfunc; + blob->throws = function->throws; /* Deprecated. Also stored in SignatureBlob. */ + blob->index = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->symbol = _g_ir_write_string (function->symbol, strings, data, offset2); + blob->signature = signature; + + if (function->is_setter || function->is_getter) + { + int index = get_index_of_member_type ((GIrNodeInterface*)parent, + G_IR_NODE_PROPERTY, + function->property); + if (index == -1) + { + g_error ("Unknown property %s:%s for accessor %s", parent->name, function->property, function->symbol); + } + + blob->setter = function->is_setter; + blob->getter = function->is_getter; + blob->index = (guint) index; + } + + /* function->result is special since it doesn't appear in the serialized format but + * we do want the attributes for it to appear + */ + build->nodes_with_attributes = g_list_prepend (build->nodes_with_attributes, function->result); + build->n_attributes += g_hash_table_size (((GIrNode *) function->result)->attributes); + g_assert (((GIrNode *) function->result)->offset == 0); + ((GIrNode *) function->result)->offset = signature; + + g_debug ("building function '%s'", function->symbol); + + _g_ir_node_build_typelib ((GIrNode *)function->result->type, + node, build, &signature, offset2, NULL); + + blob2->may_return_null = function->result->nullable; + blob2->caller_owns_return_value = function->result->transfer; + blob2->caller_owns_return_container = function->result->shallow_transfer; + blob2->skip_return = function->result->skip; + blob2->instance_transfer_ownership = function->instance_transfer_full; + blob2->reserved = 0; + blob2->n_arguments = n; + blob2->throws = function->throws; + + signature += 4; + + for (l = function->parameters; l; l = l->next) + { + GIrNode *param = (GIrNode *)l->data; + + _g_ir_node_build_typelib (param, node, build, &signature, offset2, NULL); + } + + } + break; + + case G_IR_NODE_CALLBACK: + { + CallbackBlob *blob = (CallbackBlob *)&data[*offset]; + SignatureBlob *blob2 = (SignatureBlob *)&data[*offset2]; + GIrNodeFunction *function = (GIrNodeFunction *)node; + guint32 signature; + gint n; + + signature = *offset2; + n = g_list_length (function->parameters); + + *offset += sizeof (CallbackBlob); + *offset2 += sizeof (SignatureBlob) + n * sizeof (ArgBlob); + + blob->blob_type = BLOB_TYPE_CALLBACK; + blob->deprecated = function->deprecated; + blob->reserved = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->signature = signature; + + _g_ir_node_build_typelib ((GIrNode *)function->result->type, + node, build, &signature, offset2, NULL); + + blob2->may_return_null = function->result->nullable; + blob2->caller_owns_return_value = function->result->transfer; + blob2->caller_owns_return_container = function->result->shallow_transfer; + blob2->reserved = 0; + blob2->n_arguments = n; + blob2->throws = function->throws; + + signature += 4; + + for (l = function->parameters; l; l = l->next) + { + GIrNode *param = (GIrNode *)l->data; + + _g_ir_node_build_typelib (param, node, build, &signature, offset2, NULL); + } + } + break; + + case G_IR_NODE_SIGNAL: + { + SignalBlob *blob = (SignalBlob *)&data[*offset]; + SignatureBlob *blob2 = (SignatureBlob *)&data[*offset2]; + GIrNodeSignal *signal = (GIrNodeSignal *)node; + guint32 signature; + gint n; + + signature = *offset2; + n = g_list_length (signal->parameters); + + *offset += sizeof (SignalBlob); + *offset2 += sizeof (SignatureBlob) + n * sizeof (ArgBlob); + + blob->deprecated = signal->deprecated; + blob->run_first = signal->run_first; + blob->run_last = signal->run_last; + blob->run_cleanup = signal->run_cleanup; + blob->no_recurse = signal->no_recurse; + blob->detailed = signal->detailed; + blob->action = signal->action; + blob->no_hooks = signal->no_hooks; + blob->has_class_closure = 0; /* FIXME */ + blob->true_stops_emit = 0; /* FIXME */ + blob->reserved = 0; + blob->class_closure = 0; /* FIXME */ + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->signature = signature; + + /* signal->result is special since it doesn't appear in the serialized format but + * we do want the attributes for it to appear + */ + build->nodes_with_attributes = g_list_prepend (build->nodes_with_attributes, signal->result); + build->n_attributes += g_hash_table_size (((GIrNode *) signal->result)->attributes); + g_assert (((GIrNode *) signal->result)->offset == 0); + ((GIrNode *) signal->result)->offset = signature; + + _g_ir_node_build_typelib ((GIrNode *)signal->result->type, + node, build, &signature, offset2, NULL); + + blob2->may_return_null = signal->result->nullable; + blob2->caller_owns_return_value = signal->result->transfer; + blob2->caller_owns_return_container = signal->result->shallow_transfer; + blob2->instance_transfer_ownership = signal->instance_transfer_full; + blob2->reserved = 0; + blob2->n_arguments = n; + + signature += 4; + + for (l = signal->parameters; l; l = l->next) + { + GIrNode *param = (GIrNode *)l->data; + + _g_ir_node_build_typelib (param, node, build, &signature, offset2, NULL); + } + } + break; + + case G_IR_NODE_VFUNC: + { + VFuncBlob *blob = (VFuncBlob *)&data[*offset]; + SignatureBlob *blob2 = (SignatureBlob *)&data[*offset2]; + GIrNodeVFunc *vfunc = (GIrNodeVFunc *)node; + guint32 signature; + gint n; + + signature = *offset2; + n = g_list_length (vfunc->parameters); + + *offset += sizeof (VFuncBlob); + *offset2 += sizeof (SignatureBlob) + n * sizeof (ArgBlob); + + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->must_chain_up = 0; /* FIXME */ + blob->must_be_implemented = 0; /* FIXME */ + blob->must_not_be_implemented = 0; /* FIXME */ + blob->class_closure = 0; /* FIXME */ + blob->throws = vfunc->throws; /* Deprecated. Also stored in SignatureBlob. */ + blob->reserved = 0; + + if (vfunc->invoker) + { + int index = get_index_of_member_type ((GIrNodeInterface*)parent, G_IR_NODE_FUNCTION, vfunc->invoker); + if (index == -1) + { + g_error ("Unknown member function %s for vfunc %s", vfunc->invoker, node->name); + } + blob->invoker = (guint) index; + } + else + blob->invoker = 0x3ff; /* max of 10 bits */ + + blob->struct_offset = vfunc->offset; + blob->reserved2 = 0; + blob->signature = signature; + + _g_ir_node_build_typelib ((GIrNode *)vfunc->result->type, + node, build, &signature, offset2, NULL); + + blob2->may_return_null = vfunc->result->nullable; + blob2->caller_owns_return_value = vfunc->result->transfer; + blob2->caller_owns_return_container = vfunc->result->shallow_transfer; + blob2->instance_transfer_ownership = vfunc->instance_transfer_full; + blob2->reserved = 0; + blob2->n_arguments = n; + blob2->throws = vfunc->throws; + + signature += 4; + + for (l = vfunc->parameters; l; l = l->next) + { + GIrNode *param = (GIrNode *)l->data; + + _g_ir_node_build_typelib (param, node, build, &signature, offset2, NULL); + } + } + break; + + case G_IR_NODE_PARAM: + { + ArgBlob *blob = (ArgBlob *)&data[*offset]; + GIrNodeParam *param = (GIrNodeParam *)node; + + /* The offset for this one is smaller than the struct because + * we recursively build the simple type inline here below. + */ + *offset += sizeof (ArgBlob) - sizeof (SimpleTypeBlob); + + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->in = param->in; + blob->out = param->out; + blob->caller_allocates = param->caller_allocates; + blob->nullable = param->nullable; + blob->skip = param->skip; + blob->optional = param->optional; + blob->transfer_ownership = param->transfer; + blob->transfer_container_ownership = param->shallow_transfer; + blob->return_value = param->retval; + blob->scope = param->scope; + blob->reserved = 0; + blob->closure = param->closure; + blob->destroy = param->destroy; + + _g_ir_node_build_typelib ((GIrNode *)param->type, node, build, offset, offset2, NULL); + } + break; + + case G_IR_NODE_STRUCT: + { + StructBlob *blob = (StructBlob *)&data[*offset]; + GIrNodeStruct *struct_ = (GIrNodeStruct *)node; + GList *members; + + blob->blob_type = BLOB_TYPE_STRUCT; + blob->foreign = struct_->foreign; + blob->deprecated = struct_->deprecated; + blob->is_gtype_struct = struct_->is_gtype_struct; + blob->reserved = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->alignment = struct_->alignment; + blob->size = struct_->size; + + if (struct_->gtype_name) + { + blob->unregistered = FALSE; + blob->gtype_name = _g_ir_write_string (struct_->gtype_name, strings, data, offset2); + blob->gtype_init = _g_ir_write_string (struct_->gtype_init, strings, data, offset2); + } + else + { + blob->unregistered = TRUE; + blob->gtype_name = 0; + blob->gtype_init = 0; + } + + if (struct_->copy_func) + blob->copy_func = _g_ir_write_string (struct_->copy_func, strings, data, offset2); + if (struct_->free_func) + blob->free_func = _g_ir_write_string (struct_->free_func, strings, data, offset2); + + blob->n_fields = 0; + blob->n_methods = 0; + + *offset += sizeof (StructBlob); + + members = g_list_copy (struct_->members); + + _g_ir_node_build_members (&members, G_IR_NODE_FIELD, &blob->n_fields, + node, build, offset, offset2, NULL); + + _g_ir_node_build_members (&members, G_IR_NODE_FUNCTION, &blob->n_methods, + node, build, offset, offset2, NULL); + + _g_ir_node_check_unhandled_members (&members, node->type); + + g_assert (members == NULL); + } + break; + + case G_IR_NODE_BOXED: + { + StructBlob *blob = (StructBlob *)&data[*offset]; + GIrNodeBoxed *boxed = (GIrNodeBoxed *)node; + GList *members; + + blob->blob_type = BLOB_TYPE_BOXED; + blob->deprecated = boxed->deprecated; + blob->unregistered = FALSE; + blob->reserved = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->gtype_name = _g_ir_write_string (boxed->gtype_name, strings, data, offset2); + blob->gtype_init = _g_ir_write_string (boxed->gtype_init, strings, data, offset2); + blob->alignment = boxed->alignment; + blob->size = boxed->size; + + blob->n_fields = 0; + blob->n_methods = 0; + + *offset += sizeof (StructBlob); + + members = g_list_copy (boxed->members); + + _g_ir_node_build_members (&members, G_IR_NODE_FIELD, &blob->n_fields, + node, build, offset, offset2, NULL); + + _g_ir_node_build_members (&members, G_IR_NODE_FUNCTION, &blob->n_methods, + node, build, offset, offset2, NULL); + + _g_ir_node_check_unhandled_members (&members, node->type); + + g_assert (members == NULL); + } + break; + + case G_IR_NODE_UNION: + { + UnionBlob *blob = (UnionBlob *)&data[*offset]; + GIrNodeUnion *union_ = (GIrNodeUnion *)node; + GList *members; + + blob->blob_type = BLOB_TYPE_UNION; + blob->deprecated = union_->deprecated; + blob->reserved = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->alignment = union_->alignment; + blob->size = union_->size; + if (union_->gtype_name) + { + blob->unregistered = FALSE; + blob->gtype_name = _g_ir_write_string (union_->gtype_name, strings, data, offset2); + blob->gtype_init = _g_ir_write_string (union_->gtype_init, strings, data, offset2); + } + else + { + blob->unregistered = TRUE; + blob->gtype_name = 0; + blob->gtype_init = 0; + } + + blob->n_fields = 0; + blob->n_functions = 0; + + blob->discriminator_offset = union_->discriminator_offset; + + if (union_->copy_func) + blob->copy_func = _g_ir_write_string (union_->copy_func, strings, data, offset2); + if (union_->free_func) + blob->free_func = _g_ir_write_string (union_->free_func, strings, data, offset2); + + /* We don't support Union discriminators right now. */ + /* + if (union_->discriminator_type) + { + *offset += 28; + blob->discriminated = TRUE; + _g_ir_node_build_typelib ((GIrNode *)union_->discriminator_type, + build, offset, offset2, NULL); + } + else + { + */ + *offset += sizeof (UnionBlob); + blob->discriminated = FALSE; + blob->discriminator_type.offset = 0; + + members = g_list_copy (union_->members); + + _g_ir_node_build_members (&members, G_IR_NODE_FIELD, &blob->n_fields, + node, build, offset, offset2, NULL); + + _g_ir_node_build_members (&members, G_IR_NODE_FUNCTION, &blob->n_functions, + node, build, offset, offset2, NULL); + + _g_ir_node_check_unhandled_members (&members, node->type); + + g_assert (members == NULL); + + if (union_->discriminator_type) + { + for (l = union_->discriminators; l; l = l->next) + { + GIrNode *member = (GIrNode *)l->data; + + _g_ir_node_build_typelib (member, node, build, offset, offset2, NULL); + } + } + } + break; + + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + { + EnumBlob *blob = (EnumBlob *)&data[*offset]; + GIrNodeEnum *enum_ = (GIrNodeEnum *)node; + + *offset += sizeof (EnumBlob); + + if (node->type == G_IR_NODE_ENUM) + blob->blob_type = BLOB_TYPE_ENUM; + else + blob->blob_type = BLOB_TYPE_FLAGS; + + blob->deprecated = enum_->deprecated; + blob->reserved = 0; + blob->storage_type = enum_->storage_type; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + if (enum_->gtype_name) + { + blob->unregistered = FALSE; + blob->gtype_name = _g_ir_write_string (enum_->gtype_name, strings, data, offset2); + blob->gtype_init = _g_ir_write_string (enum_->gtype_init, strings, data, offset2); + } + else + { + blob->unregistered = TRUE; + blob->gtype_name = 0; + blob->gtype_init = 0; + } + if (enum_->error_domain) + blob->error_domain = _g_ir_write_string (enum_->error_domain, strings, data, offset2); + else + blob->error_domain = 0; + + blob->n_values = 0; + blob->n_methods = 0; + + for (l = enum_->values; l; l = l->next) + { + GIrNode *value = (GIrNode *)l->data; + + blob->n_values++; + _g_ir_node_build_typelib (value, node, build, offset, offset2, NULL); + } + + for (l = enum_->methods; l; l = l->next) + { + GIrNode *method = (GIrNode *)l->data; + + blob->n_methods++; + _g_ir_node_build_typelib (method, node, build, offset, offset2, NULL); + } + } + break; + + case G_IR_NODE_OBJECT: + { + ObjectBlob *blob = (ObjectBlob *)&data[*offset]; + GIrNodeInterface *object = (GIrNodeInterface *)node; + GList *members; + + blob->blob_type = BLOB_TYPE_OBJECT; + blob->abstract = object->abstract; + blob->fundamental = object->fundamental; + blob->final_ = object->final_; + blob->deprecated = object->deprecated; + blob->reserved = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->gtype_name = _g_ir_write_string (object->gtype_name, strings, data, offset2); + blob->gtype_init = _g_ir_write_string (object->gtype_init, strings, data, offset2); + if (object->ref_func) + blob->ref_func = _g_ir_write_string (object->ref_func, strings, data, offset2); + if (object->unref_func) + blob->unref_func = _g_ir_write_string (object->unref_func, strings, data, offset2); + if (object->set_value_func) + blob->set_value_func = _g_ir_write_string (object->set_value_func, strings, data, offset2); + if (object->get_value_func) + blob->get_value_func = _g_ir_write_string (object->get_value_func, strings, data, offset2); + if (object->parent) + blob->parent = find_entry (build, object->parent); + else + blob->parent = 0; + if (object->glib_type_struct) + blob->gtype_struct = find_entry (build, object->glib_type_struct); + else + blob->gtype_struct = 0; + + blob->n_interfaces = 0; + blob->n_fields = 0; + blob->n_properties = 0; + blob->n_methods = 0; + blob->n_signals = 0; + blob->n_vfuncs = 0; + blob->n_constants = 0; + blob->n_field_callbacks = 0; + + *offset += sizeof(ObjectBlob); + for (l = object->interfaces; l; l = l->next) + { + blob->n_interfaces++; + *(guint16*)&data[*offset] = find_entry (build, (gchar *)l->data); + *offset += 2; + } + + members = g_list_copy (object->members); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_FIELD, &blob->n_fields, + node, build, offset, offset2, &blob->n_field_callbacks); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_PROPERTY, &blob->n_properties, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_FUNCTION, &blob->n_methods, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_SIGNAL, &blob->n_signals, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_VFUNC, &blob->n_vfuncs, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_CONSTANT, &blob->n_constants, + node, build, offset, offset2, NULL); + + _g_ir_node_check_unhandled_members (&members, node->type); + + g_assert (members == NULL); + } + break; + + case G_IR_NODE_INTERFACE: + { + InterfaceBlob *blob = (InterfaceBlob *)&data[*offset]; + GIrNodeInterface *iface = (GIrNodeInterface *)node; + GList *members; + + blob->blob_type = BLOB_TYPE_INTERFACE; + blob->deprecated = iface->deprecated; + blob->reserved = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->gtype_name = _g_ir_write_string (iface->gtype_name, strings, data, offset2); + blob->gtype_init = _g_ir_write_string (iface->gtype_init, strings, data, offset2); + if (iface->glib_type_struct) + blob->gtype_struct = find_entry (build, iface->glib_type_struct); + else + blob->gtype_struct = 0; + blob->n_prerequisites = 0; + blob->n_properties = 0; + blob->n_methods = 0; + blob->n_signals = 0; + blob->n_vfuncs = 0; + blob->n_constants = 0; + + *offset += sizeof (InterfaceBlob); + for (l = iface->prerequisites; l; l = l->next) + { + blob->n_prerequisites++; + *(guint16*)&data[*offset] = find_entry (build, (gchar *)l->data); + *offset += 2; + } + + members = g_list_copy (iface->members); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_PROPERTY, &blob->n_properties, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_FUNCTION, &blob->n_methods, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_SIGNAL, &blob->n_signals, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_VFUNC, &blob->n_vfuncs, + node, build, offset, offset2, NULL); + + *offset = ALIGN_VALUE (*offset, 4); + _g_ir_node_build_members (&members, G_IR_NODE_CONSTANT, &blob->n_constants, + node, build, offset, offset2, NULL); + + _g_ir_node_check_unhandled_members (&members, node->type); + + g_assert (members == NULL); + } + break; + + + case G_IR_NODE_VALUE: + { + GIrNodeValue *value = (GIrNodeValue *)node; + ValueBlob *blob = (ValueBlob *)&data[*offset]; + *offset += sizeof (ValueBlob); + + blob->deprecated = value->deprecated; + blob->reserved = 0; + blob->unsigned_value = value->value >= 0 ? 1 : 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + blob->value = (gint32)value->value; + } + break; + + case G_IR_NODE_CONSTANT: + { + GIrNodeConstant *constant = (GIrNodeConstant *)node; + ConstantBlob *blob = (ConstantBlob *)&data[*offset]; + guint32 pos; + + pos = *offset + G_STRUCT_OFFSET (ConstantBlob, type); + *offset += sizeof (ConstantBlob); + + blob->blob_type = BLOB_TYPE_CONSTANT; + blob->deprecated = constant->deprecated; + blob->reserved = 0; + blob->name = _g_ir_write_string (node->name, strings, data, offset2); + + blob->offset = *offset2; + switch (constant->type->tag) + { + case GI_TYPE_TAG_BOOLEAN: + blob->size = 4; + *(gboolean*)&data[blob->offset] = parse_boolean_value (constant->value); + break; + case GI_TYPE_TAG_INT8: + blob->size = 1; + *(gint8*)&data[blob->offset] = (gint8) parse_int_value (constant->value); + break; + case GI_TYPE_TAG_UINT8: + blob->size = 1; + *(guint8*)&data[blob->offset] = (guint8) parse_uint_value (constant->value); + break; + case GI_TYPE_TAG_INT16: + blob->size = 2; + *(gint16*)&data[blob->offset] = (gint16) parse_int_value (constant->value); + break; + case GI_TYPE_TAG_UINT16: + blob->size = 2; + *(guint16*)&data[blob->offset] = (guint16) parse_uint_value (constant->value); + break; + case GI_TYPE_TAG_INT32: + blob->size = 4; + *(gint32*)&data[blob->offset] = (gint32) parse_int_value (constant->value); + break; + case GI_TYPE_TAG_UINT32: + blob->size = 4; + *(guint32*)&data[blob->offset] = (guint32) parse_uint_value (constant->value); + break; + case GI_TYPE_TAG_INT64: + blob->size = 8; + DO_ALIGNED_COPY(&data[blob->offset], parse_int_value (constant->value), gint64); + break; + case GI_TYPE_TAG_UINT64: + blob->size = 8; + DO_ALIGNED_COPY(&data[blob->offset], parse_uint_value (constant->value), guint64); + break; + case GI_TYPE_TAG_FLOAT: + blob->size = sizeof (gfloat); + DO_ALIGNED_COPY(&data[blob->offset], parse_float_value (constant->value), gfloat); + break; + case GI_TYPE_TAG_DOUBLE: + blob->size = sizeof (gdouble); + DO_ALIGNED_COPY(&data[blob->offset], parse_float_value (constant->value), gdouble); + break; + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + blob->size = strlen (constant->value) + 1; + memcpy (&data[blob->offset], constant->value, blob->size); + break; + default: + break; + } + *offset2 += ALIGN_VALUE (blob->size, 4); + + _g_ir_node_build_typelib ((GIrNode *)constant->type, node, build, &pos, offset2, NULL); + } + break; + default: + g_assert_not_reached (); + } + + g_debug ("node %s%s%s%p type '%s', offset %d -> %d, offset2 %d -> %d", + node->name ? "'" : "", + node->name ? node->name : "", + node->name ? "' " : "", + node, _g_ir_node_type_to_string (node->type), + old_offset, *offset, old_offset2, *offset2); + + if (*offset2 - old_offset2 + *offset - old_offset > _g_ir_node_get_full_size (node)) + g_error ("exceeding space reservation; offset: %d (prev %d) offset2: %d (prev %d) nodesize: %d", + *offset, old_offset, *offset2, old_offset2, _g_ir_node_get_full_size (node)); + + if (appended_stack) + build->stack = g_list_delete_link (build->stack, build->stack); +} + +/* if str is already in the pool, return previous location, otherwise write str + * to the typelib at offset, put it in the pool and update offset. If the + * typelib is not large enough to hold the string, reallocate it. + */ +guint32 +_g_ir_write_string (const gchar *str, + GHashTable *strings, + guchar *data, + guint32 *offset) +{ + gpointer value; + guint32 start; + + string_count += 1; + string_size += strlen (str); + + value = g_hash_table_lookup (strings, str); + + if (value) + return GPOINTER_TO_UINT (value); + + unique_string_count += 1; + unique_string_size += strlen (str); + + g_hash_table_insert (strings, (gpointer)str, GUINT_TO_POINTER (*offset)); + + start = *offset; + *offset = ALIGN_VALUE (start + strlen (str) + 1, 4); + + strcpy ((gchar*)&data[start], str); + + return start; +} + diff --git a/girepository/girnode.h b/girepository/girnode.h new file mode 100644 index 000000000..a35834335 --- /dev/null +++ b/girepository/girnode.h @@ -0,0 +1,401 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Parsed GIR + * + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __G_IR_NODE_H__ +#define __G_IR_NODE_H__ + +#include + +#include "girmodule.h" + +G_BEGIN_DECLS + +typedef struct _GIrNode GIrNode; +typedef struct _GIrNodeFunction GIrNodeFunction; +typedef struct _GIrNodeParam GIrNodeParam; +typedef struct _GIrNodeType GIrNodeType; +typedef struct _GIrNodeInterface GIrNodeInterface; +typedef struct _GIrNodeSignal GIrNodeSignal; +typedef struct _GIrNodeProperty GIrNodeProperty; +typedef struct _GIrNodeVFunc GIrNodeVFunc; +typedef struct _GIrNodeField GIrNodeField; +typedef struct _GIrNodeValue GIrNodeValue; +typedef struct _GIrNodeEnum GIrNodeEnum; +typedef struct _GIrNodeBoxed GIrNodeBoxed; +typedef struct _GIrNodeStruct GIrNodeStruct; +typedef struct _GIrNodeConstant GIrNodeConstant; +typedef struct _GIrNodeXRef GIrNodeXRef; +typedef struct _GIrNodeUnion GIrNodeUnion; + +typedef enum +{ + G_IR_NODE_INVALID = 0, + G_IR_NODE_FUNCTION = 1, + G_IR_NODE_CALLBACK = 2, + G_IR_NODE_STRUCT = 3, + G_IR_NODE_BOXED = 4, + G_IR_NODE_ENUM = 5, + G_IR_NODE_FLAGS = 6, + G_IR_NODE_OBJECT = 7, + G_IR_NODE_INTERFACE = 8, + G_IR_NODE_CONSTANT = 9, + G_IR_NODE_INVALID_0 = 10, /* DELETED - used to be ERROR_DOMAIN */ + G_IR_NODE_UNION = 11, + G_IR_NODE_PARAM = 12, + G_IR_NODE_TYPE = 13, + G_IR_NODE_PROPERTY = 14, + G_IR_NODE_SIGNAL = 15, + G_IR_NODE_VALUE = 16, + G_IR_NODE_VFUNC = 17, + G_IR_NODE_FIELD = 18, + G_IR_NODE_XREF = 19 +} GIrNodeTypeId; + +struct _GIrNode +{ + GIrNodeTypeId type; + gchar *name; + GIrModule *module; + + guint32 offset; /* Assigned as we build the typelib */ + + GHashTable *attributes; +}; + +struct _GIrNodeXRef +{ + GIrNode node; + + gchar *namespace; +}; + +struct _GIrNodeFunction +{ + GIrNode node; + + gboolean deprecated; + gboolean is_varargs; /* Not in typelib yet */ + + gboolean is_method; + gboolean is_setter; + gboolean is_getter; + gboolean is_constructor; + gboolean wraps_vfunc; + gboolean throws; + gboolean instance_transfer_full; + + gchar *symbol; + char *property; + + GIrNodeParam *result; + GList *parameters; +}; + +struct _GIrNodeType +{ + GIrNode node; + + gboolean is_pointer; + gboolean is_basic; + gboolean is_array; + gboolean is_glist; + gboolean is_gslist; + gboolean is_ghashtable; + gboolean is_interface; + gboolean is_error; + gint tag; + + gchar *unparsed; + + gboolean zero_terminated; + gboolean has_length; + gint length; + gboolean has_size; + gint size; + gint array_type; + + GIrNodeType *parameter_type1; + GIrNodeType *parameter_type2; + + gchar *giinterface; + gchar **errors; +}; + +struct _GIrNodeParam +{ + GIrNode node; + + gboolean in; + gboolean out; + gboolean caller_allocates; + gboolean optional; + gboolean retval; + gboolean nullable; + gboolean skip; + gboolean transfer; + gboolean shallow_transfer; + GIScopeType scope; + + gint8 closure; + gint8 destroy; + + GIrNodeType *type; +}; + +struct _GIrNodeProperty +{ + GIrNode node; + + gboolean deprecated; + + gchar *name; + gboolean readable; + gboolean writable; + gboolean construct; + gboolean construct_only; + gboolean transfer; + gboolean shallow_transfer; + + char *setter; + char *getter; + + GIrNodeType *type; +}; + +struct _GIrNodeSignal +{ + GIrNode node; + + gboolean deprecated; + + gboolean run_first; + gboolean run_last; + gboolean run_cleanup; + gboolean no_recurse; + gboolean detailed; + gboolean action; + gboolean no_hooks; + gboolean instance_transfer_full; + + gboolean has_class_closure; + gboolean true_stops_emit; + + gint class_closure; + + GList *parameters; + GIrNodeParam *result; +}; + +struct _GIrNodeVFunc +{ + GIrNode node; + + gboolean is_varargs; /* Not in typelib yet */ + gboolean must_chain_up; + gboolean must_be_implemented; + gboolean must_not_be_implemented; + gboolean is_class_closure; + gboolean throws; + gboolean instance_transfer_full; + + char *invoker; + + GList *parameters; + GIrNodeParam *result; + + gint offset; +}; + +struct _GIrNodeField +{ + GIrNode node; + + gboolean readable; + gboolean writable; + gint bits; + gint offset; + GIrNodeFunction *callback; + + GIrNodeType *type; +}; + +struct _GIrNodeInterface +{ + GIrNode node; + + gboolean abstract; + gboolean deprecated; + gboolean fundamental; + gboolean final_; + + gchar *gtype_name; + gchar *gtype_init; + + gchar *ref_func; + gchar *unref_func; + gchar *set_value_func; + gchar *get_value_func; + + gchar *parent; + gchar *glib_type_struct; + + GList *interfaces; + GList *prerequisites; + + gint alignment; + gint size; + + GList *members; +}; + +struct _GIrNodeValue +{ + GIrNode node; + + gboolean deprecated; + + gint64 value; +}; + +struct _GIrNodeConstant +{ + GIrNode node; + + gboolean deprecated; + + GIrNodeType *type; + + gchar *value; +}; + +struct _GIrNodeEnum +{ + GIrNode node; + + gboolean deprecated; + gint storage_type; + + gchar *gtype_name; + gchar *gtype_init; + gchar *error_domain; + + GList *values; + GList *methods; +}; + +struct _GIrNodeBoxed +{ + GIrNode node; + + gboolean deprecated; + + gchar *gtype_name; + gchar *gtype_init; + + gint alignment; + gint size; + + GList *members; +}; + +struct _GIrNodeStruct +{ + GIrNode node; + + gboolean deprecated; + gboolean disguised; + gboolean opaque; + gboolean pointer; + gboolean is_gtype_struct; + gboolean foreign; + + gchar *gtype_name; + gchar *gtype_init; + + gchar *copy_func; + gchar *free_func; + + gint alignment; + gint size; + + GList *members; +}; + +struct _GIrNodeUnion +{ + GIrNode node; + + gboolean deprecated; + + GList *members; + GList *discriminators; + + gchar *gtype_name; + gchar *gtype_init; + + gchar *copy_func; + gchar *free_func; + + gint alignment; + gint size; + + gint discriminator_offset; + GIrNodeType *discriminator_type; +}; + + +GIrNode * _g_ir_node_new (GIrNodeTypeId type, + GIrModule *module); +void _g_ir_node_free (GIrNode *node); +guint32 _g_ir_node_get_size (GIrNode *node); +guint32 _g_ir_node_get_full_size (GIrNode *node); +void _g_ir_node_build_typelib (GIrNode *node, + GIrNode *parent, + GIrTypelibBuild *build, + guint32 *offset, + guint32 *offset2, + guint16 *count2); +int _g_ir_node_cmp (GIrNode *node, + GIrNode *other); +gboolean _g_ir_node_can_have_member (GIrNode *node); +void _g_ir_node_add_member (GIrNode *node, + GIrNodeFunction *member); +guint32 _g_ir_write_string (const gchar *str, + GHashTable *strings, + guchar *data, + guint32 *offset); + +const gchar * _g_ir_node_param_direction_string (GIrNodeParam * node); +const gchar * _g_ir_node_type_to_string (GIrNodeTypeId type); + +GIrNode *_g_ir_find_node (GIrTypelibBuild *build, + GIrModule *module, + const char *name); + +/* In giroffsets.c */ + +void _g_ir_node_compute_offsets (GIrTypelibBuild *build, + GIrNode *node); + + +G_END_DECLS + +#endif /* __G_IR_NODE_H__ */ diff --git a/girepository/giroffsets.c b/girepository/giroffsets.c new file mode 100644 index 000000000..84278b897 --- /dev/null +++ b/girepository/giroffsets.c @@ -0,0 +1,587 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Compute structure offsets + * + * Copyright (C) 2008 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "girffi.h" +#include "girnode.h" +#include + +/* The C standard specifies that an enumeration can be any char or any signed + * or unsigned integer type capable of representing all the values of the + * enumeration. We use test enumerations to figure out what choices the + * compiler makes. (Ignoring > 32 bit enumerations) + */ + +typedef enum { + ENUM_1 = 1 /* compiler could use int8, uint8, int16, uint16, int32, uint32 */ +} Enum1; + +typedef enum { + ENUM_2 = 128 /* compiler could use uint8, int16, uint16, int32, uint32 */ +} Enum2; + +typedef enum { + ENUM_3 = 257 /* compiler could use int16, uint16, int32, uint32 */ +} Enum3; + +typedef enum { + ENUM_4 = G_MAXSHORT + 1 /* compiler could use uint16, int32, uint32 */ +} Enum4; + +typedef enum { + ENUM_5 = G_MAXUSHORT + 1 /* compiler could use int32, uint32 */ +} Enum5; + +typedef enum { + ENUM_6 = ((guint)G_MAXINT) + 1 /* compiler could use uint32 */ +} Enum6; + +typedef enum { + ENUM_7 = -1 /* compiler could use int8, int16, int32 */ +} Enum7; + +typedef enum { + ENUM_8 = -129 /* compiler could use int16, int32 */ +} Enum8; + +typedef enum { + ENUM_9 = G_MINSHORT - 1 /* compiler could use int32 */ +} Enum9; + +static void +compute_enum_storage_type (GIrNodeEnum *enum_node) +{ + GList *l; + gint64 max_value = 0; + gint64 min_value = 0; + int width; + gboolean signed_type; + + if (enum_node->storage_type != GI_TYPE_TAG_VOID) /* already done */ + return; + + for (l = enum_node->values; l; l = l->next) + { + GIrNodeValue *value = l->data; + if (value->value > max_value) + max_value = value->value; + if (value->value < min_value) + min_value = value->value; + } + + if (min_value < 0) + { + signed_type = TRUE; + + if (min_value > -128 && max_value <= 127) + width = sizeof(Enum7); + else if (min_value >= G_MINSHORT && max_value <= G_MAXSHORT) + width = sizeof(Enum8); + else + width = sizeof(Enum9); + } + else + { + if (max_value <= 127) + { + width = sizeof (Enum1); + signed_type = (gint64)(Enum1)(-1) < 0; + } + else if (max_value <= 255) + { + width = sizeof (Enum2); + signed_type = (gint64)(Enum2)(-1) < 0; + } + else if (max_value <= G_MAXSHORT) + { + width = sizeof (Enum3); + signed_type = (gint64)(Enum3)(-1) < 0; + } + else if (max_value <= G_MAXUSHORT) + { + width = sizeof (Enum4); + signed_type = (gint64)(Enum4)(-1) < 0; + } + else if (max_value <= G_MAXINT) + { + width = sizeof (Enum5); + signed_type = (gint64)(Enum5)(-1) < 0; + } + else + { + width = sizeof (Enum6); + signed_type = (gint64)(Enum6)(-1) < 0; + } + } + + if (width == 1) + enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT8 : GI_TYPE_TAG_UINT8; + else if (width == 2) + enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT16 : GI_TYPE_TAG_UINT16; + else if (width == 4) + enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT32 : GI_TYPE_TAG_UINT32; + else if (width == 8) + enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT64 : GI_TYPE_TAG_UINT64; + else + g_error ("Unexpected enum width %d", width); +} + +static gboolean +get_enum_size_alignment (GIrNodeEnum *enum_node, + gint *size, + gint *alignment) +{ + ffi_type *type_ffi; + + compute_enum_storage_type (enum_node); + + switch (enum_node->storage_type) + { + case GI_TYPE_TAG_INT8: + case GI_TYPE_TAG_UINT8: + type_ffi = &ffi_type_uint8; + break; + case GI_TYPE_TAG_INT16: + case GI_TYPE_TAG_UINT16: + type_ffi = &ffi_type_uint16; + break; + case GI_TYPE_TAG_INT32: + case GI_TYPE_TAG_UINT32: + type_ffi = &ffi_type_uint32; + break; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + type_ffi = &ffi_type_uint64; + break; + default: + g_error ("Unexpected enum storage type %s", + g_type_tag_to_string (enum_node->storage_type)); + } + + *size = type_ffi->size; + *alignment = type_ffi->alignment; + + return TRUE; +} + +static gboolean +get_interface_size_alignment (GIrTypelibBuild *build, + GIrNodeType *type, + gint *size, + gint *alignment, + const char *who) +{ + GIrNode *iface; + + iface = _g_ir_find_node (build, ((GIrNode*)type)->module, type->giinterface); + if (!iface) + { + _g_ir_module_fatal (build, 0, "Can't resolve type '%s' for %s", type->giinterface, who); + *size = -1; + *alignment = -1; + return FALSE; + } + + _g_ir_node_compute_offsets (build, iface); + + switch (iface->type) + { + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed = (GIrNodeBoxed *)iface; + *size = boxed->size; + *alignment = boxed->alignment; + break; + } + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_ = (GIrNodeStruct *)iface; + *size = struct_->size; + *alignment = struct_->alignment; + break; + } + case G_IR_NODE_OBJECT: + case G_IR_NODE_INTERFACE: + { + GIrNodeInterface *interface = (GIrNodeInterface *)iface; + *size = interface->size; + *alignment = interface->alignment; + break; + } + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_ = (GIrNodeUnion *)iface; + *size = union_->size; + *alignment = union_->alignment; + break; + } + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + { + return get_enum_size_alignment ((GIrNodeEnum *)iface, + size, alignment); + } + case G_IR_NODE_CALLBACK: + { + *size = ffi_type_pointer.size; + *alignment = ffi_type_pointer.alignment; + break; + } + default: + { + g_warning ("%s has is not a pointer and is of type %s", + who, + _g_ir_node_type_to_string (iface->type)); + *size = -1; + *alignment = -1; + break; + } + } + + return *alignment > 0; +} + +static gboolean +get_type_size_alignment (GIrTypelibBuild *build, + GIrNodeType *type, + gint *size, + gint *alignment, + const char *who) +{ + ffi_type *type_ffi; + + if (type->is_pointer) + { + type_ffi = &ffi_type_pointer; + } + else if (type->tag == GI_TYPE_TAG_ARRAY) + { + gint elt_size, elt_alignment; + + if (!type->has_size + || !get_type_size_alignment(build, type->parameter_type1, + &elt_size, &elt_alignment, who)) + { + *size = -1; + *alignment = -1; + return FALSE; + } + + *size = type->size * elt_size; + *alignment = elt_alignment; + + return TRUE; + } + else + { + if (type->tag == GI_TYPE_TAG_INTERFACE) + { + return get_interface_size_alignment (build, type, size, alignment, who); + } + else + { + type_ffi = gi_type_tag_get_ffi_type (type->tag, type->is_pointer); + + if (type_ffi == &ffi_type_void) + { + g_warning ("%s has void type", who); + *size = -1; + *alignment = -1; + return FALSE; + } + else if (type_ffi == &ffi_type_pointer) + { + g_warning ("%s has is not a pointer and is of type %s", + who, + g_type_tag_to_string (type->tag)); + *size = -1; + *alignment = -1; + return FALSE; + } + } + } + + g_assert (type_ffi); + *size = type_ffi->size; + *alignment = type_ffi->alignment; + + return TRUE; +} + +static gboolean +get_field_size_alignment (GIrTypelibBuild *build, + GIrNodeField *field, + GIrNode *parent_node, + gint *size, + gint *alignment) +{ + GIrModule *module = build->module; + gchar *who; + gboolean success; + + who = g_strdup_printf ("field %s.%s.%s", module->name, parent_node->name, ((GIrNode *)field)->name); + + if (field->callback) + { + *size = ffi_type_pointer.size; + *alignment = ffi_type_pointer.alignment; + success = TRUE; + } + else + success = get_type_size_alignment (build, field->type, size, alignment, who); + g_free (who); + + return success; +} + +#define GI_ALIGN(n, align) (((n) + (align) - 1) & ~((align) - 1)) + +static gboolean +compute_struct_field_offsets (GIrTypelibBuild *build, + GIrNode *node, + GList *members, + gint *size_out, + gint *alignment_out) +{ + int size = 0; + int alignment = 1; + GList *l; + gboolean have_error = FALSE; + + *alignment_out = -2; /* mark to detect recursion */ + + for (l = members; l; l = l->next) + { + GIrNode *member = (GIrNode *)l->data; + + if (member->type == G_IR_NODE_FIELD) + { + GIrNodeField *field = (GIrNodeField *)member; + + if (!have_error) + { + int member_size; + int member_alignment; + + if (get_field_size_alignment (build, field, node, + &member_size, &member_alignment)) + { + size = GI_ALIGN (size, member_alignment); + alignment = MAX (alignment, member_alignment); + field->offset = size; + size += member_size; + } + else + have_error = TRUE; + } + + if (have_error) + field->offset = -1; + } + else if (member->type == G_IR_NODE_CALLBACK) + { + size = GI_ALIGN (size, ffi_type_pointer.alignment); + alignment = MAX (alignment, ffi_type_pointer.alignment); + size += ffi_type_pointer.size; + } + } + + /* Structs are tail-padded out to a multiple of their alignment */ + size = GI_ALIGN (size, alignment); + + if (!have_error) + { + *size_out = size; + *alignment_out = alignment; + } + else + { + *size_out = -1; + *alignment_out = -1; + } + + return !have_error; +} + +static gboolean +compute_union_field_offsets (GIrTypelibBuild *build, + GIrNode *node, + GList *members, + gint *size_out, + gint *alignment_out) +{ + int size = 0; + int alignment = 1; + GList *l; + gboolean have_error = FALSE; + + *alignment_out = -2; /* mark to detect recursion */ + + for (l = members; l; l = l->next) + { + GIrNode *member = (GIrNode *)l->data; + + if (member->type == G_IR_NODE_FIELD) + { + GIrNodeField *field = (GIrNodeField *)member; + + if (!have_error) + { + int member_size; + int member_alignment; + + if (get_field_size_alignment (build,field, node, + &member_size, &member_alignment)) + { + size = MAX (size, member_size); + alignment = MAX (alignment, member_alignment); + } + else + have_error = TRUE; + } + } + } + + /* Unions are tail-padded out to a multiple of their alignment */ + size = GI_ALIGN (size, alignment); + + if (!have_error) + { + *size_out = size; + *alignment_out = alignment; + } + else + { + *size_out = -1; + *alignment_out = -1; + } + + return !have_error; +} + +static gboolean +check_needs_computation (GIrTypelibBuild *build, + GIrNode *node, + gint alignment) +{ + GIrModule *module = build->module; + /* + * 0: Not yet computed + * >0: Previously succeeded + * -1: Previously failed + * -2: In progress + */ + if (alignment == -2) + { + g_warning ("Recursion encountered when computing the size of %s.%s", + module->name, node->name); + } + + return alignment == 0; +} + +/* + * _g_ir_node_compute_offsets: + * @build: Current typelib build + * @node: a #GIrNode + * + * If a node is a a structure or union, makes sure that the field + * offsets have been computed, and also computes the overall size and + * alignment for the type. + */ +void +_g_ir_node_compute_offsets (GIrTypelibBuild *build, + GIrNode *node) +{ + gboolean appended_stack; + + if (build->stack) + appended_stack = node != (GIrNode*)build->stack->data; + else + appended_stack = TRUE; + if (appended_stack) + build->stack = g_list_prepend (build->stack, node); + + switch (node->type) + { + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed = (GIrNodeBoxed *)node; + + if (!check_needs_computation (build, node, boxed->alignment)) + return; + + compute_struct_field_offsets (build, node, boxed->members, + &boxed->size, &boxed->alignment); + break; + } + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_ = (GIrNodeStruct *)node; + + if (!check_needs_computation (build, node, struct_->alignment)) + return; + + compute_struct_field_offsets (build, node, struct_->members, + &struct_->size, &struct_->alignment); + break; + } + case G_IR_NODE_OBJECT: + case G_IR_NODE_INTERFACE: + { + GIrNodeInterface *iface = (GIrNodeInterface *)node; + + if (!check_needs_computation (build, node, iface->alignment)) + return; + + compute_struct_field_offsets (build, node, iface->members, + &iface->size, &iface->alignment); + break; + } + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_ = (GIrNodeUnion *)node; + + if (!check_needs_computation (build, node, union_->alignment)) + return; + + compute_union_field_offsets (build, (GIrNode*)union_, union_->members, + &union_->size, &union_->alignment); + break; + } + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + { + GIrNodeEnum *enum_ = (GIrNodeEnum *)node; + + if (enum_->storage_type != GI_TYPE_TAG_VOID) /* already done */ + return; + + compute_enum_storage_type (enum_); + + break; + } + default: + break; + } + + if (appended_stack) + build->stack = g_list_delete_link (build->stack, build->stack); +} diff --git a/girepository/girparser.c b/girepository/girparser.c new file mode 100644 index 000000000..9a7554ca1 --- /dev/null +++ b/girepository/girparser.c @@ -0,0 +1,3813 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: A parser for the XML GIR format + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008 Philip Van Hoof + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include + +#include +#include "girparser.h" +#include "girmodule.h" +#include "girnode.h" +#include "gitypelib-internal.h" + +/* This is a "major" version in the sense that it's only bumped + * for incompatible changes. + */ +#define SUPPORTED_GIR_VERSION "1.2" + +#ifdef G_OS_WIN32 + +#include + +#ifdef GIR_DIR +#undef GIR_DIR +#endif + +/* GIR_DIR is used only in code called just once, + * so no problem leaking this + */ +#define GIR_DIR \ + g_build_filename (g_win32_get_package_installation_directory_of_module(NULL), \ + "share", \ + GIR_SUFFIX, \ + NULL) +#endif + +struct _GIrParser +{ + gchar **includes; + gchar **gi_gir_path; + GList *parsed_modules; /* All previously parsed modules */ +}; + +typedef enum +{ + STATE_NONE = 0, + STATE_START, + STATE_END, + STATE_REPOSITORY, + STATE_INCLUDE, + STATE_C_INCLUDE, /* 5 */ + STATE_PACKAGE, + STATE_NAMESPACE, + STATE_ENUM, + STATE_BITFIELD, + STATE_FUNCTION, /* 10 */ + STATE_FUNCTION_RETURN, + STATE_FUNCTION_PARAMETERS, + STATE_FUNCTION_PARAMETER, + STATE_CLASS, + STATE_CLASS_FIELD, /* 15 */ + STATE_CLASS_PROPERTY, + STATE_INTERFACE, + STATE_INTERFACE_PROPERTY, + STATE_INTERFACE_FIELD, + STATE_IMPLEMENTS, /* 20 */ + STATE_PREREQUISITE, + STATE_BOXED, + STATE_BOXED_FIELD, + STATE_STRUCT, + STATE_STRUCT_FIELD, /* 25 */ + STATE_UNION, + STATE_UNION_FIELD, + STATE_NAMESPACE_CONSTANT, + STATE_CLASS_CONSTANT, + STATE_INTERFACE_CONSTANT, /* 30 */ + STATE_ALIAS, + STATE_TYPE, + STATE_ATTRIBUTE, + STATE_PASSTHROUGH +} ParseState; + +typedef struct _ParseContext ParseContext; +struct _ParseContext +{ + GIrParser *parser; + + ParseState state; + int unknown_depth; + ParseState prev_state; + + GList *modules; + GList *include_modules; + GList *dependencies; + GHashTable *aliases; + GHashTable *disguised_structures; + GHashTable *pointer_structures; + + const char *file_path; + const char *namespace; + const char *c_prefix; + GIrModule *current_module; + GSList *node_stack; + char *current_alias; + GIrNode *current_typed; + GList *type_stack; + GList *type_parameters; + int type_depth; + ParseState in_embedded_state; +}; +#define CURRENT_NODE(ctx) ((GIrNode *)((ctx)->node_stack->data)) + +static void start_element_handler (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error); +static void end_element_handler (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error); +static void text_handler (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error); +static void cleanup (GMarkupParseContext *context, + GError *error, + gpointer user_data); +static void state_switch (ParseContext *ctx, ParseState newstate); + + +static GMarkupParser markup_parser = +{ + start_element_handler, + end_element_handler, + text_handler, + NULL, + cleanup +}; + +static gboolean +start_alias (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error); +static gboolean +start_type (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error); + +static const gchar *find_attribute (const gchar *name, + const gchar **attribute_names, + const gchar **attribute_values); + + +GIrParser * +_g_ir_parser_new (void) +{ + GIrParser *parser = g_slice_new0 (GIrParser); + const char *gi_gir_path = g_getenv ("GI_GIR_PATH"); + + if (gi_gir_path != NULL) + parser->gi_gir_path = g_strsplit (gi_gir_path, G_SEARCHPATH_SEPARATOR_S, 0); + + return parser; +} + +void +_g_ir_parser_free (GIrParser *parser) +{ + GList *l; + + g_strfreev (parser->includes); + g_strfreev (parser->gi_gir_path); + + for (l = parser->parsed_modules; l; l = l->next) + _g_ir_module_free (l->data); + + g_slice_free (GIrParser, parser); +} + +void +_g_ir_parser_set_includes (GIrParser *parser, + const gchar *const *includes) +{ + g_strfreev (parser->includes); + + parser->includes = g_strdupv ((char **)includes); +} + +static void +firstpass_start_element_handler (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + ParseContext *ctx = user_data; + + if (strcmp (element_name, "alias") == 0) + { + start_alias (context, element_name, attribute_names, attribute_values, + ctx, error); + } + else if (ctx->state == STATE_ALIAS && strcmp (element_name, "type") == 0) + { + start_type (context, element_name, attribute_names, attribute_values, + ctx, error); + } + else if (strcmp (element_name, "record") == 0) + { + const gchar *name; + const gchar *disguised; + const gchar *pointer; + + name = find_attribute ("name", attribute_names, attribute_values); + disguised = find_attribute ("disguised", attribute_names, attribute_values); + pointer = find_attribute ("pointer", attribute_names, attribute_values); + + if (g_strcmp0 (pointer, "1") == 0) + { + char *key; + + key = g_strdup_printf ("%s.%s", ctx->namespace, name); + g_hash_table_replace (ctx->pointer_structures, key, GINT_TO_POINTER (1)); + } + else if (g_strcmp0 (disguised, "1") == 0) + { + char *key; + + key = g_strdup_printf ("%s.%s", ctx->namespace, name); + g_hash_table_replace (ctx->disguised_structures, key, GINT_TO_POINTER (1)); + } + } +} + +static void +firstpass_end_element_handler (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + ParseContext *ctx = user_data; + if (strcmp (element_name, "alias") == 0) + { + state_switch (ctx, STATE_NAMESPACE); + g_free (ctx->current_alias); + ctx->current_alias = NULL; + } + else if (strcmp (element_name, "type") == 0 && ctx->state == STATE_TYPE) + state_switch (ctx, ctx->prev_state); +} + +static GMarkupParser firstpass_parser = +{ + firstpass_start_element_handler, + firstpass_end_element_handler, + NULL, + NULL, + NULL, +}; + +static char * +locate_gir (GIrParser *parser, + const char *girname) +{ + const gchar *const *datadirs; + const gchar *const *dir; + char *path = NULL; + + g_debug ("Looking for %s", girname); + datadirs = g_get_system_data_dirs (); + + if (parser->includes != NULL) + { + for (dir = (const gchar *const *)parser->includes; *dir; dir++) + { + path = g_build_filename (*dir, girname, NULL); + g_debug ("Trying %s from includes", path); + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) + return g_steal_pointer (&path); + g_clear_pointer (&path, g_free); + } + } + + if (parser->gi_gir_path != NULL) + { + for (dir = (const gchar *const *) parser->gi_gir_path; *dir; dir++) + { + if (**dir == '\0') + continue; + + path = g_build_filename (*dir, girname, NULL); + g_debug ("Trying %s from GI_GIR_PATH", path); + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) + return g_steal_pointer (&path); + g_clear_pointer (&path, g_free); + } + } + + path = g_build_filename (g_get_user_data_dir (), GIR_SUFFIX, girname, NULL); + g_debug ("Trying %s from user data dir", path); + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) + return g_steal_pointer (&path); + g_clear_pointer (&path, g_free); + + for (dir = datadirs; *dir; dir++) + { + path = g_build_filename (*dir, GIR_SUFFIX, girname, NULL); + g_debug ("Trying %s from system data dirs", path); + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) + return g_steal_pointer (&path); + g_clear_pointer (&path, g_free); + } + + path = g_build_filename (GIR_DIR, girname, NULL); + g_debug ("Trying %s from GIR_DIR", path); + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) + return g_steal_pointer (&path); + g_clear_pointer (&path, g_free); + + path = g_build_filename (GOBJECT_INTROSPECTION_DATADIR, GIR_SUFFIX, girname, NULL); + g_debug ("Trying %s from DATADIR", path); + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) + return g_steal_pointer (&path); + g_clear_pointer (&path, g_free); + +#ifdef G_OS_UNIX + path = g_build_filename ("/usr/share", GIR_SUFFIX, girname, NULL); + g_debug ("Trying %s", path); + if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) + return g_steal_pointer (&path); + g_clear_pointer (&path, g_free); +#endif + + g_debug ("Did not find %s", girname); + return NULL; +} + +#define MISSING_ATTRIBUTE(context,error,element,attribute) \ + do { \ + int line_number, char_number; \ + g_markup_parse_context_get_position (context, &line_number, &char_number); \ + g_set_error (error, \ + G_MARKUP_ERROR, \ + G_MARKUP_ERROR_INVALID_CONTENT, \ + "Line %d, character %d: The attribute '%s' on the element '%s' must be specified", \ + line_number, char_number, attribute, element); \ + } while (0) + +static const gchar * +find_attribute (const gchar *name, + const gchar **attribute_names, + const gchar **attribute_values) +{ + gint i; + + for (i = 0; attribute_names[i] != NULL; i++) + if (strcmp (attribute_names[i], name) == 0) + return attribute_values[i]; + + return 0; +} + +static void +state_switch (ParseContext *ctx, ParseState newstate) +{ + g_assert (ctx->state != newstate); + ctx->prev_state = ctx->state; + ctx->state = newstate; + + if (ctx->state == STATE_PASSTHROUGH) + ctx->unknown_depth = 1; +} + +static GIrNode * +pop_node (ParseContext *ctx) +{ + GSList *top; + GIrNode *node; + g_assert (ctx->node_stack != 0); + + top = ctx->node_stack; + node = top->data; + + g_debug ("popping node %d %s", node->type, node->name); + ctx->node_stack = top->next; + g_slist_free_1 (top); + return node; +} + +static void +push_node (ParseContext *ctx, GIrNode *node) +{ + g_assert (node != NULL); + g_debug ("pushing node %d %s", node->type, node->name); + ctx->node_stack = g_slist_prepend (ctx->node_stack, node); +} + +static GIrNodeType * parse_type_internal (GIrModule *module, + const gchar *str, gchar **next, gboolean in_glib, + gboolean in_gobject); + +typedef struct { + const gchar *str; + guint size; + guint is_signed : 1; +} IntegerAliasInfo; + +static IntegerAliasInfo integer_aliases[] = { + { "gchar", SIZEOF_CHAR, 1 }, + { "guchar", SIZEOF_CHAR, 0 }, + { "gshort", SIZEOF_SHORT, 1 }, + { "gushort", SIZEOF_SHORT, 0 }, + { "gint", SIZEOF_INT, 1 }, + { "guint", SIZEOF_INT, 0 }, + { "glong", SIZEOF_LONG, 1 }, + { "gulong", SIZEOF_LONG, 0 }, + { "gssize", GLIB_SIZEOF_SIZE_T, 1 }, + { "gsize", GLIB_SIZEOF_SIZE_T, 0 }, + { "gintptr", GLIB_SIZEOF_SIZE_T, 1 }, + { "guintptr", GLIB_SIZEOF_SIZE_T, 0 }, +}; + +typedef struct { + const gchar *str; + gint tag; + gboolean pointer; +} BasicTypeInfo; + +#define BASIC_TYPE_FIXED_OFFSET 3 + +static BasicTypeInfo basic_types[] = { + { "none", GI_TYPE_TAG_VOID, 0 }, + { "gpointer", GI_TYPE_TAG_VOID, 1 }, + + { "gboolean", GI_TYPE_TAG_BOOLEAN, 0 }, + { "gint8", GI_TYPE_TAG_INT8, 0 }, /* Start of BASIC_TYPE_FIXED_OFFSET */ + { "guint8", GI_TYPE_TAG_UINT8, 0 }, + { "gint16", GI_TYPE_TAG_INT16, 0 }, + { "guint16", GI_TYPE_TAG_UINT16, 0 }, + { "gint32", GI_TYPE_TAG_INT32, 0 }, + { "guint32", GI_TYPE_TAG_UINT32, 0 }, + { "gint64", GI_TYPE_TAG_INT64, 0 }, + { "guint64", GI_TYPE_TAG_UINT64, 0 }, + { "gfloat", GI_TYPE_TAG_FLOAT, 0 }, + { "gdouble", GI_TYPE_TAG_DOUBLE, 0 }, + { "GType", GI_TYPE_TAG_GTYPE, 0 }, + { "utf8", GI_TYPE_TAG_UTF8, 1 }, + { "filename", GI_TYPE_TAG_FILENAME,1 }, + { "gunichar", GI_TYPE_TAG_UNICHAR, 0 }, +}; + +static const BasicTypeInfo * +parse_basic (const char *str) +{ + guint i; + guint n_basic = G_N_ELEMENTS (basic_types); + + for (i = 0; i < n_basic; i++) + { + if (strcmp (str, basic_types[i].str) == 0) + return &(basic_types[i]); + } + for (i = 0; i < G_N_ELEMENTS (integer_aliases); i++) + { + if (strcmp (str, integer_aliases[i].str) == 0) + { + switch (integer_aliases[i].size) + { + case sizeof(guint8): + if (integer_aliases[i].is_signed) + return &basic_types[BASIC_TYPE_FIXED_OFFSET]; + else + return &basic_types[BASIC_TYPE_FIXED_OFFSET+1]; + break; + case sizeof(guint16): + if (integer_aliases[i].is_signed) + return &basic_types[BASIC_TYPE_FIXED_OFFSET+2]; + else + return &basic_types[BASIC_TYPE_FIXED_OFFSET+3]; + break; + case sizeof(guint32): + if (integer_aliases[i].is_signed) + return &basic_types[BASIC_TYPE_FIXED_OFFSET+4]; + else + return &basic_types[BASIC_TYPE_FIXED_OFFSET+5]; + break; + case sizeof(guint64): + if (integer_aliases[i].is_signed) + return &basic_types[BASIC_TYPE_FIXED_OFFSET+6]; + else + return &basic_types[BASIC_TYPE_FIXED_OFFSET+7]; + break; + default: + g_assert_not_reached (); + } + } + } + return NULL; +} + +static GIrNodeType * +parse_type_internal (GIrModule *module, + const gchar *str, + char **next, + gboolean in_glib, + gboolean in_gobject) +{ + const BasicTypeInfo *basic; + GIrNodeType *type; + char *temporary_type = NULL; + + type = (GIrNodeType *)_g_ir_node_new (G_IR_NODE_TYPE, module); + + type->unparsed = g_strdup (str); + + /* See comment below on GLib.List handling */ + if (in_gobject && strcmp (str, "Type") == 0) + { + temporary_type = g_strdup ("GLib.Type"); + str = temporary_type; + } + + basic = parse_basic (str); + if (basic != NULL) + { + type->is_basic = TRUE; + type->tag = basic->tag; + type->is_pointer = basic->pointer; + + str += strlen(basic->str); + } + else if (in_glib) + { + /* If we're inside GLib, handle "List" etc. by prefixing with + * "GLib." so the parsing code below doesn't have to get more + * special. + */ + if (g_str_has_prefix (str, "List<") || + strcmp (str, "List") == 0) + { + temporary_type = g_strdup_printf ("GLib.List%s", str + 4); + str = temporary_type; + } + else if (g_str_has_prefix (str, "SList<") || + strcmp (str, "SList") == 0) + { + temporary_type = g_strdup_printf ("GLib.SList%s", str + 5); + str = temporary_type; + } + else if (g_str_has_prefix (str, "HashTable<") || + strcmp (str, "HashTable") == 0) + { + temporary_type = g_strdup_printf ("GLib.HashTable%s", str + 9); + str = temporary_type; + } + else if (g_str_has_prefix (str, "Error<") || + strcmp (str, "Error") == 0) + { + temporary_type = g_strdup_printf ("GLib.Error%s", str + 5); + str = temporary_type; + } + } + + if (basic != NULL) + /* found a basic type */; + else if (g_str_has_prefix (str, "GLib.List") || + g_str_has_prefix (str, "GLib.SList")) + { + str += strlen ("GLib."); + if (g_str_has_prefix (str, "List")) + { + type->tag = GI_TYPE_TAG_GLIST; + type->is_glist = TRUE; + type->is_pointer = TRUE; + str += strlen ("List"); + } + else + { + type->tag = GI_TYPE_TAG_GSLIST; + type->is_gslist = TRUE; + type->is_pointer = TRUE; + str += strlen ("SList"); + } + } + else if (g_str_has_prefix (str, "GLib.HashTable")) + { + str += strlen ("GLib."); + + type->tag = GI_TYPE_TAG_GHASH; + type->is_ghashtable = TRUE; + type->is_pointer = TRUE; + str += strlen ("HashTable"); + } + else if (g_str_has_prefix (str, "GLib.Error")) + { + str += strlen ("GLib."); + + type->tag = GI_TYPE_TAG_ERROR; + type->is_error = TRUE; + type->is_pointer = TRUE; + str += strlen ("Error"); + + if (*str == '<') + { + char *tmp, *end; + (str)++; + + end = strchr (str, '>'); + tmp = g_strndup (str, end - str); + type->errors = g_strsplit (tmp, ",", 0); + g_free (tmp); + + str = end; + } + } + else + { + const char *start; + type->tag = GI_TYPE_TAG_INTERFACE; + type->is_interface = TRUE; + start = str; + + /* must be an interface type */ + while (g_ascii_isalnum (*str) || + *str == '.' || + *str == '-' || + *str == '_' || + *str == ':') + (str)++; + + type->giinterface = g_strndup (start, str - start); + } + + if (next) + *next = (char*)str; + g_assert (type->tag >= 0 && type->tag < GI_TYPE_TAG_N_TYPES); + g_free (temporary_type); + return type; + +/* error: */ + _g_ir_node_free ((GIrNode *)type); + g_free (temporary_type); + return NULL; +} + +static const char * +resolve_aliases (ParseContext *ctx, const gchar *type) +{ + gpointer orig; + gpointer value; + GSList *seen_values = NULL; + const gchar *lookup; + gchar *prefixed; + + if (strchr (type, '.') == NULL) + { + prefixed = g_strdup_printf ("%s.%s", ctx->namespace, type); + lookup = prefixed; + } + else + { + lookup = type; + prefixed = NULL; + } + + seen_values = g_slist_prepend (seen_values, (char*)lookup); + while (g_hash_table_lookup_extended (ctx->current_module->aliases, lookup, &orig, &value)) + { + g_debug ("Resolved: %s => %s\n", lookup, (char*)value); + lookup = value; + if (g_slist_find_custom (seen_values, lookup, + (GCompareFunc)strcmp) != NULL) + break; + seen_values = g_slist_prepend (seen_values, (gchar*)lookup); + } + g_slist_free (seen_values); + + if (lookup == prefixed) + lookup = type; + + g_free (prefixed); + + return lookup; +} + +static void +is_pointer_or_disguised_structure (ParseContext *ctx, + const gchar *type, + gboolean *is_pointer, + gboolean *is_disguised) +{ + const gchar *lookup; + gchar *prefixed; + + if (strchr (type, '.') == NULL) + { + prefixed = g_strdup_printf ("%s.%s", ctx->namespace, type); + lookup = prefixed; + } + else + { + lookup = type; + prefixed = NULL; + } + + if (is_pointer != NULL) + *is_pointer = g_hash_table_lookup (ctx->current_module->pointer_structures, lookup) != NULL; + if (is_disguised != NULL) + *is_disguised = g_hash_table_lookup (ctx->current_module->disguised_structures, lookup) != NULL; + + g_free (prefixed); +} + +static GIrNodeType * +parse_type (ParseContext *ctx, const gchar *type) +{ + GIrNodeType *node; + const BasicTypeInfo *basic; + gboolean in_glib, in_gobject; + + in_glib = strcmp (ctx->namespace, "GLib") == 0; + in_gobject = strcmp (ctx->namespace, "GObject") == 0; + + /* Do not search aliases for basic types */ + basic = parse_basic (type); + if (basic == NULL) + type = resolve_aliases (ctx, type); + + node = parse_type_internal (ctx->current_module, type, NULL, in_glib, in_gobject); + if (node) + g_debug ("Parsed type: %s => %d", type, node->tag); + else + g_critical ("Failed to parse type: '%s'", type); + + return node; +} + +static gboolean +introspectable_prelude (GMarkupParseContext *context, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + ParseState new_state) +{ + const gchar *introspectable_arg; + const gchar *shadowed_by; + gboolean introspectable; + + g_assert (ctx->state != STATE_PASSTHROUGH); + + introspectable_arg = find_attribute ("introspectable", attribute_names, attribute_values); + shadowed_by = find_attribute ("shadowed-by", attribute_names, attribute_values); + + introspectable = !(introspectable_arg && atoi (introspectable_arg) == 0) && shadowed_by == NULL; + + if (introspectable) + state_switch (ctx, new_state); + else + state_switch (ctx, STATE_PASSTHROUGH); + + return introspectable; +} + +static gboolean +start_glib_boxed (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *typename; + const gchar *typeinit; + const gchar *deprecated; + GIrNodeBoxed *boxed; + + if (!(strcmp (element_name, "glib:boxed") == 0 && + ctx->state == STATE_NAMESPACE)) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_BOXED)) + return TRUE; + + name = find_attribute ("glib:name", attribute_names, attribute_values); + typename = find_attribute ("glib:type-name", attribute_names, attribute_values); + typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:name"); + return FALSE; + } + else if (typename == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:type-name"); + return FALSE; + } + else if (typeinit == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:get-type"); + return FALSE; + } + + boxed = (GIrNodeBoxed *) _g_ir_node_new (G_IR_NODE_BOXED, + ctx->current_module); + + ((GIrNode *)boxed)->name = g_strdup (name); + boxed->gtype_name = g_strdup (typename); + boxed->gtype_init = g_strdup (typeinit); + if (deprecated) + boxed->deprecated = TRUE; + else + boxed->deprecated = FALSE; + + push_node (ctx, (GIrNode *)boxed); + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, boxed); + + return TRUE; +} + +static gboolean +start_function (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *shadows; + const gchar *symbol; + const gchar *deprecated; + const gchar *throws; + const gchar *set_property; + const gchar *get_property; + GIrNodeFunction *function; + gboolean found = FALSE; + ParseState in_embedded_state = STATE_NONE; + + switch (ctx->state) + { + case STATE_NAMESPACE: + found = (strcmp (element_name, "function") == 0 || + strcmp (element_name, "callback") == 0); + break; + case STATE_CLASS: + case STATE_BOXED: + case STATE_STRUCT: + case STATE_UNION: + found = strcmp (element_name, "constructor") == 0; + /* fallthrough */ + case STATE_INTERFACE: + found = (found || + strcmp (element_name, "function") == 0 || + strcmp (element_name, "method") == 0 || + strcmp (element_name, "callback") == 0); + break; + case STATE_ENUM: + found = strcmp (element_name, "function") == 0; + break; + case STATE_CLASS_FIELD: + case STATE_STRUCT_FIELD: + found = (found || strcmp (element_name, "callback") == 0); + in_embedded_state = ctx->state; + break; + default: + break; + } + + if (!found) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_FUNCTION)) + return TRUE; + + ctx->in_embedded_state = in_embedded_state; + + name = find_attribute ("name", attribute_names, attribute_values); + shadows = find_attribute ("shadows", attribute_names, attribute_values); + symbol = find_attribute ("c:identifier", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + throws = find_attribute ("throws", attribute_names, attribute_values); + set_property = find_attribute ("glib:set-property", attribute_names, attribute_values); + get_property = find_attribute ("glib:get-property", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + else if (strcmp (element_name, "callback") != 0 && symbol == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "c:identifier"); + return FALSE; + } + + if (shadows) + name = shadows; + + function = (GIrNodeFunction *) _g_ir_node_new (G_IR_NODE_FUNCTION, + ctx->current_module); + + ((GIrNode *)function)->name = g_strdup (name); + function->symbol = g_strdup (symbol); + function->parameters = NULL; + if (deprecated) + function->deprecated = TRUE; + else + function->deprecated = FALSE; + + if (strcmp (element_name, "method") == 0 || + strcmp (element_name, "constructor") == 0) + { + function->is_method = TRUE; + + if (strcmp (element_name, "constructor") == 0) + function->is_constructor = TRUE; + else + function->is_constructor = FALSE; + + if (set_property != NULL) + { + function->is_setter = TRUE; + function->is_getter = FALSE; + function->property = g_strdup (set_property); + } + else if (get_property != NULL) + { + function->is_setter = FALSE; + function->is_getter = TRUE; + function->property = g_strdup (get_property); + } + else + { + function->is_setter = FALSE; + function->is_getter = FALSE; + function->property = NULL; + } + } + else + { + function->is_method = FALSE; + function->is_setter = FALSE; + function->is_getter = FALSE; + function->is_constructor = FALSE; + if (strcmp (element_name, "callback") == 0) + ((GIrNode *)function)->type = G_IR_NODE_CALLBACK; + } + + if (throws && strcmp (throws, "1") == 0) + function->throws = TRUE; + else + function->throws = FALSE; + + if (ctx->node_stack == NULL) + { + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, function); + } + else if (ctx->current_typed) + { + GIrNodeField *field; + + field = (GIrNodeField *)ctx->current_typed; + field->callback = function; + } + else + switch (CURRENT_NODE (ctx)->type) + { + case G_IR_NODE_INTERFACE: + case G_IR_NODE_OBJECT: + { + GIrNodeInterface *iface; + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->members = g_list_append (iface->members, function); + } + break; + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed; + + boxed = (GIrNodeBoxed *)CURRENT_NODE (ctx); + boxed->members = g_list_append (boxed->members, function); + } + break; + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_; + + struct_ = (GIrNodeStruct *)CURRENT_NODE (ctx); + struct_->members = g_list_append (struct_->members, function); } + break; + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_; + + union_ = (GIrNodeUnion *)CURRENT_NODE (ctx); + union_->members = g_list_append (union_->members, function); + } + break; + case G_IR_NODE_ENUM: + case G_IR_NODE_FLAGS: + { + GIrNodeEnum *enum_; + + enum_ = (GIrNodeEnum *)CURRENT_NODE (ctx); + enum_->methods = g_list_append (enum_->methods, function); + } + break; + default: + g_assert_not_reached (); + } + + push_node(ctx, (GIrNode *)function); + + return TRUE; +} + +static void +parse_property_transfer (GIrNodeProperty *property, + const gchar *transfer, + ParseContext *ctx) +{ + if (transfer == NULL) + { +#if 0 + GIrNodeInterface *iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + + g_debug ("required attribute 'transfer-ownership' is missing from " + "property '%s' in type '%s.%s'. Assuming 'none'\n", + property->node.name, ctx->namespace, iface->node.name); +#endif + transfer = "none"; + } + if (strcmp (transfer, "none") == 0) + { + property->transfer = FALSE; + property->shallow_transfer = FALSE; + } + else if (strcmp (transfer, "container") == 0) + { + property->transfer = FALSE; + property->shallow_transfer = TRUE; + } + else if (strcmp (transfer, "full") == 0) + { + property->transfer = TRUE; + property->shallow_transfer = FALSE; + } + else + { + GIrNodeInterface *iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + + g_warning ("Unknown transfer-ownership value: '%s' for property '%s' in " + "type '%s.%s'", transfer, property->node.name, ctx->namespace, + iface->node.name); + } +} + +static gboolean +parse_param_transfer (GIrNodeParam *param, const gchar *transfer, const gchar *name, + GError **error) +{ + if (transfer == NULL) + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "required attribute 'transfer-ownership' missing"); + return FALSE; + } + else if (strcmp (transfer, "none") == 0) + { + param->transfer = FALSE; + param->shallow_transfer = FALSE; + } + else if (strcmp (transfer, "container") == 0) + { + param->transfer = FALSE; + param->shallow_transfer = TRUE; + } + else if (strcmp (transfer, "full") == 0) + { + param->transfer = TRUE; + param->shallow_transfer = FALSE; + } + else + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "invalid value for 'transfer-ownership': %s", transfer); + return FALSE; + } + return TRUE; +} + +static gboolean +start_instance_parameter (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *transfer; + gboolean transfer_full; + + if (!(strcmp (element_name, "instance-parameter") == 0 && + ctx->state == STATE_FUNCTION_PARAMETERS)) + return FALSE; + + transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values); + + state_switch (ctx, STATE_PASSTHROUGH); + + if (g_strcmp0 (transfer, "full") == 0) + transfer_full = TRUE; + else if (g_strcmp0 (transfer, "none") == 0) + transfer_full = FALSE; + else + { + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "invalid value for 'transfer-ownership' for instance parameter: %s", transfer); + return FALSE; + } + + switch (CURRENT_NODE (ctx)->type) + { + case G_IR_NODE_FUNCTION: + case G_IR_NODE_CALLBACK: + { + GIrNodeFunction *func; + + func = (GIrNodeFunction *)CURRENT_NODE (ctx); + func->instance_transfer_full = transfer_full; + } + break; + case G_IR_NODE_SIGNAL: + { + GIrNodeSignal *signal; + + signal = (GIrNodeSignal *)CURRENT_NODE (ctx); + signal->instance_transfer_full = transfer_full; + } + break; + case G_IR_NODE_VFUNC: + { + GIrNodeVFunc *vfunc; + + vfunc = (GIrNodeVFunc *)CURRENT_NODE (ctx); + vfunc->instance_transfer_full = transfer_full; + } + break; + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static gboolean +start_parameter (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *direction; + const gchar *retval; + const gchar *optional; + const gchar *caller_allocates; + const gchar *allow_none; + const gchar *transfer; + const gchar *scope; + const gchar *closure; + const gchar *destroy; + const gchar *skip; + const gchar *nullable; + GIrNodeParam *param; + + if (!(strcmp (element_name, "parameter") == 0 && + ctx->state == STATE_FUNCTION_PARAMETERS)) + return FALSE; + + name = find_attribute ("name", attribute_names, attribute_values); + direction = find_attribute ("direction", attribute_names, attribute_values); + retval = find_attribute ("retval", attribute_names, attribute_values); + optional = find_attribute ("optional", attribute_names, attribute_values); + allow_none = find_attribute ("allow-none", attribute_names, attribute_values); + caller_allocates = find_attribute ("caller-allocates", attribute_names, attribute_values); + transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values); + scope = find_attribute ("scope", attribute_names, attribute_values); + closure = find_attribute ("closure", attribute_names, attribute_values); + destroy = find_attribute ("destroy", attribute_names, attribute_values); + skip = find_attribute ("skip", attribute_names, attribute_values); + nullable = find_attribute ("nullable", attribute_names, attribute_values); + + if (name == NULL) + name = "unknown"; + + param = (GIrNodeParam *)_g_ir_node_new (G_IR_NODE_PARAM, + ctx->current_module); + + ctx->current_typed = (GIrNode*) param; + ctx->current_typed->name = g_strdup (name); + + state_switch (ctx, STATE_FUNCTION_PARAMETER); + + if (direction && strcmp (direction, "out") == 0) + { + param->in = FALSE; + param->out = TRUE; + if (caller_allocates == NULL) + param->caller_allocates = FALSE; + else + param->caller_allocates = strcmp (caller_allocates, "1") == 0; + } + else if (direction && strcmp (direction, "inout") == 0) + { + param->in = TRUE; + param->out = TRUE; + param->caller_allocates = FALSE; + } + else + { + param->in = TRUE; + param->out = FALSE; + param->caller_allocates = FALSE; + } + + if (retval && strcmp (retval, "1") == 0) + param->retval = TRUE; + else + param->retval = FALSE; + + if (optional && strcmp (optional, "1") == 0) + param->optional = TRUE; + else + param->optional = FALSE; + + if (nullable && strcmp (nullable, "1") == 0) + param->nullable = TRUE; + else + param->nullable = FALSE; + + if (allow_none && strcmp (allow_none, "1") == 0) + { + if (param->out) + param->optional = TRUE; + else + param->nullable = TRUE; + } + + if (skip && strcmp (skip, "1") == 0) + param->skip = TRUE; + else + param->skip = FALSE; + + if (!parse_param_transfer (param, transfer, name, error)) + return FALSE; + + if (scope && strcmp (scope, "call") == 0) + param->scope = GI_SCOPE_TYPE_CALL; + else if (scope && strcmp (scope, "async") == 0) + param->scope = GI_SCOPE_TYPE_ASYNC; + else if (scope && strcmp (scope, "notified") == 0) + param->scope = GI_SCOPE_TYPE_NOTIFIED; + else if (scope && strcmp (scope, "forever") == 0) + param->scope = GI_SCOPE_TYPE_FOREVER; + else + param->scope = GI_SCOPE_TYPE_INVALID; + + param->closure = closure ? atoi (closure) : -1; + param->destroy = destroy ? atoi (destroy) : -1; + + ((GIrNode *)param)->name = g_strdup (name); + + switch (CURRENT_NODE (ctx)->type) + { + case G_IR_NODE_FUNCTION: + case G_IR_NODE_CALLBACK: + { + GIrNodeFunction *func; + + func = (GIrNodeFunction *)CURRENT_NODE (ctx); + func->parameters = g_list_append (func->parameters, param); + } + break; + case G_IR_NODE_SIGNAL: + { + GIrNodeSignal *signal; + + signal = (GIrNodeSignal *)CURRENT_NODE (ctx); + signal->parameters = g_list_append (signal->parameters, param); + } + break; + case G_IR_NODE_VFUNC: + { + GIrNodeVFunc *vfunc; + + vfunc = (GIrNodeVFunc *)CURRENT_NODE (ctx); + vfunc->parameters = g_list_append (vfunc->parameters, param); + } + break; + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static gboolean +start_field (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *readable; + const gchar *writable; + const gchar *bits; + const gchar *branch; + GIrNodeField *field; + ParseState target_state; + gboolean introspectable; + + switch (ctx->state) + { + case STATE_CLASS: + target_state = STATE_CLASS_FIELD; + break; + case STATE_BOXED: + target_state = STATE_BOXED_FIELD; + break; + case STATE_STRUCT: + target_state = STATE_STRUCT_FIELD; + break; + case STATE_UNION: + target_state = STATE_UNION_FIELD; + break; + case STATE_INTERFACE: + target_state = STATE_INTERFACE_FIELD; + break; + default: + return FALSE; + } + + if (strcmp (element_name, "field") != 0) + return FALSE; + + g_assert (ctx->state != STATE_PASSTHROUGH); + + /* We handle introspectability specially here; we replace with just gpointer + * for the type. + */ + introspectable = introspectable_prelude (context, attribute_names, attribute_values, ctx, target_state); + + name = find_attribute ("name", attribute_names, attribute_values); + readable = find_attribute ("readable", attribute_names, attribute_values); + writable = find_attribute ("writable", attribute_names, attribute_values); + bits = find_attribute ("bits", attribute_names, attribute_values); + branch = find_attribute ("branch", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + field = (GIrNodeField *)_g_ir_node_new (G_IR_NODE_FIELD, + ctx->current_module); + if (introspectable) + { + ctx->current_typed = (GIrNode*) field; + } + else + { + field->type = parse_type (ctx, "gpointer"); + } + + ((GIrNode *)field)->name = g_strdup (name); + /* Fields are assumed to be read-only. + * (see also girwriter.py and generate.c) + */ + field->readable = readable == NULL || strcmp (readable, "0") == 0; + field->writable = writable != NULL && strcmp (writable, "1") == 0; + + if (bits) + field->bits = atoi (bits); + else + field->bits = 0; + + switch (CURRENT_NODE (ctx)->type) + { + case G_IR_NODE_OBJECT: + { + GIrNodeInterface *iface; + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->members = g_list_append (iface->members, field); + } + break; + case G_IR_NODE_INTERFACE: + { + GIrNodeInterface *iface; + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->members = g_list_append (iface->members, field); + } + break; + case G_IR_NODE_BOXED: + { + GIrNodeBoxed *boxed; + + boxed = (GIrNodeBoxed *)CURRENT_NODE (ctx); + boxed->members = g_list_append (boxed->members, field); + } + break; + case G_IR_NODE_STRUCT: + { + GIrNodeStruct *struct_; + + struct_ = (GIrNodeStruct *)CURRENT_NODE (ctx); + struct_->members = g_list_append (struct_->members, field); + } + break; + case G_IR_NODE_UNION: + { + GIrNodeUnion *union_; + + union_ = (GIrNodeUnion *)CURRENT_NODE (ctx); + union_->members = g_list_append (union_->members, field); + if (branch) + { + GIrNodeConstant *constant; + + constant = (GIrNodeConstant *) _g_ir_node_new (G_IR_NODE_CONSTANT, + ctx->current_module); + ((GIrNode *)constant)->name = g_strdup (name); + constant->value = g_strdup (branch); + constant->type = union_->discriminator_type; + constant->deprecated = FALSE; + + union_->discriminators = g_list_append (union_->discriminators, constant); + } + } + break; + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static gboolean +start_alias (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + + name = find_attribute ("name", attribute_names, attribute_values); + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + ctx->current_alias = g_strdup (name); + state_switch (ctx, STATE_ALIAS); + + return TRUE; +} + +static gboolean +start_enum (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *typename; + const gchar *typeinit; + const gchar *deprecated; + const gchar *error_domain; + GIrNodeEnum *enum_; + + if (!((strcmp (element_name, "enumeration") == 0 && ctx->state == STATE_NAMESPACE) || + (strcmp (element_name, "bitfield") == 0 && ctx->state == STATE_NAMESPACE))) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_ENUM)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + typename = find_attribute ("glib:type-name", attribute_names, attribute_values); + typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values); + error_domain = find_attribute ("glib:error-domain", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + if (strcmp (element_name, "enumeration") == 0) + enum_ = (GIrNodeEnum *) _g_ir_node_new (G_IR_NODE_ENUM, + ctx->current_module); + else + enum_ = (GIrNodeEnum *) _g_ir_node_new (G_IR_NODE_FLAGS, + ctx->current_module); + ((GIrNode *)enum_)->name = g_strdup (name); + enum_->gtype_name = g_strdup (typename); + enum_->gtype_init = g_strdup (typeinit); + enum_->error_domain = g_strdup (error_domain); + + if (deprecated) + enum_->deprecated = TRUE; + else + enum_->deprecated = FALSE; + + push_node (ctx, (GIrNode *) enum_); + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, enum_); + + return TRUE; +} + +static gboolean +start_property (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + ParseState target_state; + const gchar *name; + const gchar *readable; + const gchar *writable; + const gchar *construct; + const gchar *construct_only; + const gchar *transfer; + const gchar *setter; + const gchar *getter; + GIrNodeProperty *property; + GIrNodeInterface *iface; + + if (!(strcmp (element_name, "property") == 0 && + (ctx->state == STATE_CLASS || + ctx->state == STATE_INTERFACE))) + return FALSE; + + if (ctx->state == STATE_CLASS) + target_state = STATE_CLASS_PROPERTY; + else if (ctx->state == STATE_INTERFACE) + target_state = STATE_INTERFACE_PROPERTY; + else + g_assert_not_reached (); + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, target_state)) + return TRUE; + + + name = find_attribute ("name", attribute_names, attribute_values); + readable = find_attribute ("readable", attribute_names, attribute_values); + writable = find_attribute ("writable", attribute_names, attribute_values); + construct = find_attribute ("construct", attribute_names, attribute_values); + construct_only = find_attribute ("construct-only", attribute_names, attribute_values); + transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values); + setter = find_attribute ("setter", attribute_names, attribute_values); + getter = find_attribute ("getter", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + property = (GIrNodeProperty *) _g_ir_node_new (G_IR_NODE_PROPERTY, + ctx->current_module); + ctx->current_typed = (GIrNode*) property; + + ((GIrNode *)property)->name = g_strdup (name); + + /* Assume properties are readable */ + if (readable == NULL || strcmp (readable, "1") == 0) + property->readable = TRUE; + else + property->readable = FALSE; + if (writable && strcmp (writable, "1") == 0) + property->writable = TRUE; + else + property->writable = FALSE; + if (construct && strcmp (construct, "1") == 0) + property->construct = TRUE; + else + property->construct = FALSE; + if (construct_only && strcmp (construct_only, "1") == 0) + property->construct_only = TRUE; + else + property->construct_only = FALSE; + + property->setter = g_strdup (setter); + property->getter = g_strdup (getter); + + parse_property_transfer (property, transfer, ctx); + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->members = g_list_append (iface->members, property); + + return TRUE; +} + +static gint64 +parse_value (const gchar *str) +{ + gchar *shift_op; + + /* FIXME just a quick hack */ + shift_op = strstr (str, "<<"); + + if (shift_op) + { + gint64 base, shift; + + base = g_ascii_strtoll (str, NULL, 10); + shift = g_ascii_strtoll (shift_op + 3, NULL, 10); + + return base << shift; + } + else + return g_ascii_strtoll (str, NULL, 10); + + return 0; +} + +static gboolean +start_member (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *value; + const gchar *deprecated; + const gchar *c_identifier; + GIrNodeEnum *enum_; + GIrNodeValue *value_; + + if (!(strcmp (element_name, "member") == 0 && + ctx->state == STATE_ENUM)) + return FALSE; + + name = find_attribute ("name", attribute_names, attribute_values); + value = find_attribute ("value", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + c_identifier = find_attribute ("c:identifier", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + value_ = (GIrNodeValue *) _g_ir_node_new (G_IR_NODE_VALUE, + ctx->current_module); + + ((GIrNode *)value_)->name = g_strdup (name); + + value_->value = parse_value (value); + + if (deprecated) + value_->deprecated = TRUE; + else + value_->deprecated = FALSE; + + g_hash_table_insert (((GIrNode *)value_)->attributes, + g_strdup ("c:identifier"), + g_strdup (c_identifier)); + + enum_ = (GIrNodeEnum *)CURRENT_NODE (ctx); + enum_->values = g_list_append (enum_->values, value_); + + return TRUE; +} + +static gboolean +start_constant (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + ParseState prev_state; + ParseState target_state; + const gchar *name; + const gchar *value; + const gchar *deprecated; + GIrNodeConstant *constant; + + if (!(strcmp (element_name, "constant") == 0 && + (ctx->state == STATE_NAMESPACE || + ctx->state == STATE_CLASS || + ctx->state == STATE_INTERFACE))) + return FALSE; + + switch (ctx->state) + { + case STATE_NAMESPACE: + target_state = STATE_NAMESPACE_CONSTANT; + break; + case STATE_CLASS: + target_state = STATE_CLASS_CONSTANT; + break; + case STATE_INTERFACE: + target_state = STATE_INTERFACE_CONSTANT; + break; + default: + g_assert_not_reached (); + } + + prev_state = ctx->state; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, target_state)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + value = find_attribute ("value", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + else if (value == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "value"); + return FALSE; + } + + constant = (GIrNodeConstant *) _g_ir_node_new (G_IR_NODE_CONSTANT, + ctx->current_module); + + ((GIrNode *)constant)->name = g_strdup (name); + constant->value = g_strdup (value); + + ctx->current_typed = (GIrNode*) constant; + + if (deprecated) + constant->deprecated = TRUE; + else + constant->deprecated = FALSE; + + if (prev_state == STATE_NAMESPACE) + { + push_node (ctx, (GIrNode *) constant); + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, constant); + } + else + { + GIrNodeInterface *iface; + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->members = g_list_append (iface->members, constant); + } + + return TRUE; +} + +static gboolean +start_interface (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *typename; + const gchar *typeinit; + const gchar *deprecated; + const gchar *glib_type_struct; + GIrNodeInterface *iface; + + if (!(strcmp (element_name, "interface") == 0 && + ctx->state == STATE_NAMESPACE)) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_INTERFACE)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + typename = find_attribute ("glib:type-name", attribute_names, attribute_values); + typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values); + glib_type_struct = find_attribute ("glib:type-struct", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + else if (typename == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:type-name"); + return FALSE; + } + else if (typeinit == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:get-type"); + return FALSE; + } + + iface = (GIrNodeInterface *) _g_ir_node_new (G_IR_NODE_INTERFACE, + ctx->current_module); + ((GIrNode *)iface)->name = g_strdup (name); + iface->gtype_name = g_strdup (typename); + iface->gtype_init = g_strdup (typeinit); + iface->glib_type_struct = g_strdup (glib_type_struct); + if (deprecated) + iface->deprecated = TRUE; + else + iface->deprecated = FALSE; + + push_node (ctx, (GIrNode *) iface); + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, iface); + + return TRUE; +} + +static gboolean +start_class (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *parent; + const gchar *glib_type_struct; + const gchar *typename; + const gchar *typeinit; + const gchar *deprecated; + const gchar *abstract; + const gchar *fundamental; + const gchar *final; + const gchar *ref_func; + const gchar *unref_func; + const gchar *set_value_func; + const gchar *get_value_func; + GIrNodeInterface *iface; + + if (!(strcmp (element_name, "class") == 0 && + ctx->state == STATE_NAMESPACE)) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_CLASS)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + parent = find_attribute ("parent", attribute_names, attribute_values); + glib_type_struct = find_attribute ("glib:type-struct", attribute_names, attribute_values); + typename = find_attribute ("glib:type-name", attribute_names, attribute_values); + typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + abstract = find_attribute ("abstract", attribute_names, attribute_values); + final = find_attribute ("final", attribute_names, attribute_values); + fundamental = find_attribute ("glib:fundamental", attribute_names, attribute_values); + ref_func = find_attribute ("glib:ref-func", attribute_names, attribute_values); + unref_func = find_attribute ("glib:unref-func", attribute_names, attribute_values); + set_value_func = find_attribute ("glib:set-value-func", attribute_names, attribute_values); + get_value_func = find_attribute ("glib:get-value-func", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + else if (typename == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:type-name"); + return FALSE; + } + else if (typeinit == NULL && strcmp (typename, "GObject")) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:get-type"); + return FALSE; + } + + iface = (GIrNodeInterface *) _g_ir_node_new (G_IR_NODE_OBJECT, + ctx->current_module); + ((GIrNode *)iface)->name = g_strdup (name); + iface->gtype_name = g_strdup (typename); + iface->gtype_init = g_strdup (typeinit); + iface->parent = g_strdup (parent); + iface->glib_type_struct = g_strdup (glib_type_struct); + if (deprecated) + iface->deprecated = TRUE; + else + iface->deprecated = FALSE; + + iface->abstract = abstract && strcmp (abstract, "1") == 0; + iface->final_ = final && strcmp (final, "1") == 0; + + if (fundamental) + iface->fundamental = TRUE; + if (ref_func) + iface->ref_func = g_strdup (ref_func); + if (unref_func) + iface->unref_func = g_strdup (unref_func); + if (set_value_func) + iface->set_value_func = g_strdup (set_value_func); + if (get_value_func) + iface->get_value_func = g_strdup (get_value_func); + + push_node (ctx, (GIrNode *) iface); + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, iface); + + return TRUE; +} + +static gboolean +start_type (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *ctype; + gboolean in_alias = FALSE; + gboolean is_array; + gboolean is_varargs; + GIrNodeType *typenode; + + is_array = strcmp (element_name, "array") == 0; + is_varargs = strcmp (element_name, "varargs") == 0; + + if (!(is_array || is_varargs || (strcmp (element_name, "type") == 0))) + return FALSE; + + if (ctx->state == STATE_TYPE) + { + ctx->type_depth++; + ctx->type_stack = g_list_prepend (ctx->type_stack, ctx->type_parameters); + ctx->type_parameters = NULL; + } + else if (ctx->state == STATE_FUNCTION_PARAMETER || + ctx->state == STATE_FUNCTION_RETURN || + ctx->state == STATE_STRUCT_FIELD || + ctx->state == STATE_UNION_FIELD || + ctx->state == STATE_CLASS_PROPERTY || + ctx->state == STATE_CLASS_FIELD || + ctx->state == STATE_INTERFACE_FIELD || + ctx->state == STATE_INTERFACE_PROPERTY || + ctx->state == STATE_BOXED_FIELD || + ctx->state == STATE_NAMESPACE_CONSTANT || + ctx->state == STATE_CLASS_CONSTANT || + ctx->state == STATE_INTERFACE_CONSTANT || + ctx->state == STATE_ALIAS + ) + { + if (ctx->state == STATE_ALIAS) + in_alias = TRUE; + state_switch (ctx, STATE_TYPE); + ctx->type_depth = 1; + ctx->type_stack = NULL; + ctx->type_parameters = NULL; + } + + name = find_attribute ("name", attribute_names, attribute_values); + + if (in_alias && ctx->current_alias) + { + char *key; + char *value; + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + key = g_strdup_printf ("%s.%s", ctx->namespace, ctx->current_alias); + if (!strchr (name, '.')) + { + const BasicTypeInfo *basic = parse_basic (name); + if (!basic) + { + /* For non-basic types, re-qualify the interface */ + value = g_strdup_printf ("%s.%s", ctx->namespace, name); + } + else + { + value = g_strdup (name); + } + } + else + value = g_strdup (name); + + g_hash_table_replace (ctx->aliases, key, value); + + return TRUE; + } + else if (!ctx->current_module || in_alias) + return TRUE; + + if (!ctx->current_typed) + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "The element is invalid here"); + return FALSE; + } + + if (is_varargs) + return TRUE; + + if (is_array) + { + const char *zero; + const char *len; + const char *size; + + typenode = (GIrNodeType *)_g_ir_node_new (G_IR_NODE_TYPE, + ctx->current_module); + + typenode->tag = GI_TYPE_TAG_ARRAY; + typenode->is_pointer = TRUE; + typenode->is_array = TRUE; + + if (name && strcmp (name, "GLib.Array") == 0) { + typenode->array_type = GI_ARRAY_TYPE_ARRAY; + } else if (name && strcmp (name, "GLib.ByteArray") == 0) { + typenode->array_type = GI_ARRAY_TYPE_BYTE_ARRAY; + } else if (name && strcmp (name, "GLib.PtrArray") == 0) { + typenode->array_type = GI_ARRAY_TYPE_PTR_ARRAY; + } else { + typenode->array_type = GI_ARRAY_TYPE_C; + } + + if (typenode->array_type == GI_ARRAY_TYPE_C) { + zero = find_attribute ("zero-terminated", attribute_names, attribute_values); + len = find_attribute ("length", attribute_names, attribute_values); + size = find_attribute ("fixed-size", attribute_names, attribute_values); + + typenode->has_length = len != NULL; + typenode->length = typenode->has_length ? atoi (len) : -1; + + typenode->has_size = size != NULL; + typenode->size = typenode->has_size ? atoi (size) : -1; + + if (zero) + typenode->zero_terminated = strcmp(zero, "1") == 0; + else + /* If neither zero-terminated nor length nor fixed-size is given, assume zero-terminated. */ + typenode->zero_terminated = !(typenode->has_length || typenode->has_size); + + if (typenode->has_size && ctx->current_typed->type == G_IR_NODE_FIELD) + typenode->is_pointer = FALSE; + } else { + typenode->zero_terminated = FALSE; + typenode->has_length = FALSE; + typenode->length = -1; + typenode->has_size = FALSE; + typenode->size = -1; + } + } + else + { + int pointer_depth; + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + pointer_depth = 0; + ctype = find_attribute ("c:type", attribute_names, attribute_values); + if (ctype != NULL) + { + const char *cp = ctype + strlen(ctype) - 1; + while (cp > ctype && *cp-- == '*') + pointer_depth++; + + if (g_str_has_prefix (ctype, "gpointer") + || g_str_has_prefix (ctype, "gconstpointer")) + pointer_depth++; + } + + if (ctx->current_typed->type == G_IR_NODE_PARAM && + ((GIrNodeParam *)ctx->current_typed)->out && + pointer_depth > 0) + pointer_depth--; + + typenode = parse_type (ctx, name); + + /* A "pointer" structure is one where the c:type is a typedef that + * to a pointer to a structure; we used to call them "disguised" + * structures as well. + */ + if (typenode->tag == GI_TYPE_TAG_INTERFACE) + { + gboolean is_pointer = FALSE; + gboolean is_disguised = FALSE; + + is_pointer_or_disguised_structure (ctx, typenode->giinterface, + &is_pointer, + &is_disguised); + + if (is_pointer || is_disguised) + pointer_depth++; + } + + if (pointer_depth > 0) + typenode->is_pointer = TRUE; + } + + ctx->type_parameters = g_list_append (ctx->type_parameters, typenode); + + return TRUE; +} + +static void +end_type_top (ParseContext *ctx) +{ + GIrNodeType *typenode; + + if (!ctx->type_parameters) + goto out; + + typenode = (GIrNodeType*)ctx->type_parameters->data; + + /* Default to pointer for unspecified containers */ + if (typenode->tag == GI_TYPE_TAG_ARRAY || + typenode->tag == GI_TYPE_TAG_GLIST || + typenode->tag == GI_TYPE_TAG_GSLIST) + { + if (typenode->parameter_type1 == NULL) + typenode->parameter_type1 = parse_type (ctx, "gpointer"); + } + else if (typenode->tag == GI_TYPE_TAG_GHASH) + { + if (typenode->parameter_type1 == NULL) + { + typenode->parameter_type1 = parse_type (ctx, "gpointer"); + typenode->parameter_type2 = parse_type (ctx, "gpointer"); + } + } + + switch (ctx->current_typed->type) + { + case G_IR_NODE_PARAM: + { + GIrNodeParam *param = (GIrNodeParam *)ctx->current_typed; + param->type = typenode; + } + break; + case G_IR_NODE_FIELD: + { + GIrNodeField *field = (GIrNodeField *)ctx->current_typed; + field->type = typenode; + } + break; + case G_IR_NODE_PROPERTY: + { + GIrNodeProperty *property = (GIrNodeProperty *) ctx->current_typed; + property->type = typenode; + } + break; + case G_IR_NODE_CONSTANT: + { + GIrNodeConstant *constant = (GIrNodeConstant *)ctx->current_typed; + constant->type = typenode; + } + break; + default: + g_printerr("current node is %d\n", CURRENT_NODE (ctx)->type); + g_assert_not_reached (); + } + g_list_free (ctx->type_parameters); + + out: + ctx->type_depth = 0; + ctx->type_parameters = NULL; + ctx->current_typed = NULL; +} + +static void +end_type_recurse (ParseContext *ctx) +{ + GIrNodeType *parent; + GIrNodeType *param = NULL; + + parent = (GIrNodeType *) ((GList*)ctx->type_stack->data)->data; + if (ctx->type_parameters) + param = (GIrNodeType *) ctx->type_parameters->data; + + if (parent->tag == GI_TYPE_TAG_ARRAY || + parent->tag == GI_TYPE_TAG_GLIST || + parent->tag == GI_TYPE_TAG_GSLIST) + { + g_assert (param != NULL); + + if (parent->parameter_type1 == NULL) + parent->parameter_type1 = param; + else + g_assert_not_reached (); + } + else if (parent->tag == GI_TYPE_TAG_GHASH) + { + g_assert (param != NULL); + + if (parent->parameter_type1 == NULL) + parent->parameter_type1 = param; + else if (parent->parameter_type2 == NULL) + parent->parameter_type2 = param; + else + g_assert_not_reached (); + } + g_list_free (ctx->type_parameters); + ctx->type_parameters = (GList *)ctx->type_stack->data; + ctx->type_stack = g_list_delete_link (ctx->type_stack, ctx->type_stack); +} + +static void +end_type (ParseContext *ctx) +{ + if (ctx->type_depth == 1) + { + end_type_top (ctx); + state_switch (ctx, ctx->prev_state); + } + else + { + end_type_recurse (ctx); + ctx->type_depth--; + } +} + +static gboolean +start_attribute (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *value; + GIrNode *curnode; + + if (strcmp (element_name, "attribute") != 0 || ctx->node_stack == NULL) + return FALSE; + + name = find_attribute ("name", attribute_names, attribute_values); + value = find_attribute ("value", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + if (value == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "value"); + return FALSE; + } + + state_switch (ctx, STATE_ATTRIBUTE); + + curnode = CURRENT_NODE (ctx); + + if (ctx->current_typed && ctx->current_typed->type == G_IR_NODE_PARAM) + { + g_hash_table_insert (ctx->current_typed->attributes, g_strdup (name), g_strdup (value)); + } + else + { + g_hash_table_insert (curnode->attributes, g_strdup (name), g_strdup (value)); + } + + return TRUE; +} + +static gboolean +start_return_value (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + GIrNodeParam *param; + const gchar *transfer; + const gchar *skip; + const gchar *nullable; + + if (!(strcmp (element_name, "return-value") == 0 && + ctx->state == STATE_FUNCTION)) + return FALSE; + + param = (GIrNodeParam *)_g_ir_node_new (G_IR_NODE_PARAM, + ctx->current_module); + param->in = FALSE; + param->out = FALSE; + param->retval = TRUE; + + ctx->current_typed = (GIrNode*) param; + + state_switch (ctx, STATE_FUNCTION_RETURN); + + skip = find_attribute ("skip", attribute_names, attribute_values); + if (skip && strcmp (skip, "1") == 0) + param->skip = TRUE; + else + param->skip = FALSE; + + transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values); + if (!parse_param_transfer (param, transfer, NULL, error)) + return FALSE; + + nullable = find_attribute ("nullable", attribute_names, attribute_values); + if (nullable && g_str_equal (nullable, "1")) + param->nullable = TRUE; + + switch (CURRENT_NODE (ctx)->type) + { + case G_IR_NODE_FUNCTION: + case G_IR_NODE_CALLBACK: + { + GIrNodeFunction *func = (GIrNodeFunction *)CURRENT_NODE (ctx); + func->result = param; + } + break; + case G_IR_NODE_SIGNAL: + { + GIrNodeSignal *signal = (GIrNodeSignal *)CURRENT_NODE (ctx); + signal->result = param; + } + break; + case G_IR_NODE_VFUNC: + { + GIrNodeVFunc *vfunc = (GIrNodeVFunc *)CURRENT_NODE (ctx); + vfunc->result = param; + } + break; + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static gboolean +start_implements (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + GIrNodeInterface *iface; + const char *name; + + if (strcmp (element_name, "implements") != 0 || + !(ctx->state == STATE_CLASS)) + return FALSE; + + state_switch (ctx, STATE_IMPLEMENTS); + + name = find_attribute ("name", attribute_names, attribute_values); + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->interfaces = g_list_append (iface->interfaces, g_strdup (name)); + + return TRUE; +} + +static gboolean +start_glib_signal (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *when; + const gchar *no_recurse; + const gchar *detailed; + const gchar *action; + const gchar *no_hooks; + const gchar *has_class_closure; + GIrNodeInterface *iface; + GIrNodeSignal *signal; + + if (!(strcmp (element_name, "glib:signal") == 0 && + (ctx->state == STATE_CLASS || + ctx->state == STATE_INTERFACE))) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_FUNCTION)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + when = find_attribute ("when", attribute_names, attribute_values); + no_recurse = find_attribute ("no-recurse", attribute_names, attribute_values); + detailed = find_attribute ("detailed", attribute_names, attribute_values); + action = find_attribute ("action", attribute_names, attribute_values); + no_hooks = find_attribute ("no-hooks", attribute_names, attribute_values); + has_class_closure = find_attribute ("has-class-closure", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + signal = (GIrNodeSignal *)_g_ir_node_new (G_IR_NODE_SIGNAL, + ctx->current_module); + + ((GIrNode *)signal)->name = g_strdup (name); + + signal->run_first = FALSE; + signal->run_last = FALSE; + signal->run_cleanup = FALSE; + if (when == NULL || g_ascii_strcasecmp (when, "LAST") == 0) + signal->run_last = TRUE; + else if (g_ascii_strcasecmp (when, "FIRST") == 0) + signal->run_first = TRUE; + else + signal->run_cleanup = TRUE; + + if (no_recurse && strcmp (no_recurse, "1") == 0) + signal->no_recurse = TRUE; + else + signal->no_recurse = FALSE; + if (detailed && strcmp (detailed, "1") == 0) + signal->detailed = TRUE; + else + signal->detailed = FALSE; + if (action && strcmp (action, "1") == 0) + signal->action = TRUE; + else + signal->action = FALSE; + if (no_hooks && strcmp (no_hooks, "1") == 0) + signal->no_hooks = TRUE; + else + signal->no_hooks = FALSE; + if (has_class_closure && strcmp (has_class_closure, "1") == 0) + signal->has_class_closure = TRUE; + else + signal->has_class_closure = FALSE; + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->members = g_list_append (iface->members, signal); + + push_node (ctx, (GIrNode *)signal); + + return TRUE; +} + +static gboolean +start_vfunc (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *must_chain_up; + const gchar *override; + const gchar *is_class_closure; + const gchar *offset; + const gchar *invoker; + const gchar *throws; + GIrNodeInterface *iface; + GIrNodeVFunc *vfunc; + + if (!(strcmp (element_name, "virtual-method") == 0 && + (ctx->state == STATE_CLASS || + ctx->state == STATE_INTERFACE))) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_FUNCTION)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + must_chain_up = find_attribute ("must-chain-up", attribute_names, attribute_values); + override = find_attribute ("override", attribute_names, attribute_values); + is_class_closure = find_attribute ("is-class-closure", attribute_names, attribute_values); + offset = find_attribute ("offset", attribute_names, attribute_values); + invoker = find_attribute ("invoker", attribute_names, attribute_values); + throws = find_attribute ("throws", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + vfunc = (GIrNodeVFunc *)_g_ir_node_new (G_IR_NODE_VFUNC, + ctx->current_module); + + ((GIrNode *)vfunc)->name = g_strdup (name); + + if (must_chain_up && strcmp (must_chain_up, "1") == 0) + vfunc->must_chain_up = TRUE; + else + vfunc->must_chain_up = FALSE; + + if (override && strcmp (override, "always") == 0) + { + vfunc->must_be_implemented = TRUE; + vfunc->must_not_be_implemented = FALSE; + } + else if (override && strcmp (override, "never") == 0) + { + vfunc->must_be_implemented = FALSE; + vfunc->must_not_be_implemented = TRUE; + } + else + { + vfunc->must_be_implemented = FALSE; + vfunc->must_not_be_implemented = FALSE; + } + + if (is_class_closure && strcmp (is_class_closure, "1") == 0) + vfunc->is_class_closure = TRUE; + else + vfunc->is_class_closure = FALSE; + + if (throws && strcmp (throws, "1") == 0) + vfunc->throws = TRUE; + else + vfunc->throws = FALSE; + + if (offset) + vfunc->offset = atoi (offset); + else + vfunc->offset = 0xFFFF; + + vfunc->invoker = g_strdup (invoker); + + iface = (GIrNodeInterface *)CURRENT_NODE (ctx); + iface->members = g_list_append (iface->members, vfunc); + + push_node (ctx, (GIrNode *)vfunc); + + return TRUE; +} + +static gboolean +start_struct (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *deprecated; + const gchar *disguised; + const gchar *opaque; + const gchar *pointer; + const gchar *gtype_name; + const gchar *gtype_init; + const gchar *gtype_struct; + const gchar *foreign; + const gchar *copy_func; + const gchar *free_func; + GIrNodeStruct *struct_; + + if (!(strcmp (element_name, "record") == 0 && + (ctx->state == STATE_NAMESPACE || + ctx->state == STATE_UNION || + ctx->state == STATE_STRUCT || + ctx->state == STATE_CLASS))) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_STRUCT)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + disguised = find_attribute ("disguised", attribute_names, attribute_values); + pointer = find_attribute ("pointer", attribute_names, attribute_values); + opaque = find_attribute ("opaque", attribute_names, attribute_values); + gtype_name = find_attribute ("glib:type-name", attribute_names, attribute_values); + gtype_init = find_attribute ("glib:get-type", attribute_names, attribute_values); + gtype_struct = find_attribute ("glib:is-gtype-struct-for", attribute_names, attribute_values); + foreign = find_attribute ("foreign", attribute_names, attribute_values); + copy_func = find_attribute ("copy-function", attribute_names, attribute_values); + free_func = find_attribute ("free-function", attribute_names, attribute_values); + + if (name == NULL && ctx->node_stack == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + if ((gtype_name == NULL && gtype_init != NULL)) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:type-name"); + return FALSE; + } + if ((gtype_name != NULL && gtype_init == NULL)) + { + MISSING_ATTRIBUTE (context, error, element_name, "glib:get-type"); + return FALSE; + } + + struct_ = (GIrNodeStruct *) _g_ir_node_new (G_IR_NODE_STRUCT, + ctx->current_module); + + ((GIrNode *)struct_)->name = g_strdup (name ? name : ""); + if (deprecated) + struct_->deprecated = TRUE; + else + struct_->deprecated = FALSE; + + if (g_strcmp0 (disguised, "1") == 0) + struct_->disguised = TRUE; + + if (g_strcmp0 (pointer, "1") == 0) + struct_->pointer = TRUE; + + if (g_strcmp0 (opaque, "1") == 0) + struct_->opaque = TRUE; + + struct_->is_gtype_struct = gtype_struct != NULL; + + struct_->gtype_name = g_strdup (gtype_name); + struct_->gtype_init = g_strdup (gtype_init); + + struct_->foreign = (g_strcmp0 (foreign, "1") == 0); + + struct_->copy_func = g_strdup (copy_func); + struct_->free_func = g_strdup (free_func); + + if (ctx->node_stack == NULL) + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, struct_); + push_node (ctx, (GIrNode *)struct_); + return TRUE; +} + +static gboolean +start_union (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *name; + const gchar *deprecated; + const gchar *typename; + const gchar *typeinit; + const gchar *copy_func; + const gchar *free_func; + GIrNodeUnion *union_; + + if (!(strcmp (element_name, "union") == 0 && + (ctx->state == STATE_NAMESPACE || + ctx->state == STATE_UNION || + ctx->state == STATE_STRUCT || + ctx->state == STATE_CLASS))) + return FALSE; + + if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_UNION)) + return TRUE; + + name = find_attribute ("name", attribute_names, attribute_values); + deprecated = find_attribute ("deprecated", attribute_names, attribute_values); + typename = find_attribute ("glib:type-name", attribute_names, attribute_values); + typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values); + copy_func = find_attribute ("copy-function", attribute_names, attribute_values); + free_func = find_attribute ("free-function", attribute_names, attribute_values); + + if (name == NULL && ctx->node_stack == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + return FALSE; + } + + union_ = (GIrNodeUnion *) _g_ir_node_new (G_IR_NODE_UNION, + ctx->current_module); + + ((GIrNode *)union_)->name = g_strdup (name ? name : ""); + union_->gtype_name = g_strdup (typename); + union_->gtype_init = g_strdup (typeinit); + union_->copy_func = g_strdup (copy_func); + union_->free_func = g_strdup (free_func); + if (deprecated) + union_->deprecated = TRUE; + else + union_->deprecated = FALSE; + + if (ctx->node_stack == NULL) + ctx->current_module->entries = + g_list_append (ctx->current_module->entries, union_); + push_node (ctx, (GIrNode *)union_); + return TRUE; +} + +static gboolean +start_discriminator (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + ParseContext *ctx, + GError **error) +{ + const gchar *type; + const gchar *offset; + if (!(strcmp (element_name, "discriminator") == 0 && + ctx->state == STATE_UNION)) + return FALSE; + + type = find_attribute ("type", attribute_names, attribute_values); + offset = find_attribute ("offset", attribute_names, attribute_values); + if (type == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "type"); + return FALSE; + } + else if (offset == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "offset"); + return FALSE; + } + + ((GIrNodeUnion *)CURRENT_NODE (ctx))->discriminator_type + = parse_type (ctx, type); + ((GIrNodeUnion *)CURRENT_NODE (ctx))->discriminator_offset + = atoi (offset); + + return TRUE; +} + +static gboolean +parse_include (GMarkupParseContext *context, + ParseContext *ctx, + const char *name, + const char *version) +{ + GError *error = NULL; + gchar *buffer; + gsize length; + gchar *girpath, *girname; + GIrModule *module; + GList *l; + + for (l = ctx->parser->parsed_modules; l; l = l->next) + { + GIrModule *m = l->data; + + if (strcmp (m->name, name) == 0) + { + if (strcmp (m->version, version) == 0) + { + ctx->include_modules = g_list_prepend (ctx->include_modules, m); + + return TRUE; + } + else + { + g_printerr ("Module '%s' imported with conflicting versions '%s' and '%s'\n", + name, m->version, version); + return FALSE; + } + } + } + + girname = g_strdup_printf ("%s-%s.gir", name, version); + girpath = locate_gir (ctx->parser, girname); + + if (girpath == NULL) + { + g_printerr ("Could not find GIR file '%s'; check XDG_DATA_DIRS or use --includedir\n", + girname); + g_free (girname); + return FALSE; + } + g_free (girname); + + g_debug ("Parsing include %s\n", girpath); + + if (!g_file_get_contents (girpath, &buffer, &length, &error)) + { + g_printerr ("%s: %s\n", girpath, error->message); + g_clear_error (&error); + g_free (girpath); + return FALSE; + } + + module = _g_ir_parser_parse_string (ctx->parser, name, girpath, buffer, length, &error); + g_free (buffer); + if (error != NULL) + { + int line_number, char_number; + g_markup_parse_context_get_position (context, &line_number, &char_number); + g_printerr ("%s:%d:%d: error: %s\n", girpath, line_number, char_number, error->message); + g_clear_error (&error); + g_free (girpath); + return FALSE; + } + g_free (girpath); + + ctx->include_modules = g_list_append (ctx->include_modules, + module); + + return TRUE; +} + +extern GLogLevelFlags logged_levels; + +static void +start_element_handler (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + ParseContext *ctx = user_data; + + if (logged_levels & G_LOG_LEVEL_DEBUG) + { + GString *tags = g_string_new (""); + int i; + for (i = 0; attribute_names[i]; i++) + g_string_append_printf (tags, "%s=\"%s\" ", + attribute_names[i], + attribute_values[i]); + + if (i) + { + g_string_insert_c (tags, 0, ' '); + g_string_truncate (tags, tags->len - 1); + } + g_debug ("<%s%s>", element_name, tags->str); + g_string_free (tags, TRUE); + } + + if (ctx->state == STATE_PASSTHROUGH) + { + ctx->unknown_depth += 1; + return; + } + + switch (element_name[0]) + { + case 'a': + if (ctx->state == STATE_NAMESPACE && strcmp (element_name, "alias") == 0) + { + state_switch (ctx, STATE_ALIAS); + goto out; + } + if (start_type (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_attribute (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + case 'b': + if (start_enum (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + case 'c': + if (start_function (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_constant (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_class (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'd': + if (start_discriminator (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + if (strcmp ("doc", element_name) == 0 || strcmp ("doc-deprecated", element_name) == 0 || + strcmp ("doc-stability", element_name) == 0 || strcmp ("doc-version", element_name) == 0 || + strcmp ("docsection", element_name) == 0) + { + state_switch (ctx, STATE_PASSTHROUGH); + goto out; + } + break; + + case 'e': + if (start_enum (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'f': + if (strcmp ("function-macro", element_name) == 0) + { + state_switch (ctx, STATE_PASSTHROUGH); + goto out; + } + else if (start_function (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_field (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'g': + if (start_glib_boxed (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_glib_signal (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'i': + if (strcmp (element_name, "include") == 0 && + ctx->state == STATE_REPOSITORY) + { + const gchar *name; + const gchar *version; + + name = find_attribute ("name", attribute_names, attribute_values); + version = find_attribute ("version", attribute_names, attribute_values); + + if (name == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "name"); + break; + } + if (version == NULL) + { + MISSING_ATTRIBUTE (context, error, element_name, "version"); + break; + } + + if (!parse_include (context, ctx, name, version)) + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Failed to parse included gir %s-%s", + name, + version); + return; + } + + ctx->dependencies = g_list_prepend (ctx->dependencies, + g_strdup_printf ("%s-%s", name, version)); + + + state_switch (ctx, STATE_INCLUDE); + goto out; + } + if (start_interface (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_implements (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_instance_parameter (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (strcmp (element_name, "c:include") == 0) + { + state_switch (ctx, STATE_C_INCLUDE); + goto out; + } + break; + + case 'm': + if (start_function (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_member (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'n': + if (strcmp (element_name, "namespace") == 0 && ctx->state == STATE_REPOSITORY) + { + const gchar *name, *version, *shared_library, *cprefix; + + if (ctx->current_module != NULL) + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Only one element is currently allowed per "); + goto out; + } + + name = find_attribute ("name", attribute_names, attribute_values); + version = find_attribute ("version", attribute_names, attribute_values); + shared_library = find_attribute ("shared-library", attribute_names, attribute_values); + cprefix = find_attribute ("c:identifier-prefixes", attribute_names, attribute_values); + /* Backwards compatibility; vala currently still generates this */ + if (cprefix == NULL) + cprefix = find_attribute ("c:prefix", attribute_names, attribute_values); + + if (name == NULL) + MISSING_ATTRIBUTE (context, error, element_name, "name"); + else if (version == NULL) + MISSING_ATTRIBUTE (context, error, element_name, "version"); + else + { + GList *l; + + if (strcmp (name, ctx->namespace) != 0) + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + " name element '%s' doesn't match file name '%s'", + name, ctx->namespace); + + ctx->current_module = _g_ir_module_new (name, version, shared_library, cprefix); + + ctx->current_module->aliases = ctx->aliases; + ctx->aliases = NULL; + ctx->current_module->disguised_structures = ctx->disguised_structures; + ctx->current_module->pointer_structures = ctx->pointer_structures; + ctx->disguised_structures = NULL; + ctx->pointer_structures = NULL; + + for (l = ctx->include_modules; l; l = l->next) + _g_ir_module_add_include_module (ctx->current_module, l->data); + + g_list_free (ctx->include_modules); + ctx->include_modules = NULL; + + ctx->modules = g_list_append (ctx->modules, ctx->current_module); + ctx->current_module->dependencies = ctx->dependencies; + + state_switch (ctx, STATE_NAMESPACE); + goto out; + } + } + break; + + case 'p': + if (start_property (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (strcmp (element_name, "parameters") == 0 && + ctx->state == STATE_FUNCTION) + { + state_switch (ctx, STATE_FUNCTION_PARAMETERS); + + goto out; + } + else if (start_parameter (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (strcmp (element_name, "prerequisite") == 0 && + ctx->state == STATE_INTERFACE) + { + const gchar *name; + + name = find_attribute ("name", attribute_names, attribute_values); + + state_switch (ctx, STATE_PREREQUISITE); + + if (name == NULL) + MISSING_ATTRIBUTE (context, error, element_name, "name"); + else + { + GIrNodeInterface *iface; + + iface = (GIrNodeInterface *)CURRENT_NODE(ctx); + iface->prerequisites = g_list_append (iface->prerequisites, g_strdup (name)); + } + goto out; + } + else if (strcmp (element_name, "package") == 0 && + ctx->state == STATE_REPOSITORY) + { + state_switch (ctx, STATE_PACKAGE); + goto out; + } + break; + + case 'r': + if (strcmp (element_name, "repository") == 0 && ctx->state == STATE_START) + { + const gchar *version; + + version = find_attribute ("version", attribute_names, attribute_values); + + if (version == NULL) + MISSING_ATTRIBUTE (context, error, element_name, "version"); + else if (strcmp (version, SUPPORTED_GIR_VERSION) != 0) + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Unsupported version '%s'", + version); + else + state_switch (ctx, STATE_REPOSITORY); + + goto out; + } + else if (start_return_value (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + else if (start_struct (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 's': + if (strcmp ("source-position", element_name) == 0) + { + state_switch (ctx, STATE_PASSTHROUGH); + goto out; + } + break; + case 'u': + if (start_union (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 't': + if (start_type (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + + case 'v': + if (start_vfunc (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + if (start_type (context, element_name, + attribute_names, attribute_values, + ctx, error)) + goto out; + break; + default: + break; + } + + if (*error == NULL && ctx->state != STATE_PASSTHROUGH) + { + gint line_number, char_number; + g_markup_parse_context_get_position (context, &line_number, &char_number); + if (!g_str_has_prefix (element_name, "c:")) + g_printerr ("%s:%d:%d: warning: element %s from state %d is unknown, ignoring\n", + ctx->file_path, line_number, char_number, element_name, + ctx->state); + state_switch (ctx, STATE_PASSTHROUGH); + } + + out: + if (*error) + { + gint line_number, char_number; + g_markup_parse_context_get_position (context, &line_number, &char_number); + + g_printerr ("%s:%d:%d: error: %s\n", ctx->file_path, line_number, char_number, (*error)->message); + } +} + +static gboolean +require_one_of_end_elements (GMarkupParseContext *context, + ParseContext *ctx, + const char *actual_name, + GError **error, + ...) +{ + va_list args; + int line_number, char_number; + const char *expected; + gboolean matched = FALSE; + + va_start (args, error); + + while ((expected = va_arg (args, const char*)) != NULL) + { + if (strcmp (expected, actual_name) == 0) + { + matched = TRUE; + break; + } + } + + va_end (args); + + if (matched) + return TRUE; + + g_markup_parse_context_get_position (context, &line_number, &char_number); + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Unexpected end tag '%s' on line %d char %d; current state=%d (prev=%d)", + actual_name, + line_number, char_number, ctx->state, ctx->prev_state); + return FALSE; +} + +static gboolean +state_switch_end_struct_or_union (GMarkupParseContext *context, + ParseContext *ctx, + const gchar *element_name, + GError **error) +{ + pop_node (ctx); + if (ctx->node_stack == NULL) + { + state_switch (ctx, STATE_NAMESPACE); + } + else + { + if (CURRENT_NODE (ctx)->type == G_IR_NODE_STRUCT) + state_switch (ctx, STATE_STRUCT); + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_UNION) + state_switch (ctx, STATE_UNION); + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_OBJECT) + state_switch (ctx, STATE_CLASS); + else + { + int line_number, char_number; + g_markup_parse_context_get_position (context, &line_number, &char_number); + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Unexpected end tag '%s' on line %d char %d", + element_name, + line_number, char_number); + return FALSE; + } + } + return TRUE; +} + +static gboolean +require_end_element (GMarkupParseContext *context, + ParseContext *ctx, + const char *expected_name, + const char *actual_name, + GError **error) +{ + return require_one_of_end_elements (context, ctx, actual_name, error, expected_name, NULL); +} + +static void +end_element_handler (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + ParseContext *ctx = user_data; + + g_debug ("", element_name); + + switch (ctx->state) + { + case STATE_START: + case STATE_END: + /* no need to GError here, GMarkup already catches this */ + break; + + case STATE_REPOSITORY: + state_switch (ctx, STATE_END); + break; + + case STATE_INCLUDE: + if (require_end_element (context, ctx, "include", element_name, error)) + { + state_switch (ctx, STATE_REPOSITORY); + } + break; + + case STATE_C_INCLUDE: + if (require_end_element (context, ctx, "c:include", element_name, error)) + { + state_switch (ctx, STATE_REPOSITORY); + } + break; + + case STATE_PACKAGE: + if (require_end_element (context, ctx, "package", element_name, error)) + { + state_switch (ctx, STATE_REPOSITORY); + } + break; + + case STATE_NAMESPACE: + if (require_end_element (context, ctx, "namespace", element_name, error)) + { + ctx->current_module = NULL; + state_switch (ctx, STATE_REPOSITORY); + } + break; + + case STATE_ALIAS: + if (require_end_element (context, ctx, "alias", element_name, error)) + { + g_free (ctx->current_alias); + ctx->current_alias = NULL; + state_switch (ctx, STATE_NAMESPACE); + } + break; + + case STATE_FUNCTION_RETURN: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "return-value", element_name, error)) + { + state_switch (ctx, STATE_FUNCTION); + } + break; + + case STATE_FUNCTION_PARAMETERS: + if (require_end_element (context, ctx, "parameters", element_name, error)) + { + state_switch (ctx, STATE_FUNCTION); + } + break; + + case STATE_FUNCTION_PARAMETER: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "parameter", element_name, error)) + { + state_switch (ctx, STATE_FUNCTION_PARAMETERS); + } + break; + + case STATE_FUNCTION: + { + pop_node (ctx); + if (ctx->node_stack == NULL) + { + state_switch (ctx, STATE_NAMESPACE); + } + else + { + g_debug("case STATE_FUNCTION %d", CURRENT_NODE (ctx)->type); + if (ctx->in_embedded_state != STATE_NONE) + { + state_switch (ctx, ctx->in_embedded_state); + ctx->in_embedded_state = STATE_NONE; + } + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_INTERFACE) + state_switch (ctx, STATE_INTERFACE); + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_OBJECT) + state_switch (ctx, STATE_CLASS); + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_BOXED) + state_switch (ctx, STATE_BOXED); + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_STRUCT) + state_switch (ctx, STATE_STRUCT); + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_UNION) + state_switch (ctx, STATE_UNION); + else if (CURRENT_NODE (ctx)->type == G_IR_NODE_ENUM || + CURRENT_NODE (ctx)->type == G_IR_NODE_FLAGS) + state_switch (ctx, STATE_ENUM); + else + { + int line_number, char_number; + g_markup_parse_context_get_position (context, &line_number, &char_number); + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Unexpected end tag '%s' on line %d char %d", + element_name, + line_number, char_number); + } + } + } + break; + + case STATE_CLASS_FIELD: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "field", element_name, error)) + { + state_switch (ctx, STATE_CLASS); + } + break; + + case STATE_CLASS_PROPERTY: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "property", element_name, error)) + { + state_switch (ctx, STATE_CLASS); + } + break; + + case STATE_CLASS: + if (require_end_element (context, ctx, "class", element_name, error)) + { + pop_node (ctx); + state_switch (ctx, STATE_NAMESPACE); + } + break; + + case STATE_INTERFACE_PROPERTY: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "property", element_name, error)) + { + state_switch (ctx, STATE_INTERFACE); + } + break; + + case STATE_INTERFACE_FIELD: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "field", element_name, error)) + { + state_switch (ctx, STATE_INTERFACE); + } + break; + + case STATE_INTERFACE: + if (require_end_element (context, ctx, "interface", element_name, error)) + { + pop_node (ctx); + state_switch (ctx, STATE_NAMESPACE); + } + break; + + case STATE_ENUM: + if (strcmp ("member", element_name) == 0) + break; + else if (strcmp ("function", element_name) == 0) + break; + else if (require_one_of_end_elements (context, ctx, + element_name, error, "enumeration", + "bitfield", NULL)) + { + pop_node (ctx); + state_switch (ctx, STATE_NAMESPACE); + } + break; + + case STATE_BOXED: + if (require_end_element (context, ctx, "glib:boxed", element_name, error)) + { + pop_node (ctx); + state_switch (ctx, STATE_NAMESPACE); + } + break; + + case STATE_BOXED_FIELD: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "field", element_name, error)) + { + state_switch (ctx, STATE_BOXED); + } + break; + + case STATE_STRUCT_FIELD: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "field", element_name, error)) + { + state_switch (ctx, STATE_STRUCT); + } + break; + + case STATE_STRUCT: + if (require_end_element (context, ctx, "record", element_name, error)) + { + state_switch_end_struct_or_union (context, ctx, element_name, error); + } + break; + + case STATE_UNION_FIELD: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "field", element_name, error)) + { + state_switch (ctx, STATE_UNION); + } + break; + + case STATE_UNION: + if (require_end_element (context, ctx, "union", element_name, error)) + { + state_switch_end_struct_or_union (context, ctx, element_name, error); + } + break; + case STATE_IMPLEMENTS: + if (strcmp ("interface", element_name) == 0) + break; + if (require_end_element (context, ctx, "implements", element_name, error)) + state_switch (ctx, STATE_CLASS); + break; + case STATE_PREREQUISITE: + if (require_end_element (context, ctx, "prerequisite", element_name, error)) + state_switch (ctx, STATE_INTERFACE); + break; + case STATE_NAMESPACE_CONSTANT: + case STATE_CLASS_CONSTANT: + case STATE_INTERFACE_CONSTANT: + if (strcmp ("type", element_name) == 0) + break; + if (require_end_element (context, ctx, "constant", element_name, error)) + { + switch (ctx->state) + { + case STATE_NAMESPACE_CONSTANT: + pop_node (ctx); + state_switch (ctx, STATE_NAMESPACE); + break; + case STATE_CLASS_CONSTANT: + state_switch (ctx, STATE_CLASS); + break; + case STATE_INTERFACE_CONSTANT: + state_switch (ctx, STATE_INTERFACE); + break; + default: + g_assert_not_reached (); + break; + } + } + break; + case STATE_TYPE: + if ((strcmp ("type", element_name) == 0) || (strcmp ("array", element_name) == 0) || + (strcmp ("varargs", element_name) == 0)) + { + end_type (ctx); + } + break; + case STATE_ATTRIBUTE: + if (strcmp ("attribute", element_name) == 0) + { + state_switch (ctx, ctx->prev_state); + } + break; + + case STATE_PASSTHROUGH: + ctx->unknown_depth -= 1; + g_assert (ctx->unknown_depth >= 0); + if (ctx->unknown_depth == 0) + state_switch (ctx, ctx->prev_state); + break; + default: + g_error ("Unhandled state %d in end_element_handler\n", ctx->state); + } +} + +static void +text_handler (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + /* FIXME warn about non-whitespace text */ +} + +static void +cleanup (GMarkupParseContext *context, + GError *error, + gpointer user_data) +{ + ParseContext *ctx = user_data; + GList *m; + + for (m = ctx->modules; m; m = m->next) + _g_ir_module_free (m->data); + g_list_free (ctx->modules); + ctx->modules = NULL; + + ctx->current_module = NULL; +} + +/** + * _g_ir_parser_parse_string: + * @parser: a #GIrParser + * @namespace: the namespace of the string + * @filename: (allow-none): Path to parsed file, or %NULL + * @buffer: the data containing the XML + * @length: length of the data + * @error: return location for a #GError, or %NULL + * + * Parse a string that holds a complete GIR XML file, and return a list of a + * a #GirModule for each <namespace/> element within the file. + * + * Returns: (transfer none): a new #GirModule + */ +GIrModule * +_g_ir_parser_parse_string (GIrParser *parser, + const gchar *namespace, + const gchar *filename, + const gchar *buffer, + gssize length, + GError **error) +{ + ParseContext ctx = { 0 }; + GMarkupParseContext *context; + + ctx.parser = parser; + ctx.state = STATE_START; + ctx.file_path = filename; + ctx.namespace = namespace; + ctx.include_modules = NULL; + ctx.aliases = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + ctx.disguised_structures = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + ctx.pointer_structures = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + ctx.type_depth = 0; + ctx.dependencies = NULL; + ctx.current_module = NULL; + + context = g_markup_parse_context_new (&firstpass_parser, 0, &ctx, NULL); + + if (!g_markup_parse_context_parse (context, buffer, length, error)) + goto out; + + if (!g_markup_parse_context_end_parse (context, error)) + goto out; + + g_markup_parse_context_free (context); + + ctx.state = STATE_START; + context = g_markup_parse_context_new (&markup_parser, 0, &ctx, NULL); + if (!g_markup_parse_context_parse (context, buffer, length, error)) + goto out; + + if (!g_markup_parse_context_end_parse (context, error)) + goto out; + + parser->parsed_modules = g_list_concat (g_list_copy (ctx.modules), + parser->parsed_modules); + + out: + + if (ctx.modules == NULL) + { + /* An error occurred before we created a module, so we haven't + * transferred ownership of these hash tables to the module. + */ + g_clear_pointer (&ctx.aliases, g_hash_table_unref); + g_clear_pointer (&ctx.disguised_structures, g_hash_table_unref); + g_clear_pointer (&ctx.pointer_structures, g_hash_table_unref); + g_list_free (ctx.include_modules); + } + + g_markup_parse_context_free (context); + + if (ctx.modules) + return ctx.modules->data; + + if (error && *error == NULL) + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Expected namespace element in the gir file"); + return NULL; +} + +/** + * _g_ir_parser_parse_file: + * @parser: a #GIrParser + * @filename: filename to parse + * @error: return location for a #GError, or %NULL + * + * Parse GIR XML file, and return a list of a a #GirModule for each + * <namespace/> element within the file. + * + * Returns: (transfer container): a newly allocated list of #GIrModule. The modules themselves + * are owned by the #GIrParser and will be freed along with the parser. + */ +GIrModule * +_g_ir_parser_parse_file (GIrParser *parser, + const gchar *filename, + GError **error) +{ + gchar *buffer; + gsize length; + GIrModule *module; + char *dash; + char *namespace; + + if (!g_str_has_suffix (filename, ".gir")) + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_INVALID_CONTENT, + "Expected filename to end with '.gir'"); + return NULL; + } + + g_debug ("[parsing] filename %s", filename); + + namespace = g_path_get_basename (filename); + namespace[strlen(namespace)-4] = '\0'; + + /* Remove version */ + dash = strstr (namespace, "-"); + if (dash != NULL) + *dash = '\0'; + + if (!g_file_get_contents (filename, &buffer, &length, error)) + { + g_free (namespace); + + return NULL; + } + + module = _g_ir_parser_parse_string (parser, namespace, filename, buffer, length, error); + + g_free (namespace); + + g_free (buffer); + + return module; +} + + diff --git a/girepository/girparser.h b/girepository/girparser.h new file mode 100644 index 000000000..4f79c7776 --- /dev/null +++ b/girepository/girparser.h @@ -0,0 +1,50 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: A parser for the XML GIR format + * + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __G_GIR_PARSER_H__ +#define __G_GIR_PARSER_H__ + +#include + +G_BEGIN_DECLS + +#include "girmodule.h" + +typedef struct _GIrParser GIrParser; + +GIrParser *_g_ir_parser_new (void); +void _g_ir_parser_free (GIrParser *parser); +void _g_ir_parser_set_includes (GIrParser *parser, + const gchar *const *includes); + +GIrModule *_g_ir_parser_parse_string (GIrParser *parser, + const gchar *namespace, + const gchar *filename, + const gchar *buffer, + gssize length, + GError **error); +GIrModule *_g_ir_parser_parse_file (GIrParser *parser, + const gchar *filename, + GError **error); + +G_END_DECLS + +#endif /* __G_GIR_PARSER_H__ */ diff --git a/girepository/girwriter.c b/girepository/girwriter.c new file mode 100644 index 000000000..276bb6762 --- /dev/null +++ b/girepository/girwriter.c @@ -0,0 +1,1462 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: IDL generator + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include +#include +#include + +#include "girwriter.h" +#include "girepository.h" +#include "gitypelib-internal.h" + +typedef struct { + FILE *file; + GSList *stack; + gboolean show_all; +} Xml; + +typedef struct { + char *name; + guint has_children : 1; +} XmlElement; + +static XmlElement * +xml_element_new (const char *name) +{ + XmlElement *elem; + + elem = g_slice_new (XmlElement); + elem->name = g_strdup (name); + elem->has_children = FALSE; + return elem; +} + +static void +xml_element_free (XmlElement *elem) +{ + g_free (elem->name); + g_slice_free (XmlElement, elem); +} + +static void +xml_printf (Xml *xml, const char *fmt, ...) G_GNUC_PRINTF (2, 3); + +static void +xml_printf (Xml *xml, const char *fmt, ...) +{ + va_list ap; + char *s; + + va_start (ap, fmt); + s = g_markup_vprintf_escaped (fmt, ap); + fputs (s, xml->file); + g_free (s); + va_end (ap); +} + +static void +xml_start_element (Xml *xml, const char *element_name) +{ + XmlElement *parent = NULL; + + if (xml->stack) + { + parent = xml->stack->data; + + if (!parent->has_children) + xml_printf (xml, ">\n"); + + parent->has_children = TRUE; + } + + xml_printf (xml, "%*s<%s", g_slist_length(xml->stack)*2, "", element_name); + + xml->stack = g_slist_prepend (xml->stack, xml_element_new (element_name)); +} + +static void +xml_end_element (Xml *xml, const char *name) +{ + XmlElement *elem; + + g_assert (xml->stack != NULL); + + elem = xml->stack->data; + xml->stack = g_slist_delete_link (xml->stack, xml->stack); + + if (name != NULL) + g_assert_cmpstr (name, ==, elem->name); + + if (elem->has_children) + xml_printf (xml, "%*s\n", g_slist_length (xml->stack)*2, "", elem->name); + else + xml_printf (xml, "/>\n"); + + xml_element_free (elem); +} + +static void +xml_end_element_unchecked (Xml *xml) +{ + xml_end_element (xml, NULL); +} + +static Xml * +xml_open (FILE *file) +{ + Xml *xml; + + xml = g_slice_new (Xml); + xml->file = file; + xml->stack = NULL; + + return xml; +} + +static void +xml_close (Xml *xml) +{ + g_assert (xml->stack == NULL); + if (xml->file != NULL) + { + fflush (xml->file); + if (xml->file != stdout) + fclose (xml->file); + xml->file = NULL; + } +} + +static void +xml_free (Xml *xml) +{ + xml_close (xml); + g_slice_free (Xml, xml); +} + + +static void +check_unresolved (GIBaseInfo *info) +{ + if (g_base_info_get_type (info) != GI_INFO_TYPE_UNRESOLVED) + return; + + g_critical ("Found unresolved type '%s' '%s'\n", + g_base_info_get_name (info), g_base_info_get_namespace (info)); +} + +static void +write_type_name (const gchar *namespace, + GIBaseInfo *info, + Xml *file) +{ + if (strcmp (namespace, g_base_info_get_namespace (info)) != 0) + xml_printf (file, "%s.", g_base_info_get_namespace (info)); + + xml_printf (file, "%s", g_base_info_get_name (info)); +} + +static void +write_type_name_attribute (const gchar *namespace, + GIBaseInfo *info, + const char *attr_name, + Xml *file) +{ + xml_printf (file, " %s=\"", attr_name); + write_type_name (namespace, info, file); + xml_printf (file, "\""); +} + + static void +write_ownership_transfer (GITransfer transfer, + Xml *file) +{ + switch (transfer) + { + case GI_TRANSFER_NOTHING: + xml_printf (file, " transfer-ownership=\"none\""); + break; + case GI_TRANSFER_CONTAINER: + xml_printf (file, " transfer-ownership=\"container\""); + break; + case GI_TRANSFER_EVERYTHING: + xml_printf (file, " transfer-ownership=\"full\""); + break; + default: + g_assert_not_reached (); + } +} + +static void +write_type_info (const gchar *namespace, + GITypeInfo *info, + Xml *file) +{ + gint tag; + GITypeInfo *type; + gboolean is_pointer; + + check_unresolved ((GIBaseInfo*)info); + + tag = g_type_info_get_tag (info); + is_pointer = g_type_info_is_pointer (info); + + if (tag == GI_TYPE_TAG_VOID) + { + xml_start_element (file, "type"); + + xml_printf (file, " name=\"%s\"", is_pointer ? "any" : "none"); + + xml_end_element (file, "type"); + } + else if (GI_TYPE_TAG_IS_BASIC (tag)) + { + xml_start_element (file, "type"); + xml_printf (file, " name=\"%s\"", g_type_tag_to_string (tag)); + xml_end_element (file, "type"); + } + else if (tag == GI_TYPE_TAG_ARRAY) + { + gint length, size; + const char *name = NULL; + + xml_start_element (file, "array"); + + switch (g_type_info_get_array_type (info)) { + case GI_ARRAY_TYPE_C: + break; + case GI_ARRAY_TYPE_ARRAY: + name = "GLib.Array"; + break; + case GI_ARRAY_TYPE_PTR_ARRAY: + name = "GLib.PtrArray"; + break; + case GI_ARRAY_TYPE_BYTE_ARRAY: + name = "GLib.ByteArray"; + break; + default: + break; + } + + if (name) + xml_printf (file, " name=\"%s\"", name); + + type = g_type_info_get_param_type (info, 0); + + length = g_type_info_get_array_length (info); + if (length >= 0) + xml_printf (file, " length=\"%d\"", length); + + size = g_type_info_get_array_fixed_size (info); + if (size >= 0) + xml_printf (file, " fixed-size=\"%d\"", size); + + if (g_type_info_is_zero_terminated (info)) + xml_printf (file, " zero-terminated=\"1\""); + + write_type_info (namespace, type, file); + + g_base_info_unref ((GIBaseInfo *)type); + + xml_end_element (file, "array"); + } + else if (tag == GI_TYPE_TAG_INTERFACE) + { + GIBaseInfo *iface = g_type_info_get_interface (info); + xml_start_element (file, "type"); + write_type_name_attribute (namespace, iface, "name", file); + xml_end_element (file, "type"); + g_base_info_unref (iface); + } + else if (tag == GI_TYPE_TAG_GLIST) + { + xml_start_element (file, "type"); + xml_printf (file, " name=\"GLib.List\""); + type = g_type_info_get_param_type (info, 0); + if (type) + { + write_type_info (namespace, type, file); + g_base_info_unref ((GIBaseInfo *)type); + } + xml_end_element (file, "type"); + } + else if (tag == GI_TYPE_TAG_GSLIST) + { + xml_start_element (file, "type"); + xml_printf (file, " name=\"GLib.SList\""); + type = g_type_info_get_param_type (info, 0); + if (type) + { + write_type_info (namespace, type, file); + g_base_info_unref ((GIBaseInfo *)type); + } + xml_end_element (file, "type"); + } + else if (tag == GI_TYPE_TAG_GHASH) + { + xml_start_element (file, "type"); + xml_printf (file, " name=\"GLib.HashTable\""); + type = g_type_info_get_param_type (info, 0); + if (type) + { + write_type_info (namespace, type, file); + g_base_info_unref ((GIBaseInfo *)type); + type = g_type_info_get_param_type (info, 1); + write_type_info (namespace, type, file); + g_base_info_unref ((GIBaseInfo *)type); + } + xml_end_element (file, "type"); + } + else if (tag == GI_TYPE_TAG_ERROR) + { + xml_start_element (file, "type"); + xml_printf (file, " name=\"GLib.Error\""); + xml_end_element (file, "type"); + } + else + { + g_printerr ("Unhandled type tag %d\n", tag); + g_assert_not_reached (); + } +} + +static void +write_attributes (Xml *file, + GIBaseInfo *info) +{ + GIAttributeIter iter = { 0, }; + char *name, *value; + + while (g_base_info_iterate_attributes (info, &iter, &name, &value)) + { + xml_start_element (file, "attribute"); + xml_printf (file, " name=\"%s\" value=\"%s\"", name, value); + xml_end_element (file, "attribute"); + } +} + +static void +write_return_value_attributes (Xml *file, + GICallableInfo *info) +{ + GIAttributeIter iter = { 0, }; + char *name, *value; + + while (g_callable_info_iterate_return_attributes (info, &iter, &name, &value)) + { + xml_start_element (file, "attribute"); + xml_printf (file, " name=\"%s\" value=\"%s\"", name, value); + xml_end_element (file, "attribute"); + } +} + +static void +write_constant_value (const gchar *namespace, + GITypeInfo *info, + GIArgument *argument, + Xml *file); + +static void +write_callback_info (const gchar *namespace, + GICallbackInfo *info, + Xml *file); + +static void +write_field_info (const gchar *namespace, + GIFieldInfo *info, + GIConstantInfo *branch, + Xml *file) +{ + const gchar *name; + GIFieldInfoFlags flags; + gint size; + gint offset; + GITypeInfo *type; + GIBaseInfo *interface; + GIArgument value; + + name = g_base_info_get_name ((GIBaseInfo *)info); + flags = g_field_info_get_flags (info); + size = g_field_info_get_size (info); + offset = g_field_info_get_offset (info); + + xml_start_element (file, "field"); + xml_printf (file, " name=\"%s\"", name); + + /* Fields are assumed to be read-only + * (see also girwriter.py and girparser.c) + */ + if (!(flags & GI_FIELD_IS_READABLE)) + xml_printf (file, " readable=\"0\""); + if (flags & GI_FIELD_IS_WRITABLE) + xml_printf (file, " writable=\"1\""); + + if (size) + xml_printf (file, " bits=\"%d\"", size); + + write_attributes (file, (GIBaseInfo*) info); + + type = g_field_info_get_type (info); + + if (branch) + { + xml_printf (file, " branch=\""); + g_base_info_unref ((GIBaseInfo *)type); + type = g_constant_info_get_type (branch); + g_constant_info_get_value (branch, &value); + write_constant_value (namespace, type, &value, file); + xml_printf (file, "\""); + } + + if (file->show_all) + { + if (offset >= 0) + xml_printf (file, "offset=\"%d\"", offset); + } + + interface = g_type_info_get_interface (type); + if (interface && g_base_info_get_type(interface) == GI_INFO_TYPE_CALLBACK) + write_callback_info (namespace, (GICallbackInfo *)interface, file); + else + write_type_info (namespace, type, file); + + if (interface) + g_base_info_unref (interface); + + g_base_info_unref ((GIBaseInfo *)type); + + xml_end_element (file, "field"); +} + +static void +write_callable_info (const gchar *namespace, + GICallableInfo *info, + Xml *file) +{ + GITypeInfo *type; + gint i; + + if (g_callable_info_can_throw_gerror (info)) + xml_printf (file, " throws=\"1\""); + + write_attributes (file, (GIBaseInfo*) info); + + type = g_callable_info_get_return_type (info); + + xml_start_element (file, "return-value"); + + write_ownership_transfer (g_callable_info_get_caller_owns (info), file); + + if (g_callable_info_may_return_null (info)) + xml_printf (file, " allow-none=\"1\""); + + if (g_callable_info_skip_return (info)) + xml_printf (file, " skip=\"1\""); + + write_return_value_attributes (file, info); + + write_type_info (namespace, type, file); + + xml_end_element (file, "return-value"); + + if (g_callable_info_get_n_args (info) <= 0) + return; + + xml_start_element (file, "parameters"); + for (i = 0; i < g_callable_info_get_n_args (info); i++) + { + GIArgInfo *arg = g_callable_info_get_arg (info, i); + + xml_start_element (file, "parameter"); + xml_printf (file, " name=\"%s\"", + g_base_info_get_name ((GIBaseInfo *) arg)); + + write_ownership_transfer (g_arg_info_get_ownership_transfer (arg), file); + + switch (g_arg_info_get_direction (arg)) + { + case GI_DIRECTION_IN: + break; + case GI_DIRECTION_OUT: + xml_printf (file, " direction=\"out\" caller-allocates=\"%s\"", + g_arg_info_is_caller_allocates (arg) ? "1" : "0"); + break; + case GI_DIRECTION_INOUT: + xml_printf (file, " direction=\"inout\""); + break; + default: + g_assert_not_reached (); + } + + if (g_arg_info_may_be_null (arg)) + xml_printf (file, " allow-none=\"1\""); + + if (g_arg_info_is_return_value (arg)) + xml_printf (file, " retval=\"1\""); + + if (g_arg_info_is_optional (arg)) + xml_printf (file, " optional=\"1\""); + + switch (g_arg_info_get_scope (arg)) + { + case GI_SCOPE_TYPE_INVALID: + break; + case GI_SCOPE_TYPE_CALL: + xml_printf (file, " scope=\"call\""); + break; + case GI_SCOPE_TYPE_ASYNC: + xml_printf (file, " scope=\"async\""); + break; + case GI_SCOPE_TYPE_NOTIFIED: + xml_printf (file, " scope=\"notified\""); + break; + case GI_SCOPE_TYPE_FOREVER: + xml_printf (file, " scope=\"forever\""); + break; + default: + g_assert_not_reached (); + } + + if (g_arg_info_get_closure (arg) >= 0) + xml_printf (file, " closure=\"%d\"", g_arg_info_get_closure (arg)); + + if (g_arg_info_get_destroy (arg) >= 0) + xml_printf (file, " destroy=\"%d\"", g_arg_info_get_destroy (arg)); + + if (g_arg_info_is_skip (arg)) + xml_printf (file, " skip=\"1\""); + + write_attributes (file, (GIBaseInfo*) arg); + + type = g_arg_info_get_type (arg); + write_type_info (namespace, type, file); + + xml_end_element (file, "parameter"); + + g_base_info_unref ((GIBaseInfo *)arg); + } + + xml_end_element (file, "parameters"); + g_base_info_unref ((GIBaseInfo *)type); +} + +static void +write_function_info (const gchar *namespace, + GIFunctionInfo *info, + Xml *file) +{ + GIFunctionInfoFlags flags; + const gchar *tag; + const gchar *name; + const gchar *symbol; + gboolean deprecated; + + flags = g_function_info_get_flags (info); + name = g_base_info_get_name ((GIBaseInfo *)info); + symbol = g_function_info_get_symbol (info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + if (flags & GI_FUNCTION_IS_CONSTRUCTOR) + tag = "constructor"; + else if (flags & GI_FUNCTION_IS_METHOD) + tag = "method"; + else + tag = "function"; + + xml_start_element (file, tag); + xml_printf (file, " name=\"%s\" c:identifier=\"%s\"", + name, symbol); + + if ((flags & GI_FUNCTION_IS_SETTER) || (flags & GI_FUNCTION_IS_GETTER)) + { + GIPropertyInfo *property = g_function_info_get_property (info); + + if (property != NULL) + { + const char *property_name = g_base_info_get_name ((GIBaseInfo *)property); + + if (flags & GI_FUNCTION_IS_SETTER) + xml_printf (file, " glib:set-property=\"%s\"", property_name); + else if (flags & GI_FUNCTION_IS_GETTER) + xml_printf (file, " glib:get-property=\"%s\"", property_name); + + g_base_info_unref (property); + } + } + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + write_callable_info (namespace, (GICallableInfo*)info, file); + xml_end_element (file, tag); +} + +static void +write_callback_info (const gchar *namespace, + GICallbackInfo *info, + Xml *file) +{ + const gchar *name; + gboolean deprecated; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + xml_start_element (file, "callback"); + xml_printf (file, " name=\"%s\"", name); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + write_callable_info (namespace, (GICallableInfo*)info, file); + xml_end_element (file, "callback"); +} + +static void +write_struct_info (const gchar *namespace, + GIStructInfo *info, + Xml *file) +{ + const gchar *name; + const gchar *type_name; + const gchar *type_init; + const gchar *func; + gboolean deprecated; + gboolean is_gtype_struct; + gboolean foreign; + gint i; + gint size; + int n_elts; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info); + type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info); + + if (g_base_info_get_type ((GIBaseInfo *)info) == GI_INFO_TYPE_BOXED) + { + xml_start_element (file, "glib:boxed"); + xml_printf (file, " glib:name=\"%s\"", name); + } + else + { + xml_start_element (file, "record"); + xml_printf (file, " name=\"%s\"", name); + } + + if (type_name != NULL) + xml_printf (file, " glib:type-name=\"%s\" glib:get-type=\"%s\"", type_name, type_init); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + is_gtype_struct = g_struct_info_is_gtype_struct (info); + if (is_gtype_struct) + xml_printf (file, " glib:is-gtype-struct=\"1\""); + + func = g_struct_info_get_copy_function (info); + if (func) + xml_printf (file, " copy-function=\"%s\"", func); + + func = g_struct_info_get_free_function (info); + if (func) + xml_printf (file, " free-function=\"%s\"", func); + + write_attributes (file, (GIBaseInfo*) info); + + size = g_struct_info_get_size (info); + if (file->show_all && size >= 0) + xml_printf (file, " size=\"%d\"", size); + + foreign = g_struct_info_is_foreign (info); + if (foreign) + xml_printf (file, " foreign=\"1\""); + + n_elts = g_struct_info_get_n_fields (info) + g_struct_info_get_n_methods (info); + if (n_elts > 0) + { + for (i = 0; i < g_struct_info_get_n_fields (info); i++) + { + GIFieldInfo *field = g_struct_info_get_field (info, i); + write_field_info (namespace, field, NULL, file); + g_base_info_unref ((GIBaseInfo *)field); + } + + for (i = 0; i < g_struct_info_get_n_methods (info); i++) + { + GIFunctionInfo *function = g_struct_info_get_method (info, i); + write_function_info (namespace, function, file); + g_base_info_unref ((GIBaseInfo *)function); + } + + } + + xml_end_element_unchecked (file); +} + +static void +write_value_info (const gchar *namespace, + GIValueInfo *info, + Xml *file) +{ + const gchar *name; + gint64 value; + gchar *value_str; + gboolean deprecated; + + name = g_base_info_get_name ((GIBaseInfo *)info); + value = g_value_info_get_value (info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + xml_start_element (file, "member"); + value_str = g_strdup_printf ("%" G_GINT64_FORMAT, value); + xml_printf (file, " name=\"%s\" value=\"%s\"", name, value_str); + g_free (value_str); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + write_attributes (file, (GIBaseInfo*) info); + + xml_end_element (file, "member"); +} + +static void +write_constant_value (const gchar *namespace, + GITypeInfo *type, + GIArgument *value, + Xml *file) +{ + switch (g_type_info_get_tag (type)) + { + case GI_TYPE_TAG_BOOLEAN: + xml_printf (file, "%d", value->v_boolean); + break; + case GI_TYPE_TAG_INT8: + xml_printf (file, "%d", value->v_int8); + break; + case GI_TYPE_TAG_UINT8: + xml_printf (file, "%d", value->v_uint8); + break; + case GI_TYPE_TAG_INT16: + xml_printf (file, "%" G_GINT16_FORMAT, value->v_int16); + break; + case GI_TYPE_TAG_UINT16: + xml_printf (file, "%" G_GUINT16_FORMAT, value->v_uint16); + break; + case GI_TYPE_TAG_INT32: + xml_printf (file, "%" G_GINT32_FORMAT, value->v_int32); + break; + case GI_TYPE_TAG_UINT32: + xml_printf (file, "%" G_GUINT32_FORMAT, value->v_uint32); + break; + case GI_TYPE_TAG_INT64: + xml_printf (file, "%" G_GINT64_FORMAT, value->v_int64); + break; + case GI_TYPE_TAG_UINT64: + xml_printf (file, "%" G_GUINT64_FORMAT, value->v_uint64); + break; + case GI_TYPE_TAG_FLOAT: + xml_printf (file, "%f", (double)value->v_float); + break; + case GI_TYPE_TAG_DOUBLE: + xml_printf (file, "%f", value->v_double); + break; + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + xml_printf (file, "%s", value->v_string); + break; + default: + g_assert_not_reached (); + } +} + +static void +write_constant_info (const gchar *namespace, + GIConstantInfo *info, + Xml *file) +{ + GITypeInfo *type; + const gchar *name; + GIArgument value; + + name = g_base_info_get_name ((GIBaseInfo *)info); + + xml_start_element (file, "constant"); + xml_printf (file, " name=\"%s\"", name); + + type = g_constant_info_get_type (info); + xml_printf (file, " value=\""); + + g_constant_info_get_value (info, &value); + write_constant_value (namespace, type, &value, file); + xml_printf (file, "\""); + + write_type_info (namespace, type, file); + + write_attributes (file, (GIBaseInfo*) info); + + xml_end_element (file, "constant"); + + g_base_info_unref ((GIBaseInfo *)type); +} + + +static void +write_enum_info (const gchar *namespace, + GIEnumInfo *info, + Xml *file) +{ + const gchar *name; + const gchar *type_name; + const gchar *type_init; + const gchar *error_domain; + gboolean deprecated; + gint i; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info); + type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info); + error_domain = g_enum_info_get_error_domain (info); + + if (g_base_info_get_type ((GIBaseInfo *)info) == GI_INFO_TYPE_ENUM) + xml_start_element (file, "enumeration"); + else + xml_start_element (file, "bitfield"); + xml_printf (file, " name=\"%s\"", name); + + if (type_init) + xml_printf (file, " glib:type-name=\"%s\" glib:get-type=\"%s\"", type_name, type_init); + if (error_domain) + xml_printf (file, " glib:error-domain=\"%s\"", error_domain); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + write_attributes (file, (GIBaseInfo*) info); + + for (i = 0; i < g_enum_info_get_n_values (info); i++) + { + GIValueInfo *value = g_enum_info_get_value (info, i); + write_value_info (namespace, value, file); + g_base_info_unref ((GIBaseInfo *)value); + } + + xml_end_element_unchecked (file); +} + +static void +write_signal_info (const gchar *namespace, + GISignalInfo *info, + Xml *file) +{ + GSignalFlags flags; + const gchar *name; + gboolean deprecated; + + name = g_base_info_get_name ((GIBaseInfo *)info); + flags = g_signal_info_get_flags (info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + xml_start_element (file, "glib:signal"); + xml_printf (file, " name=\"%s\"", name); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + if (flags & G_SIGNAL_RUN_FIRST) + xml_printf (file, " when=\"FIRST\""); + else if (flags & G_SIGNAL_RUN_LAST) + xml_printf (file, " when=\"LAST\""); + else if (flags & G_SIGNAL_RUN_CLEANUP) + xml_printf (file, " when=\"CLEANUP\""); + + if (flags & G_SIGNAL_NO_RECURSE) + xml_printf (file, " no-recurse=\"1\""); + + if (flags & G_SIGNAL_DETAILED) + xml_printf (file, " detailed=\"1\""); + + if (flags & G_SIGNAL_ACTION) + xml_printf (file, " action=\"1\""); + + if (flags & G_SIGNAL_NO_HOOKS) + xml_printf (file, " no-hooks=\"1\""); + + write_callable_info (namespace, (GICallableInfo*)info, file); + + xml_end_element (file, "glib:signal"); +} + +static void +write_vfunc_info (const gchar *namespace, + GIVFuncInfo *info, + Xml *file) +{ + GIVFuncInfoFlags flags; + const gchar *name; + GIFunctionInfo *invoker; + gboolean deprecated; + gint offset; + + name = g_base_info_get_name ((GIBaseInfo *)info); + flags = g_vfunc_info_get_flags (info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + offset = g_vfunc_info_get_offset (info); + invoker = g_vfunc_info_get_invoker (info); + + xml_start_element (file, "virtual-method"); + xml_printf (file, " name=\"%s\"", name); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + if (flags & GI_VFUNC_MUST_CHAIN_UP) + xml_printf (file, " must-chain-up=\"1\""); + + if (flags & GI_VFUNC_MUST_OVERRIDE) + xml_printf (file, " override=\"always\""); + else if (flags & GI_VFUNC_MUST_NOT_OVERRIDE) + xml_printf (file, " override=\"never\""); + + xml_printf (file, " offset=\"%d\"", offset); + + if (invoker) + { + xml_printf (file, " invoker=\"%s\"", g_base_info_get_name ((GIBaseInfo*)invoker)); + g_base_info_unref ((GIBaseInfo *)invoker); + } + + write_callable_info (namespace, (GICallableInfo*)info, file); + + xml_end_element (file, "virtual-method"); +} + +static void +write_property_info (const gchar *namespace, + GIPropertyInfo *info, + Xml *file) +{ + GParamFlags flags; + const gchar *name; + gboolean deprecated; + GITypeInfo *type; + + name = g_base_info_get_name ((GIBaseInfo *)info); + flags = g_property_info_get_flags (info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + xml_start_element (file, "property"); + xml_printf (file, " name=\"%s\"", name); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + /* Properties are assumed to be read-only (see also girwriter.py) */ + if (!(flags & G_PARAM_READABLE)) + xml_printf (file, " readable=\"0\""); + if (flags & G_PARAM_WRITABLE) + xml_printf (file, " writable=\"1\""); + + if (flags & G_PARAM_CONSTRUCT) + xml_printf (file, " construct=\"1\""); + + if (flags & G_PARAM_CONSTRUCT_ONLY) + xml_printf (file, " construct-only=\"1\""); + + if (flags & G_PARAM_READABLE) + { + GIFunctionInfo *getter = g_property_info_get_getter (info); + + if (getter != NULL) + { + xml_printf (file, " getter=\"%s\"", g_base_info_get_name ((GIBaseInfo *) getter)); + g_base_info_unref ((GIBaseInfo *) getter); + } + } + + if (flags & G_PARAM_WRITABLE) + { + GIFunctionInfo *setter = g_property_info_get_setter (info); + + if (setter != NULL) + { + xml_printf (file, " setter=\"%s\"", g_base_info_get_name ((GIBaseInfo *) setter)); + g_base_info_unref ((GIBaseInfo *) setter); + } + } + + write_ownership_transfer (g_property_info_get_ownership_transfer (info), file); + + write_attributes (file, (GIBaseInfo*) info); + + type = g_property_info_get_type (info); + + write_type_info (namespace, type, file); + + xml_end_element (file, "property"); +} + +static void +write_object_info (const gchar *namespace, + GIObjectInfo *info, + Xml *file) +{ + const gchar *name; + const gchar *type_name; + const gchar *type_init; + const gchar *func; + gboolean deprecated; + gboolean is_abstract; + gboolean is_fundamental; + gboolean is_final; + GIObjectInfo *pnode; + GIStructInfo *class_struct; + gint i; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + is_abstract = g_object_info_get_abstract (info); + is_fundamental = g_object_info_get_fundamental (info); + is_final = g_object_info_get_final (info); + + type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info); + type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info); + xml_start_element (file, "class"); + xml_printf (file, " name=\"%s\"", name); + + pnode = g_object_info_get_parent (info); + if (pnode) + { + write_type_name_attribute (namespace, (GIBaseInfo *)pnode, "parent", file); + g_base_info_unref ((GIBaseInfo *)pnode); + } + + class_struct = g_object_info_get_class_struct (info); + if (class_struct) + { + write_type_name_attribute (namespace, (GIBaseInfo*) class_struct, "glib:type-struct", file); + g_base_info_unref ((GIBaseInfo*)class_struct); + } + + if (is_abstract) + xml_printf (file, " abstract=\"1\""); + + if (is_final) + xml_printf (file, " final=\"1\""); + + xml_printf (file, " glib:type-name=\"%s\" glib:get-type=\"%s\"", type_name, type_init); + + if (is_fundamental) + xml_printf (file, " glib:fundamental=\"1\""); + + func = g_object_info_get_unref_function (info); + if (func) + xml_printf (file, " glib:unref-function=\"%s\"", func); + + func = g_object_info_get_ref_function (info); + if (func) + xml_printf (file, " glib:ref-function=\"%s\"", func); + + func = g_object_info_get_set_value_function (info); + if (func) + xml_printf (file, " glib:set-value-function=\"%s\"", func); + + func = g_object_info_get_get_value_function (info); + if (func) + xml_printf (file, " glib:get-value-function=\"%s\"", func); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + write_attributes (file, (GIBaseInfo*) info); + + if (g_object_info_get_n_interfaces (info) > 0) + { + for (i = 0; i < g_object_info_get_n_interfaces (info); i++) + { + GIInterfaceInfo *imp = g_object_info_get_interface (info, i); + xml_start_element (file, "implements"); + write_type_name_attribute (namespace, (GIBaseInfo *)imp, "name", file); + xml_end_element (file, "implements"); + g_base_info_unref ((GIBaseInfo*)imp); + } + } + + for (i = 0; i < g_object_info_get_n_fields (info); i++) + { + GIFieldInfo *field = g_object_info_get_field (info, i); + write_field_info (namespace, field, NULL, file); + g_base_info_unref ((GIBaseInfo *)field); + } + + for (i = 0; i < g_object_info_get_n_methods (info); i++) + { + GIFunctionInfo *function = g_object_info_get_method (info, i); + write_function_info (namespace, function, file); + g_base_info_unref ((GIBaseInfo *)function); + } + + for (i = 0; i < g_object_info_get_n_properties (info); i++) + { + GIPropertyInfo *prop = g_object_info_get_property (info, i); + write_property_info (namespace, prop, file); + g_base_info_unref ((GIBaseInfo *)prop); + } + + for (i = 0; i < g_object_info_get_n_signals (info); i++) + { + GISignalInfo *signal = g_object_info_get_signal (info, i); + write_signal_info (namespace, signal, file); + g_base_info_unref ((GIBaseInfo *)signal); + } + + for (i = 0; i < g_object_info_get_n_vfuncs (info); i++) + { + GIVFuncInfo *vfunc = g_object_info_get_vfunc (info, i); + write_vfunc_info (namespace, vfunc, file); + g_base_info_unref ((GIBaseInfo *)vfunc); + } + + for (i = 0; i < g_object_info_get_n_constants (info); i++) + { + GIConstantInfo *constant = g_object_info_get_constant (info, i); + write_constant_info (namespace, constant, file); + g_base_info_unref ((GIBaseInfo *)constant); + } + + xml_end_element (file, "class"); +} + +static void +write_interface_info (const gchar *namespace, + GIInterfaceInfo *info, + Xml *file) +{ + const gchar *name; + const gchar *type_name; + const gchar *type_init; + GIStructInfo *class_struct; + gboolean deprecated; + gint i; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info); + type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info); + xml_start_element (file, "interface"); + xml_printf (file, " name=\"%s\" glib:type-name=\"%s\" glib:get-type=\"%s\"", + name, type_name, type_init); + + class_struct = g_interface_info_get_iface_struct (info); + if (class_struct) + { + write_type_name_attribute (namespace, (GIBaseInfo*) class_struct, "glib:type-struct", file); + g_base_info_unref ((GIBaseInfo*)class_struct); + } + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + write_attributes (file, (GIBaseInfo*) info); + + if (g_interface_info_get_n_prerequisites (info) > 0) + { + for (i = 0; i < g_interface_info_get_n_prerequisites (info); i++) + { + GIBaseInfo *req = g_interface_info_get_prerequisite (info, i); + + xml_start_element (file, "prerequisite"); + write_type_name_attribute (namespace, req, "name", file); + + xml_end_element_unchecked (file); + g_base_info_unref (req); + } + } + + for (i = 0; i < g_interface_info_get_n_methods (info); i++) + { + GIFunctionInfo *function = g_interface_info_get_method (info, i); + write_function_info (namespace, function, file); + g_base_info_unref ((GIBaseInfo *)function); + } + + for (i = 0; i < g_interface_info_get_n_properties (info); i++) + { + GIPropertyInfo *prop = g_interface_info_get_property (info, i); + write_property_info (namespace, prop, file); + g_base_info_unref ((GIBaseInfo *)prop); + } + + for (i = 0; i < g_interface_info_get_n_signals (info); i++) + { + GISignalInfo *signal = g_interface_info_get_signal (info, i); + write_signal_info (namespace, signal, file); + g_base_info_unref ((GIBaseInfo *)signal); + } + + for (i = 0; i < g_interface_info_get_n_vfuncs (info); i++) + { + GIVFuncInfo *vfunc = g_interface_info_get_vfunc (info, i); + write_vfunc_info (namespace, vfunc, file); + g_base_info_unref ((GIBaseInfo *)vfunc); + } + + for (i = 0; i < g_interface_info_get_n_constants (info); i++) + { + GIConstantInfo *constant = g_interface_info_get_constant (info, i); + write_constant_info (namespace, constant, file); + g_base_info_unref ((GIBaseInfo *)constant); + } + + xml_end_element (file, "interface"); +} + +static void +write_union_info (const gchar *namespace, + GIUnionInfo *info, + Xml *file) +{ + const gchar *name; + const gchar *type_name; + const gchar *type_init; + const gchar *func; + gboolean deprecated; + gint i; + gint size; + + name = g_base_info_get_name ((GIBaseInfo *)info); + deprecated = g_base_info_is_deprecated ((GIBaseInfo *)info); + + type_name = g_registered_type_info_get_type_name ((GIRegisteredTypeInfo*)info); + type_init = g_registered_type_info_get_type_init ((GIRegisteredTypeInfo*)info); + + xml_start_element (file, "union"); + xml_printf (file, " name=\"%s\"", name); + + if (type_name) + xml_printf (file, " type-name=\"%s\" get-type=\"%s\"", type_name, type_init); + + if (deprecated) + xml_printf (file, " deprecated=\"1\""); + + size = g_union_info_get_size (info); + if (file->show_all && size >= 0) + xml_printf (file, " size=\"%d\"", size); + + func = g_union_info_get_copy_function (info); + if (func) + xml_printf (file, " copy-function=\"%s\"", func); + + func = g_union_info_get_free_function (info); + if (func) + xml_printf (file, " free-function=\"%s\"", func); + + write_attributes (file, (GIBaseInfo*) info); + + if (g_union_info_is_discriminated (info)) + { + gint offset; + GITypeInfo *type; + + offset = g_union_info_get_discriminator_offset (info); + type = g_union_info_get_discriminator_type (info); + + xml_start_element (file, "discriminator"); + xml_printf (file, " offset=\"%d\" type=\"", offset); + write_type_info (namespace, type, file); + xml_end_element (file, "discriminator"); + g_base_info_unref ((GIBaseInfo *)type); + } + + for (i = 0; i < g_union_info_get_n_fields (info); i++) + { + GIFieldInfo *field = g_union_info_get_field (info, i); + GIConstantInfo *constant = g_union_info_get_discriminator (info, i); + write_field_info (namespace, field, constant, file); + g_base_info_unref ((GIBaseInfo *)field); + if (constant) + g_base_info_unref ((GIBaseInfo *)constant); + } + + for (i = 0; i < g_union_info_get_n_methods (info); i++) + { + GIFunctionInfo *function = g_union_info_get_method (info, i); + write_function_info (namespace, function, file); + g_base_info_unref ((GIBaseInfo *)function); + } + + xml_end_element (file, "union"); +} + + +/** + * gir_writer_write: + * @filename: filename to write to + * @namespace: GIR namespace to write + * @needs_prefix: if the filename needs prefixing + * @show_all: if field size calculations should be included + * + * Writes the output of a typelib represented by @namespace + * into a GIR xml file named @filename. + */ +void +gir_writer_write (const char *filename, + const char *namespace, + gboolean needs_prefix, + gboolean show_all) +{ + FILE *ofile; + gint i, j; + char **dependencies; + GIRepository *repository; + Xml *xml; + + repository = g_irepository_get_default (); + + if (filename == NULL) + ofile = stdout; + else + { + gchar *full_filename; + + if (needs_prefix) + full_filename = g_strdup_printf ("%s-%s", namespace, filename); + else + full_filename = g_strdup (filename); + ofile = g_fopen (filename, "w"); + + if (ofile == NULL) + { + g_fprintf (stderr, "failed to open '%s': %s\n", + full_filename, g_strerror (errno)); + g_free (full_filename); + + return; + } + + g_free (full_filename); + } + + xml = xml_open (ofile); + xml->show_all = show_all; + xml_printf (xml, "\n"); + xml_start_element (xml, "repository"); + xml_printf (xml, " version=\"1.0\"\n" + " xmlns=\"http://www.gtk.org/introspection/core/1.0\"\n" + " xmlns:c=\"http://www.gtk.org/introspection/c/1.0\"\n" + " xmlns:glib=\"http://www.gtk.org/introspection/glib/1.0\""); + + dependencies = g_irepository_get_immediate_dependencies (repository, + namespace); + if (dependencies != NULL) + { + for (i = 0; dependencies[i]; i++) + { + char **parts = g_strsplit (dependencies[i], "-", 2); + xml_start_element (xml, "include"); + xml_printf (xml, " name=\"%s\" version=\"%s\"", parts[0], parts[1]); + xml_end_element (xml, "include"); + g_strfreev (parts); + } + } + + if (TRUE) + { + const gchar *shared_library; + const gchar *c_prefix; + const char *ns = namespace; + const char *version; + gint n_infos; + + version = g_irepository_get_version (repository, ns); + + shared_library = g_irepository_get_shared_library (repository, ns); + c_prefix = g_irepository_get_c_prefix (repository, ns); + xml_start_element (xml, "namespace"); + xml_printf (xml, " name=\"%s\" version=\"%s\"", ns, version); + if (shared_library) + xml_printf (xml, " shared-library=\"%s\"", shared_library); + if (c_prefix) + xml_printf (xml, " c:prefix=\"%s\"", c_prefix); + + n_infos = g_irepository_get_n_infos (repository, ns); + for (j = 0; j < n_infos; j++) + { + GIBaseInfo *info = g_irepository_get_info (repository, ns, j); + switch (g_base_info_get_type (info)) + { + case GI_INFO_TYPE_FUNCTION: + write_function_info (ns, (GIFunctionInfo *)info, xml); + break; + + case GI_INFO_TYPE_CALLBACK: + write_callback_info (ns, (GICallbackInfo *)info, xml); + break; + + case GI_INFO_TYPE_STRUCT: + case GI_INFO_TYPE_BOXED: + write_struct_info (ns, (GIStructInfo *)info, xml); + break; + + case GI_INFO_TYPE_UNION: + write_union_info (ns, (GIUnionInfo *)info, xml); + break; + + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + write_enum_info (ns, (GIEnumInfo *)info, xml); + break; + + case GI_INFO_TYPE_CONSTANT: + write_constant_info (ns, (GIConstantInfo *)info, xml); + break; + + case GI_INFO_TYPE_OBJECT: + write_object_info (ns, (GIObjectInfo *)info, xml); + break; + + case GI_INFO_TYPE_INTERFACE: + write_interface_info (ns, (GIInterfaceInfo *)info, xml); + break; + + default: + g_error ("unknown info type %d\n", g_base_info_get_type (info)); + } + + g_base_info_unref (info); + } + + xml_end_element (xml, "namespace"); + } + + xml_end_element (xml, "repository"); + + xml_free (xml); +} diff --git a/girepository/girwriter.h b/girepository/girwriter.h new file mode 100644 index 000000000..85f74a79e --- /dev/null +++ b/girepository/girwriter.h @@ -0,0 +1,30 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: IDL writer + * + * Copyright (C) 2007 Johan Dahlin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIRWRITER_H__ +#define __GIRWRITER_H__ + +void gir_writer_write (const char *filename, + const char *namespace, + gboolean needs_prefix, + gboolean show_all); + +#endif /* __GIRWRITER_H__ */ diff --git a/girepository/gisignalinfo.c b/girepository/gisignalinfo.c new file mode 100644 index 000000000..9d5abec56 --- /dev/null +++ b/girepository/gisignalinfo.c @@ -0,0 +1,140 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Signal implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" + +/** + * SECTION:gisignalinfo + * @title: GISignalInfo + * @short_description: Struct representing a signal + * + * GISignalInfo represents a signal. + * + * It's a sub-struct of #GICallableInfo and contains a set of flags and + * a class closure. + * + * See #GICallableInfo for information on how to retreive arguments + * and other metadata from the signal. + */ + +/** + * g_signal_info_get_flags: + * @info: a #GISignalInfo + * + * Obtain the flags for this signal info. See #GSignalFlags for + * more information about possible flag values. + * + * Returns: the flags + */ +GSignalFlags +g_signal_info_get_flags (GISignalInfo *info) +{ + GSignalFlags flags; + GIRealInfo *rinfo = (GIRealInfo *)info; + SignalBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_SIGNAL_INFO (info), 0); + + blob = (SignalBlob *)&rinfo->typelib->data[rinfo->offset]; + flags = 0; + + if (blob->run_first) + flags = flags | G_SIGNAL_RUN_FIRST; + + if (blob->run_last) + flags = flags | G_SIGNAL_RUN_LAST; + + if (blob->run_cleanup) + flags = flags | G_SIGNAL_RUN_CLEANUP; + + if (blob->no_recurse) + flags = flags | G_SIGNAL_NO_RECURSE; + + if (blob->detailed) + flags = flags | G_SIGNAL_DETAILED; + + if (blob->action) + flags = flags | G_SIGNAL_ACTION; + + if (blob->no_hooks) + flags = flags | G_SIGNAL_NO_HOOKS; + + return flags; +} + +/** + * g_signal_info_get_class_closure: + * @info: a #GISignalInfo + * + * Obtain the class closure for this signal if one is set. The class + * closure is a virtual function on the type that the signal belongs to. + * If the signal lacks a closure %NULL will be returned. + * + * Returns: (transfer full): the class closure or %NULL + */ +GIVFuncInfo * +g_signal_info_get_class_closure (GISignalInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SignalBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_SIGNAL_INFO (info), 0); + + blob = (SignalBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->has_class_closure) + return g_interface_info_get_vfunc ((GIInterfaceInfo *)rinfo->container, blob->class_closure); + + return NULL; +} + +/** + * g_signal_info_true_stops_emit: + * @info: a #GISignalInfo + * + * Obtain if the returning true in the signal handler will + * stop the emission of the signal. + * + * Returns: %TRUE if returning true stops the signal emission + */ +gboolean +g_signal_info_true_stops_emit (GISignalInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SignalBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_SIGNAL_INFO (info), 0); + + blob = (SignalBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->true_stops_emit; +} + diff --git a/girepository/gisignalinfo.h b/girepository/gisignalinfo.h new file mode 100644 index 000000000..047aeb6e6 --- /dev/null +++ b/girepository/gisignalinfo.h @@ -0,0 +1,57 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Signal + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GISIGNALINFO_H__ +#define __GISIGNALINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +/** + * GI_IS_SIGNAL_INFO + * @info: an info structure + * + * Checks if @info is a #GISignalInfo. + */ +#define GI_IS_SIGNAL_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_SIGNAL) + + +GI_AVAILABLE_IN_ALL +GSignalFlags g_signal_info_get_flags (GISignalInfo *info); + +GI_AVAILABLE_IN_ALL +GIVFuncInfo * g_signal_info_get_class_closure (GISignalInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_signal_info_true_stops_emit (GISignalInfo *info); + +G_END_DECLS + + +#endif /* __GISIGNALINFO_H__ */ diff --git a/girepository/gistructinfo.c b/girepository/gistructinfo.c new file mode 100644 index 000000000..c13cb7163 --- /dev/null +++ b/girepository/gistructinfo.c @@ -0,0 +1,337 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Struct implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" + +/** + * SECTION:gistructinfo + * @title: GIStructInfo + * @short_description: Struct representing a C structure + * + * GIStructInfo represents a generic C structure type. + * + * A structure has methods and fields. + */ + +/** + * g_struct_info_get_n_fields: + * @info: a #GIStructInfo + * + * Obtain the number of fields this structure has. + * + * Returns: number of fields + */ +gint +g_struct_info_get_n_fields (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_fields; +} + +/** + * g_struct_info_get_field_offset: + * @info: a #GIStructInfo + * @n: index of queried field + * + * Obtain the offset of the specified field. + * + * Returns: field offset in bytes + */ +static gint32 +g_struct_get_field_offset (GIStructInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + guint32 offset = rinfo->offset + header->struct_blob_size; + gint i; + FieldBlob *field_blob; + + for (i = 0; i < n; i++) + { + field_blob = (FieldBlob *)&rinfo->typelib->data[offset]; + offset += header->field_blob_size; + if (field_blob->has_embedded_type) + offset += header->callback_blob_size; + } + + return offset; +} + +/** + * g_struct_info_get_field: + * @info: a #GIStructInfo + * @n: a field index + * + * Obtain the type information for field with specified index. + * + * Returns: (transfer full): the #GIFieldInfo, free it with g_base_info_unref() + * when done. + */ +GIFieldInfo * +g_struct_info_get_field (GIStructInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + + return (GIFieldInfo *) g_info_new (GI_INFO_TYPE_FIELD, (GIBaseInfo*)info, rinfo->typelib, + g_struct_get_field_offset (info, n)); +} + +/** + * g_struct_info_find_field: + * @info: a #GIStructInfo + * @name: a field name + * + * Obtain the type information for field named @name. + * + * Since: 1.46 + * Returns: (transfer full): the #GIFieldInfo or %NULL if not found, + * free it with g_base_info_unref() when done. + */ +GIFieldInfo * +g_struct_info_find_field (GIStructInfo *info, + const gchar *name) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + Header *header = (Header *)rinfo->typelib->data; + guint32 offset = rinfo->offset + header->struct_blob_size; + gint i; + + for (i = 0; i < blob->n_fields; i++) + { + FieldBlob *field_blob = (FieldBlob *)&rinfo->typelib->data[offset]; + const gchar *fname = (const gchar *)&rinfo->typelib->data[field_blob->name]; + + if (strcmp (name, fname) == 0) + { + return (GIFieldInfo *) g_info_new (GI_INFO_TYPE_FIELD, + (GIBaseInfo* )info, + rinfo->typelib, + offset); + } + + offset += header->field_blob_size; + if (field_blob->has_embedded_type) + offset += header->callback_blob_size; + } + + return NULL; +} + +/** + * g_struct_info_get_n_methods: + * @info: a #GIStructInfo + * + * Obtain the number of methods this structure has. + * + * Returns: number of methods + */ +gint +g_struct_info_get_n_methods (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_methods; +} + +/** + * g_struct_info_get_method: + * @info: a #GIStructInfo + * @n: a method index + * + * Obtain the type information for method with specified index. + * + * Returns: (transfer full): the #GIFunctionInfo, free it with g_base_info_unref() + * when done. + */ +GIFunctionInfo * +g_struct_info_get_method (GIStructInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + Header *header = (Header *)rinfo->typelib->data; + gint offset; + + offset = g_struct_get_field_offset (info, blob->n_fields) + n * header->function_blob_size; + return (GIFunctionInfo *) g_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_struct_info_find_method: + * @info: a #GIStructInfo + * @name: a method name + * + * Obtain the type information for method named @name. + * + * Returns: (transfer full): the #GIFunctionInfo, free it with g_base_info_unref() + * when done. + */ +GIFunctionInfo * +g_struct_info_find_method (GIStructInfo *info, + const gchar *name) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = g_struct_get_field_offset (info, blob->n_fields); + return _g_base_info_find_method ((GIBaseInfo*)info, offset, blob->n_methods, name); +} + +/** + * g_struct_info_get_size: + * @info: a #GIStructInfo + * + * Obtain the total size of the structure. + * + * Returns: size of the structure in bytes + */ +gsize +g_struct_info_get_size (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->size; +} + +/** + * g_struct_info_get_alignment: + * @info: a #GIStructInfo + * + * Obtain the required alignment of the structure. + * + * Returns: required alignment in bytes + */ +gsize +g_struct_info_get_alignment (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->alignment; +} + +/** + * g_struct_info_is_foreign: + * @info: TODO + * + * TODO + * + * Returns: TODO + */ +gboolean +g_struct_info_is_foreign (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->foreign; +} + +/** + * g_struct_info_is_gtype_struct: + * @info: a #GIStructInfo + * + * Return true if this structure represents the "class structure" for some + * #GObject or #GInterface. This function is mainly useful to hide this kind of structure + * from generated public APIs. + * + * Returns: %TRUE if this is a class struct, %FALSE otherwise + */ +gboolean +g_struct_info_is_gtype_struct (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->is_gtype_struct; +} + +/** + * g_struct_info_get_copy_function: + * @info: a struct information blob + * + * Retrieves the name of the copy function for @info, if any is set. + * + * Returns: (transfer none) (nullable): the name of the copy function + * + * Since: 1.76 + */ +const char * +g_struct_info_get_copy_function (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_STRUCT_INFO (info), NULL); + + blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->copy_func) + return g_typelib_get_string (rinfo->typelib, blob->copy_func); + + return NULL; +} + +/** + * g_struct_info_get_free_function: + * @info: a struct information blob + * + * Retrieves the name of the free function for @info, if any is set. + * + * Returns: (transfer none) (nullable): the name of the free function + * + * Since: 1.76 + */ +const char * +g_struct_info_get_free_function (GIStructInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + StructBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_STRUCT_INFO (info), NULL); + + blob = (StructBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->free_func) + return g_typelib_get_string (rinfo->typelib, blob->free_func); + + return NULL; +} diff --git a/girepository/gistructinfo.h b/girepository/gistructinfo.h new file mode 100644 index 000000000..8ff10d3ce --- /dev/null +++ b/girepository/gistructinfo.h @@ -0,0 +1,86 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Struct + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GISTRUCTINFO_H__ +#define __GISTRUCTINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_STRUCT_INFO + * @info: an info structure + * + * Checks if @info is a #GIStructInfo. + */ +#define GI_IS_STRUCT_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_STRUCT) + + +GI_AVAILABLE_IN_ALL +gint g_struct_info_get_n_fields (GIStructInfo *info); + +GI_AVAILABLE_IN_ALL +GIFieldInfo * g_struct_info_get_field (GIStructInfo *info, + gint n); + +GI_AVAILABLE_IN_1_46 +GIFieldInfo * g_struct_info_find_field (GIStructInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gint g_struct_info_get_n_methods (GIStructInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_struct_info_get_method (GIStructInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_struct_info_find_method (GIStructInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gsize g_struct_info_get_size (GIStructInfo *info); + +GI_AVAILABLE_IN_ALL +gsize g_struct_info_get_alignment (GIStructInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_struct_info_is_gtype_struct (GIStructInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_struct_info_is_foreign (GIStructInfo *info); + +GI_AVAILABLE_IN_1_76 +const char * g_struct_info_get_copy_function (GIStructInfo *info); + +GI_AVAILABLE_IN_1_76 +const char * g_struct_info_get_free_function (GIStructInfo *info); + +G_END_DECLS + +#endif /* __GISTRUCTINFO_H__ */ diff --git a/girepository/gitypeinfo.c b/girepository/gitypeinfo.c new file mode 100644 index 000000000..f3c596583 --- /dev/null +++ b/girepository/gitypeinfo.c @@ -0,0 +1,568 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Type implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" + +/** + * SECTION:gitypeinfo + * @title: GITypeInfo + * @short_description: Struct representing a type + * + * GITypeInfo represents a type. + * + * You can retrieve a type info from an argument (see #GIArgInfo), a + * functions return value (see #GIFunctionInfo), a field (see + * #GIFieldInfo), a property (see #GIPropertyInfo), a constant + * (see #GIConstantInfo) or for a union discriminator (see #GIUnionInfo). + * + * A type can either be a of a basic type which is a standard C primitive + * type or an interface type. For interface types you need to call + * g_type_info_get_interface() to get a reference to the base info for that + * interface. + */ + +/** + * g_type_info_is_pointer: + * @info: a #GITypeInfo + * + * Obtain if the type is passed as a reference. + * + * Note that the types of %GI_DIRECTION_OUT and %GI_DIRECTION_INOUT parameters + * will only be pointers if the underlying type being transferred is a pointer + * (i.e. only if the type of the C function’s formal parameter is a pointer to a + * pointer). + * + * Returns: %TRUE if it is a pointer + */ +gboolean +g_type_info_is_pointer (GITypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SimpleTypeBlob *type; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), FALSE); + + type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (type->flags.reserved == 0 && type->flags.reserved2 == 0) + return type->flags.pointer; + else + { + InterfaceTypeBlob *iface = (InterfaceTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + return iface->pointer; + } +} + +/** + * g_type_info_get_tag: + * @info: a #GITypeInfo + * + * Obtain the type tag for the type. See #GITypeTag for a list + * of type tags. + * + * Returns: the type tag + */ +GITypeTag +g_type_info_get_tag (GITypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SimpleTypeBlob *type; + + g_return_val_if_fail (info != NULL, GI_TYPE_TAG_BOOLEAN); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), GI_TYPE_TAG_BOOLEAN); + + type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (rinfo->type_is_embedded) + return GI_TYPE_TAG_INTERFACE; + else if (type->flags.reserved == 0 && type->flags.reserved2 == 0) + return type->flags.tag; + else + { + InterfaceTypeBlob *iface = (InterfaceTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + return iface->tag; + } +} + +/** + * g_type_info_get_param_type: + * @info: a #GITypeInfo + * @n: index of the parameter + * + * Obtain the parameter type @n. + * + * Returns: (transfer full): the param type info + */ +GITypeInfo * +g_type_info_get_param_type (GITypeInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SimpleTypeBlob *type; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), NULL); + + type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (!(type->flags.reserved == 0 && type->flags.reserved2 == 0)) + { + ParamTypeBlob *param = (ParamTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + switch (param->tag) + { + case GI_TYPE_TAG_ARRAY: + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + case GI_TYPE_TAG_GHASH: + return _g_type_info_new ((GIBaseInfo*)info, rinfo->typelib, + rinfo->offset + sizeof (ParamTypeBlob) + + sizeof (SimpleTypeBlob) * n); + break; + default: + break; + } + } + + return NULL; +} + +/** + * g_type_info_get_interface: + * @info: a #GITypeInfo + * + * For types which have #GI_TYPE_TAG_INTERFACE such as GObjects and boxed values, + * this function returns full information about the referenced type. You can then + * inspect the type of the returned #GIBaseInfo to further query whether it is + * a concrete GObject, a GInterface, a structure, etc. using g_base_info_get_type(). + * + * Returns: (transfer full): the #GIBaseInfo, or %NULL. Free it with + * g_base_info_unref() when done. + */ +GIBaseInfo * +g_type_info_get_interface (GITypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), NULL); + + /* For embedded types, the given offset is a pointer to the actual blob, + * after the end of the field. In that case we know it's a "subclass" of + * CommonBlob, so use that to determine the info type. + */ + if (rinfo->type_is_embedded) + { + CommonBlob *common = (CommonBlob *)&rinfo->typelib->data[rinfo->offset]; + GIInfoType info_type; + + switch (common->blob_type) + { + case BLOB_TYPE_CALLBACK: + info_type = GI_INFO_TYPE_CALLBACK; + break; + default: + g_assert_not_reached (); + return NULL; + } + return (GIBaseInfo *) g_info_new (info_type, (GIBaseInfo*)info, rinfo->typelib, + rinfo->offset); + } + else + { + SimpleTypeBlob *type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + if (!(type->flags.reserved == 0 && type->flags.reserved2 == 0)) + { + InterfaceTypeBlob *blob = (InterfaceTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->tag == GI_TYPE_TAG_INTERFACE) + return _g_info_from_entry (rinfo->repository, rinfo->typelib, blob->interface); + } + } + + return NULL; +} + +/** + * g_type_info_get_array_length: + * @info: a #GITypeInfo + * + * Obtain the position of the argument which gives the array length of the type. + * The type tag must be a #GI_TYPE_TAG_ARRAY or -1 will be returned. + * + * Returns: the array length, or -1 if the type is not an array + */ +gint +g_type_info_get_array_length (GITypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SimpleTypeBlob *type; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), -1); + + type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (!(type->flags.reserved == 0 && type->flags.reserved2 == 0)) + { + ArrayTypeBlob *blob = (ArrayTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->tag == GI_TYPE_TAG_ARRAY) + { + if (blob->has_length) + return blob->dimensions.length; + } + } + + return -1; +} + +/** + * g_type_info_get_array_fixed_size: + * @info: a #GITypeInfo + * + * Obtain the fixed array size of the type. The type tag must be a + * #GI_TYPE_TAG_ARRAY or -1 will be returned. + * + * Returns: the size or -1 if it's not an array + */ +gint +g_type_info_get_array_fixed_size (GITypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SimpleTypeBlob *type; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), 0); + + type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (!(type->flags.reserved == 0 && type->flags.reserved2 == 0)) + { + ArrayTypeBlob *blob = (ArrayTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->tag == GI_TYPE_TAG_ARRAY) + { + if (blob->has_size) + return blob->dimensions.size; + } + } + + return -1; +} + +/** + * g_type_info_is_zero_terminated: + * @info: a #GITypeInfo + * + * Obtain if the last element of the array is %NULL. The type tag must be a + * #GI_TYPE_TAG_ARRAY or %FALSE will be returned. + * + * Returns: %TRUE if zero terminated + */ +gboolean +g_type_info_is_zero_terminated (GITypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SimpleTypeBlob *type; + + g_return_val_if_fail (info != NULL, FALSE); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), FALSE); + + type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (!(type->flags.reserved == 0 && type->flags.reserved2 == 0)) + { + ArrayTypeBlob *blob = (ArrayTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->tag == GI_TYPE_TAG_ARRAY) + return blob->zero_terminated; + } + + return FALSE; +} + +/** + * g_type_info_get_array_type: + * @info: a #GITypeInfo + * + * Obtain the array type for this type. See #GIArrayType for a list of + * possible values. If the type tag of this type is not array, -1 will be + * returned. + * + * Returns: the array type or -1 + */ +GIArrayType +g_type_info_get_array_type (GITypeInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + SimpleTypeBlob *type; + + g_return_val_if_fail (info != NULL, -1); + g_return_val_if_fail (GI_IS_TYPE_INFO (info), -1); + + type = (SimpleTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (!(type->flags.reserved == 0 && type->flags.reserved2 == 0)) + { + ArrayTypeBlob *blob = (ArrayTypeBlob *)&rinfo->typelib->data[rinfo->offset]; + g_return_val_if_fail (blob->tag == GI_TYPE_TAG_ARRAY, -1); + + return blob->array_type; + } + + return -1; +} + +/** + * g_type_info_get_storage_type: + * @info: a #GITypeInfo + * + * Obtain the type tag corresponding to the underlying storage type in C for + * the type. + * See #GITypeTag for a list of type tags. + * + * Returns: the type tag + * + * Since: 1.66 + */ +GITypeTag +g_type_info_get_storage_type (GITypeInfo *info) +{ + GITypeTag type_tag = g_type_info_get_tag (info); + + if (type_tag == GI_TYPE_TAG_INTERFACE) + { + GIBaseInfo *interface = g_type_info_get_interface (info); + GIInfoType info_type = g_base_info_get_type (interface); + if (info_type == GI_INFO_TYPE_ENUM || info_type == GI_INFO_TYPE_FLAGS) + type_tag = g_enum_info_get_storage_type (interface); + g_base_info_unref (interface); + } + + return type_tag; +} + +/** + * gi_type_tag_argument_from_hash_pointer: + * @storage_type: a #GITypeTag obtained from g_type_info_get_storage_type() + * @hash_pointer: A pointer, such as a #GHashTable data pointer + * @arg: A #GIArgument to fill in + * + * GLib data structures, such as #GList, #GSList, and #GHashTable, all store + * data pointers. + * In the case where the list or hash table is storing single types rather than + * structs, these data pointers may have values stuffed into them via macros + * such as %GPOINTER_TO_INT. + * + * Use this function to ensure that all values are correctly extracted from + * stuffed pointers, regardless of the machine's architecture or endianness. + * + * This function fills in the appropriate field of @arg with the value extracted + * from @hash_pointer, depending on @storage_type. + * + * Since: 1.72 + */ +void +gi_type_tag_argument_from_hash_pointer (GITypeTag storage_type, + gpointer hash_pointer, + GIArgument *arg) +{ + switch (storage_type) + { + case GI_TYPE_TAG_BOOLEAN: + arg->v_boolean = !!GPOINTER_TO_INT (hash_pointer); + break; + case GI_TYPE_TAG_INT8: + arg->v_int8 = (gint8)GPOINTER_TO_INT (hash_pointer); + break; + case GI_TYPE_TAG_UINT8: + arg->v_uint8 = (guint8)GPOINTER_TO_UINT (hash_pointer); + break; + case GI_TYPE_TAG_INT16: + arg->v_int16 = (gint16)GPOINTER_TO_INT (hash_pointer); + break; + case GI_TYPE_TAG_UINT16: + arg->v_uint16 = (guint16)GPOINTER_TO_UINT (hash_pointer); + break; + case GI_TYPE_TAG_INT32: + arg->v_int32 = (gint32)GPOINTER_TO_INT (hash_pointer); + break; + case GI_TYPE_TAG_UINT32: + case GI_TYPE_TAG_UNICHAR: + arg->v_uint32 = (guint32)GPOINTER_TO_UINT (hash_pointer); + break; + case GI_TYPE_TAG_GTYPE: + arg->v_size = GPOINTER_TO_SIZE (hash_pointer); + break; + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + case GI_TYPE_TAG_INTERFACE: + case GI_TYPE_TAG_ARRAY: + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + case GI_TYPE_TAG_GHASH: + case GI_TYPE_TAG_ERROR: + arg->v_pointer = hash_pointer; + break; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + case GI_TYPE_TAG_FLOAT: + case GI_TYPE_TAG_DOUBLE: + default: + g_critical ("Unsupported storage type for pointer-stuffing: %s", + g_type_tag_to_string (storage_type)); + arg->v_pointer = hash_pointer; + } +} + +/** + * g_type_info_argument_from_hash_pointer: + * @info: a #GITypeInfo + * @hash_pointer: A pointer, such as a #GHashTable data pointer + * @arg: A #GIArgument to fill in + * + * GLib data structures, such as #GList, #GSList, and #GHashTable, all store + * data pointers. + * In the case where the list or hash table is storing single types rather than + * structs, these data pointers may have values stuffed into them via macros + * such as %GPOINTER_TO_INT. + * + * Use this function to ensure that all values are correctly extracted from + * stuffed pointers, regardless of the machine's architecture or endianness. + * + * This function fills in the appropriate field of @arg with the value extracted + * from @hash_pointer, depending on the storage type of @info. + * + * Since: 1.66 + */ +void +g_type_info_argument_from_hash_pointer (GITypeInfo *info, + gpointer hash_pointer, + GIArgument *arg) +{ + GITypeTag storage_type = g_type_info_get_storage_type (info); + gi_type_tag_argument_from_hash_pointer (storage_type, hash_pointer, + arg); +} + +/** + * gi_type_tag_hash_pointer_from_argument: + * @storage_type: a #GITypeTag obtained from g_type_info_get_storage_type() + * @arg: A #GIArgument with the value to stuff into a pointer + * + * GLib data structures, such as #GList, #GSList, and #GHashTable, all store + * data pointers. + * In the case where the list or hash table is storing single types rather than + * structs, these data pointers may have values stuffed into them via macros + * such as %GPOINTER_TO_INT. + * + * Use this function to ensure that all values are correctly stuffed into + * pointers, regardless of the machine's architecture or endianness. + * + * This function returns a pointer stuffed with the appropriate field of @arg, + * depending on @storage_type. + * + * Returns: A stuffed pointer, that can be stored in a #GHashTable, for example + * + * Since: 1.72 + */ +gpointer +gi_type_tag_hash_pointer_from_argument (GITypeTag storage_type, + GIArgument *arg) +{ + switch (storage_type) + { + case GI_TYPE_TAG_BOOLEAN: + return GINT_TO_POINTER (arg->v_boolean); + case GI_TYPE_TAG_INT8: + return GINT_TO_POINTER (arg->v_int8); + case GI_TYPE_TAG_UINT8: + return GUINT_TO_POINTER (arg->v_uint8); + case GI_TYPE_TAG_INT16: + return GINT_TO_POINTER (arg->v_int16); + case GI_TYPE_TAG_UINT16: + return GUINT_TO_POINTER (arg->v_uint16); + case GI_TYPE_TAG_INT32: + return GINT_TO_POINTER (arg->v_int32); + case GI_TYPE_TAG_UINT32: + case GI_TYPE_TAG_UNICHAR: + return GUINT_TO_POINTER (arg->v_uint32); + case GI_TYPE_TAG_GTYPE: + return GSIZE_TO_POINTER (arg->v_size); + case GI_TYPE_TAG_UTF8: + case GI_TYPE_TAG_FILENAME: + case GI_TYPE_TAG_INTERFACE: + case GI_TYPE_TAG_ARRAY: + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + case GI_TYPE_TAG_GHASH: + case GI_TYPE_TAG_ERROR: + return arg->v_pointer; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + case GI_TYPE_TAG_FLOAT: + case GI_TYPE_TAG_DOUBLE: + default: + g_critical ("Unsupported storage type for pointer-stuffing: %s", + g_type_tag_to_string (storage_type)); + return arg->v_pointer; + } +} + +/** + * g_type_info_hash_pointer_from_argument: + * @info: a #GITypeInfo + * @arg: A #GIArgument with the value to stuff into a pointer + * + * GLib data structures, such as #GList, #GSList, and #GHashTable, all store + * data pointers. + * In the case where the list or hash table is storing single types rather than + * structs, these data pointers may have values stuffed into them via macros + * such as %GPOINTER_TO_INT. + * + * Use this function to ensure that all values are correctly stuffed into + * pointers, regardless of the machine's architecture or endianness. + * + * This function returns a pointer stuffed with the appropriate field of @arg, + * depending on the storage type of @info. + * + * Returns: A stuffed pointer, that can be stored in a #GHashTable, for example + * + * Since: 1.66 + */ +gpointer +g_type_info_hash_pointer_from_argument (GITypeInfo *info, + GIArgument *arg) +{ + GITypeTag storage_type = g_type_info_get_storage_type (info); + return gi_type_tag_hash_pointer_from_argument (storage_type, arg); +} diff --git a/girepository/gitypeinfo.h b/girepository/gitypeinfo.h new file mode 100644 index 000000000..283658ef3 --- /dev/null +++ b/girepository/gitypeinfo.h @@ -0,0 +1,162 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Type + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GITYPEINFO_H__ +#define __GITYPEINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_TYPE_INFO + * @info: an info structure + * + * Checks if @info is a #GITypeInfo. + */ +#define GI_IS_TYPE_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_TYPE) + +/** + * G_TYPE_TAG_IS_BASIC + * @tag: a type tag + * + * Checks if @tag is a basic type. + * + * Deprecated: 1.72: Use GI_TYPE_TAG_IS_BASIC() instead + */ +#define G_TYPE_TAG_IS_BASIC(tag) GI_TYPE_TAG_IS_BASIC(tag) + +/** + * GI_TYPE_TAG_IS_BASIC + * @tag: a type tag + * + * Checks if @tag is a basic type. + * + * Since: 1.72 + */ +#define GI_TYPE_TAG_IS_BASIC(tag) ((tag) < GI_TYPE_TAG_ARRAY || (tag) == GI_TYPE_TAG_UNICHAR) + +/** + * GI_TYPE_TAG_IS_NUMERIC: + * @tag: a type tag + * + * Checks if @tag is a numeric type. That is, integer or floating point. + * + * Since: 1.72 + */ +#define GI_TYPE_TAG_IS_NUMERIC(tag) ((tag) >= GI_TYPE_TAG_INT8 && (tag) <= GI_TYPE_TAG_DOUBLE) + +/** + * GI_TYPE_TAG_IS_NUMERIC: + * @tag: a type tag + * + * Checks if @tag is a numeric type. That is, integer or floating point. + * + * Since: 1.72 + */ +#define GI_TYPE_TAG_IS_NUMERIC(tag) ((tag) >= GI_TYPE_TAG_INT8 && (tag) <= GI_TYPE_TAG_DOUBLE) + +/** + * GI_TYPE_TAG_IS_NUMERIC: + * @tag: a type tag + * + * Checks if @tag is a numeric type. That is, integer or floating point. + * + * Since: 1.72 + */ +#define GI_TYPE_TAG_IS_NUMERIC(tag) ((tag) >= GI_TYPE_TAG_INT8 && (tag) <= GI_TYPE_TAG_DOUBLE) + +/** + * GI_TYPE_TAG_IS_CONTAINER: + * @tag: a type tag + * + * Checks if @tag is a container type. That is, a type which may have a nonnull + * return from g_type_info_get_param_type(). + * + * Since: 1.72 + */ + #define GI_TYPE_TAG_IS_CONTAINER(tag) ((tag) == GI_TYPE_TAG_ARRAY || \ + ((tag) >= GI_TYPE_TAG_GLIST && (tag) <= GI_TYPE_TAG_GHASH)) + +GI_AVAILABLE_IN_ALL +const gchar* g_type_tag_to_string (GITypeTag type); + +GI_AVAILABLE_IN_ALL +const gchar* g_info_type_to_string (GIInfoType type); + + +GI_AVAILABLE_IN_ALL +gboolean g_type_info_is_pointer (GITypeInfo *info); + +GI_AVAILABLE_IN_ALL +GITypeTag g_type_info_get_tag (GITypeInfo *info); + +GI_AVAILABLE_IN_ALL +GITypeInfo * g_type_info_get_param_type (GITypeInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIBaseInfo * g_type_info_get_interface (GITypeInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_type_info_get_array_length (GITypeInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_type_info_get_array_fixed_size(GITypeInfo *info); + +GI_AVAILABLE_IN_ALL +gboolean g_type_info_is_zero_terminated (GITypeInfo *info); + +GI_AVAILABLE_IN_ALL +GIArrayType g_type_info_get_array_type (GITypeInfo *info); + +GI_AVAILABLE_IN_1_66 +GITypeTag g_type_info_get_storage_type (GITypeInfo *info); + +GI_AVAILABLE_IN_1_66 +void g_type_info_argument_from_hash_pointer (GITypeInfo *info, + gpointer hash_pointer, + GIArgument *arg); + +GI_AVAILABLE_IN_1_66 +gpointer g_type_info_hash_pointer_from_argument (GITypeInfo *info, + GIArgument *arg); + +GI_AVAILABLE_IN_1_72 +void gi_type_tag_argument_from_hash_pointer (GITypeTag storage_type, + gpointer hash_pointer, + GIArgument *arg); + +GI_AVAILABLE_IN_1_72 +gpointer gi_type_tag_hash_pointer_from_argument (GITypeTag storage_type, + GIArgument *arg); + +G_END_DECLS + + +#endif /* __GITYPEINFO_H__ */ + diff --git a/girepository/gitypelib-internal.h b/girepository/gitypelib-internal.h new file mode 100644 index 000000000..4ba53a2a5 --- /dev/null +++ b/girepository/gitypelib-internal.h @@ -0,0 +1,1352 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: struct definitions for the binary + * typelib format, validation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __G_TYPELIB_H__ +#define __G_TYPELIB_H__ + +#include +#include "girepository.h" + +G_BEGIN_DECLS + +/** + * SECTION:gitypelib-internal + * @title: GITypelib Internals + * @short_description: Layout and accessors for typelib + * @stability: Stable + * + * The "typelib" is a binary, readonly, memory-mappable database + * containing reflective information about a GObject library. + * + * What the typelib describes and the types used are the same for every + * platform so, apart the endianness of its scalar values, the typelib + * database must be considered architecture-independent. + * + * The format of GObject typelib is strongly influenced by the Mozilla XPCOM + * format. + * + * Some of the differences to XPCOM include: + * + * - Type information is stored not quite as compactly (XPCOM stores it inline + * in function descriptions in variable-sized blobs of 1 to n bytes. We store + * 16 bits of type information for each parameter, which is enough to encode + * simple types inline. Complex (e.g. recursive) types are stored out of line + * in a separate list of types. + * - String and complex type data is stored outside of typelib entry blobs, + * references are stored as offsets relative to the start of the typelib. + * One possibility is to store the strings and types in a pools at the end + * of the typelib. + * + * The typelib has the following general format: + * + * |[ + * typelib ::= header, section-index, directory, blobs, attributes, attributedata + * + * directory ::= list of entries + * + * entry ::= blob type, name, namespace, offset + * blob ::= function|callback|struct|boxed|enum|flags|object|interface|constant|union + * attribute ::= offset, key, value + * attributedata ::= string data for attributes + * ]| + * + * Details + * + * We describe the fragments that make up the typelib in the form of C structs + * (although some fall short of being valid C structs since they contain + * multiple flexible arrays). + */ + +/* +TYPELIB HISTORY +----- + +Version 1.1 +- Add ref/unref/set-value/get-value functions to Object, to be able + to support instantiatable fundamental types which are not GObject based. + +Version 1.0 +- Rename class_struct to gtype_struct, add to interfaces + +Changes since 0.9: +- Add padding to structures + +Changes since 0.8: +- Add class struct concept to ObjectBlob +- Add is_class_struct bit to StructBlob + +Changes since 0.7: +- Add dependencies + +Changes since 0.6: +- rename metadata to typelib, to follow xpcom terminology + +Changes since 0.5: +- basic type cleanup: + + remove GString + + add [u]int, [u]long, [s]size_t + + rename string to utf8, add filename +- allow blob_type to be zero for non-local entries + +Changes since 0.4: +- add a UnionBlob + +Changes since 0.3: +- drop short_name for ValueBlob + +Changes since 0.2: +- make inline types 4 bytes after all, remove header->types and allow + types to appear anywhere +- allow error domains in the directory + +Changes since 0.1: + +- drop comments about _GOBJ_METADATA +- drop string pool, strings can appear anywhere +- use 'blob' as collective name for the various blob types +- rename 'type' field in blobs to 'blob_type' +- rename 'type_name' and 'type_init' fields to 'gtype_name', 'gtype_init' +- shrink directory entries to 12 bytes +- merge struct and boxed blobs +- split interface blobs into enum, object and interface blobs +- add an 'unregistered' flag to struct and enum blobs +- add a 'wraps_vfunc' flag to function blobs and link them to + the vfuncs they wrap +- restrict value blobs to only occur inside enums and flags again +- add constant blobs, allow them toplevel, in interfaces and in objects +- rename 'receiver_owns_value' and 'receiver_owns_container' to + 'transfer_ownership' and 'transfer_container_ownership' +- add a 'struct_offset' field to virtual function and field blobs +- add 'dipper' and 'optional' flags to arg blobs +- add a 'true_stops_emit' flag to signal blobs +- add variable blob sizes to header +- store offsets to signature blobs instead of including them directly +- change the type offset to be measured in words rather than bytes +*/ + +/** + * G_IR_MAGIC: + * + * Identifying prefix for the typelib. This was inspired by XPCOM, + * which in turn borrowed from PNG. + */ +#define G_IR_MAGIC "GOBJ\nMETADATA\r\n\032" + +/** + * GTypelibBlobType: + * @BLOB_TYPE_INVALID: Should not appear in code + * @BLOB_TYPE_FUNCTION: A #FunctionBlob + * @BLOB_TYPE_CALLBACK: A #CallbackBlob + * @BLOB_TYPE_STRUCT: A #StructBlob + * @BLOB_TYPE_BOXED: Can be either a #StructBlob or #UnionBlob + * @BLOB_TYPE_ENUM: An #EnumBlob + * @BLOB_TYPE_FLAGS: An #EnumBlob + * @BLOB_TYPE_OBJECT: An #ObjectBlob + * @BLOB_TYPE_INTERFACE: An #InterfaceBlob + * @BLOB_TYPE_CONSTANT: A #ConstantBlob + * @BLOB_TYPE_INVALID_0: Deleted, used to be ErrorDomain. + * @BLOB_TYPE_UNION: A #UnionBlob + * + * The integral value of this enumeration appears in each "Blob" component of + * a typelib to identify its type. + */ +typedef enum { + BLOB_TYPE_INVALID, + BLOB_TYPE_FUNCTION, + BLOB_TYPE_CALLBACK, + BLOB_TYPE_STRUCT, + BLOB_TYPE_BOXED, + BLOB_TYPE_ENUM, + BLOB_TYPE_FLAGS, + BLOB_TYPE_OBJECT, + BLOB_TYPE_INTERFACE, + BLOB_TYPE_CONSTANT, + BLOB_TYPE_INVALID_0, + BLOB_TYPE_UNION +} GTypelibBlobType; + + +#if defined (G_CAN_INLINE) && defined (G_ALWAYS_INLINE) + +G_ALWAYS_INLINE +inline gboolean +_blob_is_registered_type (GTypelibBlobType blob_type) +{ + switch (blob_type) + { + case BLOB_TYPE_STRUCT: + case BLOB_TYPE_UNION: + case BLOB_TYPE_ENUM: + case BLOB_TYPE_FLAGS: + case BLOB_TYPE_OBJECT: + case BLOB_TYPE_INTERFACE: + return TRUE; + default: + return FALSE; + } +} + +#define BLOB_IS_REGISTERED_TYPE(blob) \ + _blob_is_registered_type ((GTypelibBlobType) (blob)->blob_type) + +#else + +#define BLOB_IS_REGISTERED_TYPE(blob) \ + ((blob)->blob_type == BLOB_TYPE_STRUCT || \ + (blob)->blob_type == BLOB_TYPE_UNION || \ + (blob)->blob_type == BLOB_TYPE_ENUM || \ + (blob)->blob_type == BLOB_TYPE_FLAGS || \ + (blob)->blob_type == BLOB_TYPE_OBJECT || \ + (blob)->blob_type == BLOB_TYPE_INTERFACE) + +#endif /* defined (G_CAN_INLINE) && defined (G_ALWAYS_INLINE) */ + +/** + * Header: + * @magic: See #G_IR_MAGIC. + * @major_version: The major version number of the typelib format. Major version + * number changes indicate incompatible changes to the tyeplib format. + * @minor_version: The minor version number of the typelib format. Minor version + * number changes indicate compatible changes and should still allow the + * typelib to be parsed by a parser designed for the same @major_version. + * @reserved: Reserved for future use. + * @n_entries: The number of entries in the directory. + * @n_local_entries: The number of entries referring to blobs in this typelib. + * The local entries must occur before the unresolved entries. + * @directory: Offset of the directory in the typelib. + * @n_attributes: Number of attribute blocks + * @attributes: Offset of the list of attributes in the typelib. + * @dependencies: Offset of a single string, which is the list of immediate + * dependencies, separated by the '|' character. The dependencies are + * required in order to avoid having programs consuming a typelib check for + * an "Unresolved" type return from every API call. + * @size: The size in bytes of the typelib. + * @namespace: Offset of the namespace string in the typelib. + * @nsversion: Offset of the namespace version string in the typelib. + * @shared_library: This field is the set of shared libraries associated with + * the typelib. The entries are separated by the '|' (pipe) character. + * @c_prefix: The prefix for the function names of the library + * @entry_blob_size: The sizes of fixed-size blobs. Recording this information + * here allows to write parser which continue to work if the format is + * extended by adding new fields to the end of the fixed-size blobs. + * @function_blob_size: See @entry_blob_size. + * @callback_blob_size: See @entry_blob_size. + * @signal_blob_size: See @entry_blob_size. + * @vfunc_blob_size: See @entry_blob_size. + * @arg_blob_size: See @entry_blob_size. + * @property_blob_size: See @entry_blob_size. + * @field_blob_size: See @entry_blob_size. + * @value_blob_size: See @entry_blob_size. + * @attribute_blob_size: See @entry_blob_size. + * @constant_blob_size: See @entry_blob_size. + * @error_domain_blob_size: See @entry_blob_size. + * @signature_blob_size: See @entry_blob_size. + * @enum_blob_size: See @entry_blob_size. + * @struct_blob_size: See @entry_blob_size. + * @object_blob_size: See @entry_blob_size. + * @interface_blob_size: For variable-size blobs, the size of the struct up to + * the first flexible array member. Recording this information here allows + * to 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. + * @union_blob_size: See @entry_blob_size. + * @sections: Offset of section blob array + * @padding: TODO + * + * 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. + */ +typedef struct { + gchar magic[16]; + guint8 major_version; + guint8 minor_version; + guint16 reserved; + guint16 n_entries; + guint16 n_local_entries; + guint32 directory; + guint32 n_attributes; + guint32 attributes; + + guint32 dependencies; + + guint32 size; + guint32 namespace; + guint32 nsversion; + guint32 shared_library; + guint32 c_prefix; + + guint16 entry_blob_size; + guint16 function_blob_size; + guint16 callback_blob_size; + guint16 signal_blob_size; + guint16 vfunc_blob_size; + guint16 arg_blob_size; + guint16 property_blob_size; + guint16 field_blob_size; + guint16 value_blob_size; + guint16 attribute_blob_size; + guint16 constant_blob_size; + guint16 error_domain_blob_size; + + guint16 signature_blob_size; + guint16 enum_blob_size; + guint16 struct_blob_size; + guint16 object_blob_size; + guint16 interface_blob_size; + guint16 union_blob_size; + + guint32 sections; + + guint16 padding[6]; +} Header; + +/** + * SectionType: + * @GI_SECTION_END: TODO + * @GI_SECTION_DIRECTORY_INDEX: TODO + * + * TODO + */ +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 + * @local: Whether this entry refers to a blob in this typelib. + * @reserved: Reserved for future use. + * @name: The name of the entry. + * @offset: If is_local is set, this is the offset of the blob in the typelib. + * Otherwise, it is the offset of the namespace in which the blob has to be + * looked up by name. + * + * References to directory entries are stored as 1-based 16-bit indexes. + * + * All blobs pointed to by a directory entry start with the same layout for + * the first 8 bytes (the reserved flags may be used by some blob types) + */ +typedef struct { + guint16 blob_type; + + guint16 local : 1; + guint16 reserved :15; + guint32 name; + guint32 offset; +} DirEntry; + +/** + * SimpleTypeBlobFlags: + * @reserved: Reserved for future use. + * @reserved2: Reserved for future use. + * @pointer: TODO + * @reserved3: Reserved for future use. + * @tag: A #GITypeTag + * + * TODO + */ +typedef struct { + guint reserved : 8; + guint reserved2 :16; + guint pointer : 1; + guint reserved3 : 2; + guint tag : 5; +} SimpleTypeBlobFlags; + +union _SimpleTypeBlob +{ + SimpleTypeBlobFlags flags; + guint32 offset; +}; + +/** + * SimpleTypeBlob: + * @flags: TODO + * @offset: Offset relative to header->types that points to a TypeBlob. + * Unlike other offsets, this is in words (ie 32bit units) rather + * than bytes. + * + * The SimpleTypeBlob is the general purpose "reference to a type" construct, + * used in method parameters, returns, callback definitions, fields, constants, + * etc. It's actually just a 32 bit integer which you can see from the union + * definition. This is for efficiency reasons, since there are so many + * references to types. + * + * SimpleTypeBlob is divided into two cases; first, if "reserved" and + * "reserved2" are both zero, the type tag for a basic type is embedded in the + * "tag" bits. This allows e.g. GI_TYPE_TAG_UTF8, GI_TYPE_TAG_INT and the like + * to be embedded directly without taking up extra space. + * + * References to "interfaces" (objects, interfaces) are more complicated; + * In this case, the integer is actually an offset into the directory (see + * above). Because the header is larger than 2^8=256 bits, all offsets will + * have one of the upper 24 bits set. + */ +typedef union _SimpleTypeBlob SimpleTypeBlob; + +/** + * ArgBlob: + * @name: A suggested name for the parameter. + * @in: The parameter is an input to the function + * @out: The parameter is used to return an output of the function. Parameters + * can be both in and out. Out parameters implicitly add another level of + * indirection to the parameter type. Ie if the type is uint32 in an out + * parameter, the function actually takes an uint32*. + * @caller_allocates: The parameter is a pointer to a struct or object that + * will receive an output of the function. + * @nullable: Only meaningful for types which are passed as pointers. For an + * in parameter, indicates if it is ok to pass NULL in. Gor an out + * parameter, indicates whether it may return NULL. Note that NULL is a + * valid GList and GSList value, thus allow_none will normally be set + * for parameters of these types. + * @optional: For an out parameter, indicates that NULL may be passed in + * if the value is not needed. + * @transfer_ownership: For an in parameter, indicates that the function takes + * over ownership of the parameter value. For an out parameter, it indicates + * that the caller is responsible for freeing the return value. + * @transfer_container_ownership: For container types, indicates that the + * ownership of the container, but not of its contents is transferred. + * This is typically the case for out parameters returning lists of + * statically allocated things. + * @return_value: The parameter should be considered the return value of the + * function. Only out parameters can be marked as return value, and there + * can be at most one per function call. If an out parameter is marked as + * return value, the actual return value of the function should be either + * void or a boolean indicating the success of the call. + * @scope: A #GIScopeType. If the parameter is of a callback type, this denotes + * the scope of the user_data and the callback function pointer itself + * (for languages that emit code at run-time). + * @skip: Indicates that the parameter is only useful in C and should be skipped. + * @reserved: Reserved for future use. + * @closure: Index of the closure (user_data) parameter associated with the + * callback, or -1. + * @destroy: Index of the destroy notfication callback parameter associated + * with the callback, or -1. + * @padding: TODO + * @arg_type: Describes the type of the parameter. See details below. + * + * Types are specified by four bytes. If the three high bytes are zero, + * the low byte describes a basic type, otherwise the 32bit number is an + * offset which points to a TypeBlob. + */ +typedef struct { + guint32 name; + + guint in : 1; + guint out : 1; + guint caller_allocates : 1; + guint nullable : 1; + guint optional : 1; + guint transfer_ownership : 1; + guint transfer_container_ownership : 1; + guint return_value : 1; + guint scope : 3; + guint skip : 1; + guint reserved :20; + gint8 closure; + gint8 destroy; + + guint16 padding; + + SimpleTypeBlob arg_type; +} ArgBlob; + +/** + * SignatureBlob: + * @return_type: Describes the type of the return value. See details below. + * @may_return_null: Only relevant for pointer types. Indicates whether the + * caller must expect NULL as a return value. + * @caller_owns_return_value: If set, the caller is responsible for freeing + * the return value if it is no longer needed. + * @caller_owns_return_container: This flag is only relevant if the return type + * is a container type. If the flag is set, the caller is resonsible for + * freeing the container, but not its contents. + * @skip_return: Indicates that the return value is only useful in C and should + * be skipped. + * @instance_transfer_ownership: When calling, the function assumes ownership of + * the instance parameter. + * @throws: Denotes the signature takes an additional #GError argument beyond + * the annotated arguments. + * @reserved: Reserved for future use. + * @n_arguments: The number of arguments that this function expects, also the + * length of the array of ArgBlobs. + * @arguments: An array of ArgBlob for the arguments of the function. + * + * TODO + */ +typedef struct { + SimpleTypeBlob return_type; + + guint16 may_return_null : 1; + guint16 caller_owns_return_value : 1; + guint16 caller_owns_return_container : 1; + guint16 skip_return : 1; + guint16 instance_transfer_ownership : 1; + guint16 throws : 1; + guint16 reserved :10; + + guint16 n_arguments; + + ArgBlob arguments[]; +} SignatureBlob; + +/** + * CommonBlob: + * @blob_type: A #GTypelibBlobType + * @deprecated: Whether the blob is deprecated. + * @reserved: Reserved for future use. + * @name: The name of the blob. + * + * The #CommonBlob is shared between #FunctionBlob, + * #CallbackBlob, #SignalBlob. + * + * TODO + */ +typedef struct { + guint16 blob_type; /* 1 */ + + guint16 deprecated : 1; + guint16 reserved :15; + guint32 name; +} CommonBlob; + +/** + * FunctionBlob: + * @blob_type: #BLOB_TYPE_FUNCTION + * @deprecated: The function is deprecated. + * @setter: The function is a setter for a property. Language bindings may + * prefer to not bind individual setters and rely on the generic + * g_object_set(). + * @getter: The function is a getter for a property. Language bindings may + * prefer to not bind individual getters and rely on the generic + * g_object_get(). + * @constructor: The function acts as a constructor for the object it is + * contained in. + * @wraps_vfunc: The function is a simple wrapper for a virtual function. + * @throws: This is now additionally stored in the #SignatureBlob. (deprecated) + * @index: Index of the property that this function is a setter or getter of + * in the array of properties of the containing interface, or index + * of the virtual function that this function wraps. + * @name: TODO + * @symbol: The symbol which can be used to obtain the function pointer with + * dlsym(). + * @signature: Offset of the SignatureBlob describing the parameter types and the + * return value type. + * @is_static: The function is a "static method"; in other words it's a pure + * function whose name is conceptually scoped to the object. + * @reserved: Reserved for future use. + * @reserved2: Reserved for future use. + * + * TODO + */ +typedef struct { + guint16 blob_type; /* 1 */ + + guint16 deprecated : 1; + guint16 setter : 1; + guint16 getter : 1; + guint16 constructor : 1; + guint16 wraps_vfunc : 1; + guint16 throws : 1; + guint16 index :10; + /* Note the bits above need to match CommonBlob + * and are thus exhausted, extend things using + * the reserved block below. */ + + guint32 name; + guint32 symbol; + guint32 signature; + + guint16 is_static : 1; + guint16 reserved : 15; + guint16 reserved2 : 16; +} FunctionBlob; + +/** + * CallbackBlob: + * @blob_type: TODO + * @deprecated: TODO + * @reserved: Reserved for future use. + * @name: TODO + * @signature: Offset of the #SignatureBlob describing the parameter types and + * the return value type. + * + * TODO + */ +typedef struct { + guint16 blob_type; /* 2 */ + + guint16 deprecated : 1; + guint16 reserved :15; + guint32 name; + guint32 signature; +} CallbackBlob; + +/** + * InterfaceTypeBlob: + * @pointer: Whether this type represents an indirection + * @reserved: Reserved for future use. + * @tag: A #GITypeTag + * @reserved2: Reserved for future use. + * @interface: Index of the directory entry for the interface. + * + * If the interface is an enum of flags type, is_pointer is 0, otherwise it is 1. + */ +typedef struct { + guint8 pointer :1; + guint8 reserved :2; + guint8 tag :5; + guint8 reserved2; + guint16 interface; +} InterfaceTypeBlob; + +/** + * ArrayTypeDimension: + * @length: TODO + * @size: TODO + * + * TODO + */ +typedef union { + guint16 length; + guint16 size; +} ArrayTypeDimension; + +/** + * ArrayTypeBlob: + * @pointer: TODO + * @reserved: Reserved for future use. + * @tag: TODO + * @zero_terminated: Indicates that the array must be terminated by a suitable + * #NULL value. + * @has_length: Indicates that length points to a parameter specifying the + * length of the array. If both has_length and zero_terminated are set, the + * convention is to pass -1 for the length if the array is zero-terminated. + * @has_size: Indicates that size is the fixed size of the array. + * @array_type: Indicates whether this is a C array, GArray, GPtrArray, or + * GByteArray. If something other than a C array, the length and element + * size are implicit in the structure. + * @reserved2: Reserved for future use. + * @dimensions: TODO + * @type: TODO + * + * TODO + */ +typedef struct { + guint16 pointer :1; + guint16 reserved :2; + guint16 tag :5; + + guint16 zero_terminated :1; + guint16 has_length :1; + guint16 has_size :1; + guint16 array_type :2; + guint16 reserved2 :3; + + ArrayTypeDimension dimensions; + + SimpleTypeBlob type; +} ArrayTypeBlob; + +/** + * ParamTypeBlob: + * @pointer: TODO + * @reserved: Reserved for future use. + * @tag: TODO + * @reserved2: Reserved for future use. + * @n_types: The number of parameter types to follow. + * @type: Describes the type of the list elements. + * + * TODO + */ +typedef struct { + guint8 pointer :1; + guint8 reserved :2; + guint8 tag :5; + + guint8 reserved2; + guint16 n_types; + + SimpleTypeBlob type[]; +} ParamTypeBlob; + +/** + * ErrorTypeBlob: + * @pointer: TODO + * @reserved: TODO + * @tag: TODO + * @reserved2: TODO + * @n_domains: TODO: must be 0 + * @domains: TODO + * + * TODO + */ +typedef struct { + guint8 pointer :1; + guint8 reserved :2; + guint8 tag :5; + + guint8 reserved2; + + guint16 n_domains; /* Must be 0 */ + guint16 domains[]; +} ErrorTypeBlob; + +/** + * ValueBlob: + * @deprecated: Whether this value is deprecated + * @unsigned_value: if set, value is a 32-bit unsigned integer cast to gint32 + * @reserved: Reserved for future use. + * @name: Name of blob + * @value: The numerical value + * + * Values commonly occur in enums and flags. + */ +typedef struct { + guint32 deprecated : 1; + guint32 unsigned_value : 1; + guint32 reserved :30; + guint32 name; + gint32 value; +} ValueBlob; + +/** + * FieldBlob: + * @name: The name of the field. + * @readable: TODO + * @writable: How the field may be accessed. + * @has_embedded_type: An anonymous type follows the FieldBlob. + * @reserved: Reserved for future use. + * @bits: If this field is part of a bitfield, the number of bits which it + * uses, otherwise 0. + * @struct_offset: The offset of the field in the struct. The value 0xFFFF + * indicates that the struct offset is unknown. + * @reserved2: Reserved for future use. + * @type: The type of the field. + * + * TODO + */ +typedef struct { + guint32 name; + + guint8 readable :1; + guint8 writable :1; + guint8 has_embedded_type :1; + guint8 reserved :5; + guint8 bits; + + guint16 struct_offset; + + guint32 reserved2; + + SimpleTypeBlob type; +} FieldBlob; + +/** + * RegisteredTypeBlob: + * @blob_type: TODO + * @deprecated: TODO + * @unregistered: TODO + * @reserved: Reserved for future use. + * @name: TODO + * @gtype_name: The name under which the type is registered with GType. + * @gtype_init: The symbol name of the get_type() function which registers the + * type. + * + * TODO + */ +typedef struct { + guint16 blob_type; + guint16 deprecated : 1; + guint16 unregistered : 1; + guint16 reserved :14; + guint32 name; + + guint32 gtype_name; + guint32 gtype_init; +} RegisteredTypeBlob; + +/** + * StructBlob: + * @blob_type: #BLOB_TYPE_STRUCT + * @deprecated: Whether this structure is deprecated + * @unregistered: If this is set, the type is not registered with GType. + * @is_gtype_struct: Whether this structure is the class or interface layout + * for a GObject + * @alignment: The byte boundary that the struct is aligned to in memory + * @foreign: If the type is foreign, eg if it's expected to be overridden by + * a native language binding instead of relying of introspected bindings. + * @reserved: Reserved for future use. + * @name: TODO + * @gtype_name: String name of the associated #GType + * @gtype_init: String naming the symbol which gets the runtime #GType + * @size: The size of the struct in bytes. + * @n_fields: TODO + * @n_methods: TODO + * @copy_func: String pointing to a function which can be called to copy + * the contents of this struct type + * @free_func: String pointing to a function which can be called to free + * the contents of this struct type + * + * TODO + */ +typedef struct { + guint16 blob_type; + + guint16 deprecated : 1; + guint16 unregistered : 1; + guint16 is_gtype_struct : 1; + guint16 alignment : 6; + guint16 foreign : 1; + guint16 reserved : 6; + + guint32 name; + + guint32 gtype_name; + guint32 gtype_init; + + guint32 size; + + guint16 n_fields; + guint16 n_methods; + + guint32 copy_func; + guint32 free_func; +} StructBlob; + +/** + * UnionBlob: + * @blob_type: TODO + * @deprecated: TODO + * @unregistered: If this is set, the type is not registered with GType. + * @discriminated: Is set if the union is discriminated + * @alignment: The byte boundary that the union is aligned to in memory + * @reserved: Reserved for future use. + * @name: TODO + * @gtype_name: String name of the associated #GType + * @gtype_init: String naming the symbol which gets the runtime #GType + * @size: TODO + * @n_fields: Length of the arrays + * @n_functions: TODO + * @copy_func: String pointing to a function which can be called to copy + * the contents of this union type + * @free_func: String pointing to a function which can be called to free + * the contents of this union type + * @discriminator_offset: Offset from the beginning of the union where the + * discriminator of a discriminated union is located. The value 0xFFFF + * indicates that the discriminator offset is unknown. + * @discriminator_type: Type of the discriminator + * + * TODO + */ +typedef struct { + guint16 blob_type; + guint16 deprecated : 1; + guint16 unregistered : 1; + guint16 discriminated : 1; + guint16 alignment : 6; + guint16 reserved : 7; + guint32 name; + + guint32 gtype_name; + guint32 gtype_init; + + guint32 size; + + guint16 n_fields; + guint16 n_functions; + + guint32 copy_func; + guint32 free_func; + + gint32 discriminator_offset; + SimpleTypeBlob discriminator_type; +} UnionBlob; + +/** + * EnumBlob: + * @blob_type: TODO + * @deprecated: TODO + * @unregistered: If this is set, the type is not registered with GType. + * @storage_type: The tag of the type used for the enum in the C ABI + * (will be a signed or unsigned integral type) + * @reserved: Reserved for future use. + * @name: TODO + * @gtype_name: String name of the associated #GType + * @gtype_init: String naming the symbol which gets the runtime #GType + * @n_values: The length of the values array. + * @n_methods: The length of the methods array. + * @error_domain: String naming the #GError domain this enum is associated with + * @values: TODO + * + * TODO + */ +typedef struct { + guint16 blob_type; + + guint16 deprecated : 1; + guint16 unregistered : 1; + guint16 storage_type : 5; + guint16 reserved : 9; + + guint32 name; + + guint32 gtype_name; + guint32 gtype_init; + + guint16 n_values; + guint16 n_methods; + + guint32 error_domain; + + ValueBlob values[]; +} EnumBlob; + +#define ACCESSOR_SENTINEL 0x3ff + +/** + * PropertyBlob: + * @name: The name of the property. + * @deprecated: TODO + * @readable: TODO + * @writable: TODO + * @construct: TODO + * @construct_only: The ParamFlags used when registering the property. + * @transfer_ownership: When writing, the type containing the property takes + * ownership of the value. When reading, the returned value needs to be + * released by the caller. + * @transfer_container_ownership: For container types indicates that the + * ownership of the container, but not of its contents, is transferred. + * This is typically the case when reading lists of statically allocated + * things. + * @setter: the index of the setter function for this property, if @writable + * is set; if the method is not known, the value will be set to %ACCESSOR_SENTINEL + * @getter: ths index of the getter function for this property, if @readable + * is set; if the method is not known, the value will be set to %ACCESSOR_SENTINEL + * @reserved: Reserved for future use. + * @reserved2: Reserved for future use. + * @type: Describes the type of the property. + * + * TODO + */ +typedef struct { + guint32 name; + + guint32 deprecated : 1; + guint32 readable : 1; + guint32 writable : 1; + guint32 construct : 1; + guint32 construct_only : 1; + guint32 transfer_ownership : 1; + guint32 transfer_container_ownership : 1; + guint32 setter :10; + guint32 getter :10; + guint32 reserved : 5; + + guint32 reserved2; + + SimpleTypeBlob type; +} PropertyBlob; + +/** + * SignalBlob: + * @deprecated: TODO + * @run_first: TODO + * @run_last: TODO + * @run_cleanup: TODO + * @no_recurse: TODO + * @detailed: TODO + * @action: TODO + * @no_hooks: The flags used when registering the signal. + * @has_class_closure: Set if the signal has a class closure. + * @true_stops_emit: Whether the signal has true-stops-emit semantics + * @reserved: Reserved for future use. + * @class_closure: The index of the class closure in the list of virtual + * functions of the object or interface on which the signal is defined. + * @name: The name of the signal. + * @reserved2: Reserved for future use. + * @signature: Offset of the SignatureBlob describing the parameter types + * and the return value type. + * + * TODO + */ +typedef struct { + guint16 deprecated : 1; + guint16 run_first : 1; + guint16 run_last : 1; + guint16 run_cleanup : 1; + guint16 no_recurse : 1; + guint16 detailed : 1; + guint16 action : 1; + guint16 no_hooks : 1; + guint16 has_class_closure : 1; + guint16 true_stops_emit : 1; + guint16 reserved : 6; + + guint16 class_closure; + + guint32 name; + + guint32 reserved2; + + guint32 signature; +} SignalBlob; + +/** + * VFuncBlob: + * @name: The name of the virtual function. + * @must_chain_up: If set, every implementation of this virtual function must + * chain up to the implementation of the parent class. + * @must_be_implemented: If set, every derived class must override this virtual + * function. + * @must_not_be_implemented: If set, derived class must not override this + * virtual function. + * @class_closure: Set if this virtual function is the class closure of a + * signal. + * @throws: This is now additionally stored in the #SignatureBlob. (deprecated) + * @reserved: Reserved for future use. + * @signal: The index of the signal in the list of signals of the object or + * interface to which this virtual function belongs. + * @struct_offset: The offset of the function pointer in the class struct. + * The value 0xFFFF indicates that the struct offset is unknown. + * @invoker: If a method invoker for this virtual exists, this is the offset + * in the class structure of the method. If no method is known, this value + * will be 0x3ff. + * @reserved2: Reserved for future use. + * @reserved3: Reserved for future use. + * @signature: Offset of the SignatureBlob describing the parameter types and + * the return value type. + * + * TODO + */ +typedef struct { + guint32 name; + + guint16 must_chain_up : 1; + guint16 must_be_implemented : 1; + guint16 must_not_be_implemented : 1; + guint16 class_closure : 1; + guint16 throws : 1; + guint16 reserved :11; + guint16 signal; + + guint16 struct_offset; + guint16 invoker : 10; /* Number of bits matches @index in FunctionBlob */ + guint16 reserved2 : 6; + + guint32 reserved3; + guint32 signature; +} VFuncBlob; + +/** + * ObjectBlob: + * @blob_type: #BLOB_TYPE_OBJECT + * @deprecated: whether the type is deprecated + * @abstract: whether the type can be instantiated + * @fundamental: this object is not a GObject derived type, instead it's + * an additional fundamental type. + * @final_: whether the type can be derived + * @reserved: Reserved for future use. + * @name: TODO + * @gtype_name: String name of the associated #GType + * @gtype_init: String naming the symbol which gets the runtime #GType + * @parent: The directory index of the parent type. This is only set for + * objects. If an object does not have a parent, it is zero. + * @gtype_struct: TODO + * @n_interfaces: TODO + * @n_fields: TODO + * @n_properties: TODO + * @n_methods: TODO + * @n_signals: TODO + * @n_vfuncs: TODO + * @n_constants: The lengths of the arrays.Up to 16bits of padding may be + * inserted between the arrays to ensure that they start on a 32bit + * boundary. + * @n_field_callbacks: The number of n_fields which are also callbacks. + * This is used to calculate the fields section size in constant time. + * @ref_func: String pointing to a function which can be called to increase + * the reference count for an instance of this object type. + * @unref_func: String pointing to a function which can be called to decrease + * the reference count for an instance of this object type. + * @set_value_func: String pointing to a function which can be called to + * convert a pointer of this object to a GValue + * @get_value_func: String pointing to a function which can be called to + * convert extract a pointer to this object from a GValue + * @reserved3: Reserved for future use. + * @reserved4: Reserved for future use. + * @interfaces: An array of indices of directory entries for the implemented + * interfaces. + * + * TODO + */ +typedef struct { + guint16 blob_type; /* 7 */ + guint16 deprecated : 1; + guint16 abstract : 1; + guint16 fundamental : 1; + guint16 final_ : 1; + guint16 reserved :12; + guint32 name; + + guint32 gtype_name; + guint32 gtype_init; + + guint16 parent; + guint16 gtype_struct; + + guint16 n_interfaces; + guint16 n_fields; + guint16 n_properties; + guint16 n_methods; + guint16 n_signals; + guint16 n_vfuncs; + guint16 n_constants; + guint16 n_field_callbacks; + + guint32 ref_func; + guint32 unref_func; + guint32 set_value_func; + guint32 get_value_func; + + guint32 reserved3; + guint32 reserved4; + + guint16 interfaces[]; +} ObjectBlob; + +/** + * InterfaceBlob: + * @blob_type: TODO + * @deprecated: TODO + * @reserved: Reserved for future use. + * @name: TODO + * @gtype_name: TODO + * @gtype_init: TODO + * @gtype_struct: Name of the interface "class" C structure + * @n_prerequisites: Number of prerequisites + * @n_properties: Number of properties + * @n_methods: Number of methods + * @n_signals: Number of signals + * @n_vfuncs: Number of virtual functions + * @n_constants: The lengths of the arrays. Up to 16bits of padding may be + * inserted between the arrays to ensure that they start on a 32bit + * boundary. + * @padding: TODO + * @reserved2: Reserved for future use. + * @reserved3: Reserved for future use. + * @prerequisites: An array of indices of directory entries for required + * interfaces. + * + * TODO + */ +typedef struct { + guint16 blob_type; + guint16 deprecated : 1; + guint16 reserved :15; + guint32 name; + + guint32 gtype_name; + guint32 gtype_init; + guint16 gtype_struct; + + guint16 n_prerequisites; + guint16 n_properties; + guint16 n_methods; + guint16 n_signals; + guint16 n_vfuncs; + guint16 n_constants; + + guint16 padding; + + guint32 reserved2; + guint32 reserved3; + + guint16 prerequisites[]; +} InterfaceBlob; + +/** + * ConstantBlob: + * @blob_type: TODO + * @deprecated: TODO + * @reserved: Reserved for future use. + * @name: TODO + * @type: The type of the value. In most cases this should be a numeric type + * or string. + * @size: The size of the value in bytes. + * @offset: The offset of the value in the typelib. + * @reserved2: Reserved for future use. + * + * TODO + */ +typedef struct { + guint16 blob_type; + guint16 deprecated : 1; + guint16 reserved :15; + guint32 name; + + SimpleTypeBlob type; + + guint32 size; + guint32 offset; + + guint32 reserved2; +} ConstantBlob; + +/** + * AttributeBlob: + * @offset: The offset of the typelib entry to which this attribute refers. + * Attributes are kept sorted by offset, so that the attributes of an + * entry can be found by a binary search. + * @name: The name of the attribute, a string. + * @value: The value of the attribute (also a string) + * + * TODO + */ +typedef struct { + guint32 offset; + guint32 name; + guint32 value; +} AttributeBlob; + +struct _GITypelib { + /*< private >*/ + guchar *data; + gsize len; + gboolean owns_memory; + GMappedFile *mfile; + GList *modules; + gboolean open_attempted; +}; + +DirEntry *g_typelib_get_dir_entry (GITypelib *typelib, + guint16 index); + +DirEntry *g_typelib_get_dir_entry_by_name (GITypelib *typelib, + const char *name); + +DirEntry *g_typelib_get_dir_entry_by_gtype_name (GITypelib *typelib, + const gchar *gtype_name); + +DirEntry *g_typelib_get_dir_entry_by_error_domain (GITypelib *typelib, + GQuark error_domain); + +gboolean g_typelib_matches_gtype_name_prefix (GITypelib *typelib, + const gchar *gtype_name); + + +GI_AVAILABLE_IN_ALL +void g_typelib_check_sanity (void); + +/** + * g_typelib_get_string: + * @typelib: TODO + * @offset: TODO + * + * TODO + * + * Returns: TODO + */ +#define g_typelib_get_string(typelib,offset) ((const gchar*)&(typelib->data)[(offset)]) + + +/** + * GITypelibError: + * @G_TYPELIB_ERROR_INVALID: the typelib is invalid + * @G_TYPELIB_ERROR_INVALID_HEADER: the typelib header is invalid + * @G_TYPELIB_ERROR_INVALID_DIRECTORY: the typelib directory is invalid + * @G_TYPELIB_ERROR_INVALID_ENTRY: a typelib entry is invalid + * @G_TYPELIB_ERROR_INVALID_BLOB: a typelib blob is invalid + * + * A error set while validating the #GITypelib + */ +typedef enum +{ + G_TYPELIB_ERROR_INVALID, + G_TYPELIB_ERROR_INVALID_HEADER, + G_TYPELIB_ERROR_INVALID_DIRECTORY, + G_TYPELIB_ERROR_INVALID_ENTRY, + G_TYPELIB_ERROR_INVALID_BLOB +} GITypelibError; + +/** + * G_TYPELIB_ERROR: + * + * TODO + */ +#define G_TYPELIB_ERROR (g_typelib_error_quark ()) + +GQuark g_typelib_error_quark (void); + + +GI_AVAILABLE_IN_ALL +gboolean g_typelib_validate (GITypelib *typelib, + GError **error); + + +/* defined in gibaseinfo.c */ +AttributeBlob *_attribute_blob_find_first (GIBaseInfo *info, + guint32 blob_offset); + +/** + * GITypelibHashBuilder: + * + * TODO + */ +typedef struct _GITypelibHashBuilder GITypelibHashBuilder; + +GITypelibHashBuilder * _gi_typelib_hash_builder_new (void); + +void _gi_typelib_hash_builder_add_string (GITypelibHashBuilder *builder, const char *str, guint16 value); + +gboolean _gi_typelib_hash_builder_prepare (GITypelibHashBuilder *builder); + +guint32 _gi_typelib_hash_builder_get_buffer_size (GITypelibHashBuilder *builder); + +void _gi_typelib_hash_builder_pack (GITypelibHashBuilder *builder, guint8* mem, guint32 size); + +void _gi_typelib_hash_builder_destroy (GITypelibHashBuilder *builder); + +guint16 _gi_typelib_hash_search (guint8* memory, const char *str, guint n_entries); + + +G_END_DECLS + +#endif /* __G_TYPELIB_H__ */ + diff --git a/girepository/gitypelib.c b/girepository/gitypelib.c new file mode 100644 index 000000000..29349da33 --- /dev/null +++ b/girepository/gitypelib.c @@ -0,0 +1,2527 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: typelib validation, auxiliary functions + * related to the binary typelib format + * + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include + +#include + +#include "gitypelib-internal.h" + +typedef struct { + GITypelib *typelib; + GSList *context_stack; +} ValidateContext; + +#define ALIGN_VALUE(this, boundary) \ + (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1))) + +static void +push_context (ValidateContext *ctx, const char *name) +{ + ctx->context_stack = g_slist_prepend (ctx->context_stack, (char*)name); +} + +static void +pop_context (ValidateContext *ctx) +{ + g_assert (ctx->context_stack != NULL); + ctx->context_stack = g_slist_delete_link (ctx->context_stack, + ctx->context_stack); +} + +static gboolean +validate_interface_blob (ValidateContext *ctx, + guint32 offset, + GError **error); + +static DirEntry * +get_dir_entry_checked (GITypelib *typelib, + guint16 index, + GError **error) +{ + Header *header = (Header *)typelib->data; + guint32 offset; + + if (index == 0 || index > header->n_entries) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid directory index %d", index); + return FALSE; + } + + offset = header->directory + (index - 1) * header->entry_blob_size; + + if (typelib->len < offset + sizeof (DirEntry)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + return (DirEntry *)&typelib->data[offset]; +} + + +static CommonBlob * +get_blob (GITypelib *typelib, + guint32 offset, + GError **error) +{ + if (typelib->len < offset + sizeof (CommonBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + return (CommonBlob *)&typelib->data[offset]; +} + +static InterfaceTypeBlob * +get_type_blob (GITypelib *typelib, + SimpleTypeBlob *simple, + GError **error) +{ + if (simple->offset == 0) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "Expected blob for type"); + return FALSE; + } + + if (simple->flags.reserved == 0 && simple->flags.reserved2 == 0) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "Expected non-basic type but got %d", + simple->flags.tag); + return FALSE; + } + + return (InterfaceTypeBlob*) get_blob (typelib, simple->offset, error); +} + +/** + * g_typelib_get_dir_entry: + * @typelib: TODO + * @index: TODO + * + * TODO + * + * Returns: TODO + */ +DirEntry * +g_typelib_get_dir_entry (GITypelib *typelib, + guint16 index) +{ + Header *header = (Header *)typelib->data; + + 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; +} + +/** + * g_typelib_get_dir_entry_by_name: + * @typelib: TODO + * @name: TODO + * + * TODO + * + * Returns: TODO + */ +DirEntry * +g_typelib_get_dir_entry_by_name (GITypelib *typelib, + const char *name) +{ + Section *dirindex; + gint i, n_entries; + const char *entry_name; + DirEntry *entry; + + dirindex = get_section_by_id (typelib, GI_SECTION_DIRECTORY_INDEX); + n_entries = ((Header *)typelib->data)->n_local_entries; + + if (dirindex == NULL) + { + 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 + { + guint8 *hash = (guint8*) &typelib->data[dirindex->offset]; + guint16 index; + + index = _gi_typelib_hash_search (hash, name, n_entries); + 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; + } +} + +/** + * g_typelib_get_dir_entry_by_gtype_name: + * @typelib: TODO + * @gtype_name: TODO + * + * TODO + * + * Returns: TODO + */ +DirEntry * +g_typelib_get_dir_entry_by_gtype_name (GITypelib *typelib, + const gchar *gtype_name) +{ + Header *header = (Header *)typelib->data; + guint i; + + for (i = 1; i <= header->n_local_entries; i++) + { + RegisteredTypeBlob *blob; + const char *type; + DirEntry *entry = g_typelib_get_dir_entry (typelib, i); + if (!BLOB_IS_REGISTERED_TYPE (entry)) + continue; + + blob = (RegisteredTypeBlob *)(&typelib->data[entry->offset]); + if (!blob->gtype_name) + continue; + + type = g_typelib_get_string (typelib, blob->gtype_name); + if (strcmp (type, gtype_name) == 0) + return entry; + } + return NULL; +} + +typedef struct { + const char *s; + const char *separator; + gsize sep_len; + GString buf; +} StrSplitIter; + +static void +strsplit_iter_init (StrSplitIter *iter, + const char *s, + const char *separator) +{ + iter->s = s; + iter->separator = separator; + iter->sep_len = strlen (separator); + iter->buf.str = NULL; + iter->buf.len = 0; + iter->buf.allocated_len = 0; +} + +static gboolean +strsplit_iter_next (StrSplitIter *iter, + const char **out_val) +{ + const char *s = iter->s; + const char *next; + gsize len; + + if (!s) + return FALSE; + next = strstr (s, iter->separator); + if (next) + { + iter->s = next + iter->sep_len; + len = next - s; + } + else + { + iter->s = NULL; + len = strlen (s); + } + if (len == 0) + { + *out_val = ""; + } + else + { + g_string_overwrite_len (&iter->buf, 0, s, (gssize)len); + *out_val = iter->buf.str; + } + return TRUE; +} + +static void +strsplit_iter_clear (StrSplitIter *iter) +{ + g_free (iter->buf.str); +} + +/** + * g_typelib_matches_gtype_name_prefix: + * @typelib: TODO + * @gtype_name: TODO + * + * TODO + * + * Returns: TODO + */ +gboolean +g_typelib_matches_gtype_name_prefix (GITypelib *typelib, + const gchar *gtype_name) +{ + Header *header = (Header *)typelib->data; + const char *c_prefix; + const gchar *prefix; + gboolean ret = FALSE; + StrSplitIter split_iter; + gsize gtype_name_len; + + c_prefix = g_typelib_get_string (typelib, header->c_prefix); + if (c_prefix == NULL || strlen (c_prefix) == 0) + return FALSE; + + gtype_name_len = strlen (gtype_name); + + /* c_prefix is a comma separated string of supported prefixes + * in the typelib. + * We match the specified gtype_name if the gtype_name starts + * with the prefix, and is followed by a capital letter. + * For example, a typelib offering the 'Gdk' prefix does match + * GdkX11Cursor, however a typelib offering the 'G' prefix does not. + */ + strsplit_iter_init (&split_iter, c_prefix, ","); + while (strsplit_iter_next (&split_iter, &prefix)) + { + size_t len = strlen (prefix); + + if (gtype_name_len < len) + continue; + + if (strncmp (prefix, gtype_name, len) != 0) + continue; + + if (g_ascii_isupper (gtype_name[len])) + { + ret = TRUE; + break; + } + } + strsplit_iter_clear (&split_iter); + return ret; +} + +/** + * g_typelib_get_dir_entry_by_error_domain: + * @typelib: TODO + * @error_domain: TODO + * + * TODO + * + * Returns: TODO + */ +DirEntry * +g_typelib_get_dir_entry_by_error_domain (GITypelib *typelib, + GQuark error_domain) +{ + Header *header = (Header *)typelib->data; + guint n_entries = header->n_local_entries; + const char *domain_string = g_quark_to_string (error_domain); + DirEntry *entry; + guint i; + + for (i = 1; i <= n_entries; i++) + { + EnumBlob *blob; + const char *enum_domain_string; + + entry = g_typelib_get_dir_entry (typelib, i); + if (entry->blob_type != BLOB_TYPE_ENUM) + continue; + + blob = (EnumBlob *)(&typelib->data[entry->offset]); + if (!blob->error_domain) + continue; + + enum_domain_string = g_typelib_get_string (typelib, blob->error_domain); + if (strcmp (domain_string, enum_domain_string) == 0) + return entry; + } + return NULL; +} + +/** + * g_typelib_check_sanity: + * + * TODO + */ +void +g_typelib_check_sanity (void) +{ + /* Check that struct layout is as we expect */ + + gboolean size_check_ok = TRUE; + +#define CHECK_SIZE(s,n) \ + if (sizeof(s) != n) \ + { \ + g_printerr ("sizeof("#s") is expected to be %d but is %"G_GSIZE_FORMAT".\n", \ + n, sizeof (s)); \ + size_check_ok = FALSE; \ + } + + /* When changing the size of a typelib structure, you are required to update + * the hardcoded size here. Do NOT change these to use sizeof(); these + * should match whatever is defined in the text specification and serve as + * a sanity check on structure modifications. + * + * Everything else in the code however should be using sizeof(). + */ + + CHECK_SIZE (Header, 112); + CHECK_SIZE (DirEntry, 12); + CHECK_SIZE (SimpleTypeBlob, 4); + CHECK_SIZE (ArgBlob, 16); + CHECK_SIZE (SignatureBlob, 8); + CHECK_SIZE (CommonBlob, 8); + CHECK_SIZE (FunctionBlob, 20); + CHECK_SIZE (CallbackBlob, 12); + CHECK_SIZE (InterfaceTypeBlob, 4); + CHECK_SIZE (ArrayTypeBlob, 8); + CHECK_SIZE (ParamTypeBlob, 4); + CHECK_SIZE (ErrorTypeBlob, 4); + CHECK_SIZE (ValueBlob, 12); + CHECK_SIZE (FieldBlob, 16); + CHECK_SIZE (RegisteredTypeBlob, 16); + CHECK_SIZE (StructBlob, 32); + CHECK_SIZE (EnumBlob, 24); + CHECK_SIZE (PropertyBlob, 16); + CHECK_SIZE (SignalBlob, 16); + CHECK_SIZE (VFuncBlob, 20); + CHECK_SIZE (ObjectBlob, 60); + CHECK_SIZE (InterfaceBlob, 40); + CHECK_SIZE (ConstantBlob, 24); + CHECK_SIZE (AttributeBlob, 12); + CHECK_SIZE (UnionBlob, 40); +#undef CHECK_SIZE + + g_assert (size_check_ok); +} + + +static gboolean +is_aligned (guint32 offset) +{ + return offset == ALIGN_VALUE (offset, 4); +} + +#define MAX_NAME_LEN 2048 + +static const char * +get_string (GITypelib *typelib, guint32 offset, GError **error) +{ + if (typelib->len < offset) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "Buffer is too short while looking up name"); + return NULL; + } + + return (const char*)&typelib->data[offset]; +} + +static const char * +get_string_nofail (GITypelib *typelib, guint32 offset) +{ + const char *ret = get_string (typelib, offset, NULL); + g_assert (ret); + return ret; +} + +static gboolean +validate_name (GITypelib *typelib, + const char *msg, + const guchar *data, guint32 offset, + GError **error) +{ + const char *name; + + name = get_string (typelib, offset, error); + if (!name) + return FALSE; + + if (!memchr (name, '\0', MAX_NAME_LEN)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The %s is too long: %s", + msg, name); + return FALSE; + } + + if (strspn (name, G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_") < strlen (name)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The %s contains invalid characters: '%s'", + msg, name); + return FALSE; + } + + return TRUE; +} + +/* Fast path sanity check, operates on a memory blob */ +static gboolean +validate_header_basic (const guint8 *memory, + gsize len, + GError **error) +{ + Header *header = (Header *)memory; + + if (len < sizeof (Header)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The specified typelib length %" G_GSIZE_FORMAT " is too short", + len); + return FALSE; + } + + if (strncmp (header->magic, G_IR_MAGIC, 16) != 0) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Invalid magic header"); + return FALSE; + + } + + if (header->major_version != 4) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Typelib version mismatch; expected 4, found %d", + header->major_version); + return FALSE; + + } + + if (header->n_entries < header->n_local_entries) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Inconsistent entry counts"); + return FALSE; + } + + if (header->size != len) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Typelib size %" G_GSIZE_FORMAT " does not match %" G_GSIZE_FORMAT, + (gsize) header->size, len); + return FALSE; + } + + /* This is a sanity check for a specific typelib; it + * prevents us from loading an incompatible typelib. + * + * The hardcoded checks in g_typelib_check_sanity to + * protect against inadvertent or buggy changes to the typelib format + * itself. + */ + + if (header->entry_blob_size != sizeof (DirEntry) || + header->function_blob_size != sizeof (FunctionBlob) || + header->callback_blob_size != sizeof (CallbackBlob) || + header->signal_blob_size != sizeof (SignalBlob) || + header->vfunc_blob_size != sizeof (VFuncBlob) || + header->arg_blob_size != sizeof (ArgBlob) || + header->property_blob_size != sizeof (PropertyBlob) || + header->field_blob_size != sizeof (FieldBlob) || + header->value_blob_size != sizeof (ValueBlob) || + header->constant_blob_size != sizeof (ConstantBlob) || + header->attribute_blob_size != sizeof (AttributeBlob) || + header->signature_blob_size != sizeof (SignatureBlob) || + header->enum_blob_size != sizeof (EnumBlob) || + header->struct_blob_size != sizeof (StructBlob) || + header->object_blob_size != sizeof(ObjectBlob) || + header->interface_blob_size != sizeof (InterfaceBlob) || + header->union_blob_size != sizeof (UnionBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Blob size mismatch"); + return FALSE; + } + + if (!is_aligned (header->directory)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Misaligned directory"); + return FALSE; + } + + if (!is_aligned (header->attributes)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Misaligned attributes"); + return FALSE; + } + + if (header->attributes == 0 && header->n_attributes > 0) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_HEADER, + "Wrong number of attributes"); + return FALSE; + } + + return TRUE; +} + +static gboolean +validate_header (ValidateContext *ctx, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + + if (!validate_header_basic (typelib->data, typelib->len, error)) + return FALSE; + + { + Header *header = (Header*)typelib->data; + if (!validate_name (typelib, "namespace", typelib->data, header->namespace, error)) + return FALSE; + } + + return TRUE; +} + +static gboolean validate_type_blob (GITypelib *typelib, + guint32 offset, + guint32 signature_offset, + gboolean return_type, + GError **error); + +static gboolean +validate_array_type_blob (GITypelib *typelib, + guint32 offset, + guint32 signature_offset, + gboolean return_type, + GError **error) +{ + /* FIXME validate length */ + + if (!validate_type_blob (typelib, + offset + G_STRUCT_OFFSET (ArrayTypeBlob, type), + 0, FALSE, error)) + return FALSE; + + return TRUE; +} + +static gboolean +validate_iface_type_blob (GITypelib *typelib, + guint32 offset, + guint32 signature_offset, + gboolean return_type, + GError **error) +{ + InterfaceTypeBlob *blob; + InterfaceBlob *target; + + blob = (InterfaceTypeBlob*)&typelib->data[offset]; + + target = (InterfaceBlob*) get_dir_entry_checked (typelib, blob->interface, error); + + if (!target) + return FALSE; + if (target->blob_type == 0) /* non-local */ + return TRUE; + + return TRUE; +} + +static gboolean +validate_param_type_blob (GITypelib *typelib, + guint32 offset, + guint32 signature_offset, + gboolean return_type, + gint n_params, + GError **error) +{ + ParamTypeBlob *blob; + gint i; + + blob = (ParamTypeBlob*)&typelib->data[offset]; + + if (!blob->pointer) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Pointer type exected for tag %d", blob->tag); + return FALSE; + } + + if (blob->n_types != n_params) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Parameter type number mismatch"); + return FALSE; + } + + for (i = 0; i < n_params; i++) + { + if (!validate_type_blob (typelib, + offset + sizeof (ParamTypeBlob) + + i * sizeof (SimpleTypeBlob), + 0, FALSE, error)) + return FALSE; + } + + return TRUE; +} + +static gboolean +validate_error_type_blob (GITypelib *typelib, + guint32 offset, + guint32 signature_offset, + gboolean return_type, + GError **error) +{ + ErrorTypeBlob *blob; + + blob = (ErrorTypeBlob*)&typelib->data[offset]; + + if (!blob->pointer) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Pointer type exected for tag %d", blob->tag); + return FALSE; + } + + return TRUE; +} + +static gboolean +validate_type_blob (GITypelib *typelib, + guint32 offset, + guint32 signature_offset, + gboolean return_type, + GError **error) +{ + SimpleTypeBlob *simple; + InterfaceTypeBlob *iface; + + simple = (SimpleTypeBlob *)&typelib->data[offset]; + + if (simple->flags.reserved == 0 && + simple->flags.reserved2 == 0) + { + if (!GI_TYPE_TAG_IS_BASIC(simple->flags.tag)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid non-basic tag %d in simple type", simple->flags.tag); + return FALSE; + } + + if (simple->flags.tag >= GI_TYPE_TAG_UTF8 && + simple->flags.tag != GI_TYPE_TAG_UNICHAR && + !simple->flags.pointer) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Pointer type exected for tag %d", simple->flags.tag); + return FALSE; + } + + return TRUE; + } + + iface = (InterfaceTypeBlob*)&typelib->data[simple->offset]; + + switch (iface->tag) + { + case GI_TYPE_TAG_ARRAY: + if (!validate_array_type_blob (typelib, simple->offset, + signature_offset, return_type, error)) + return FALSE; + break; + case GI_TYPE_TAG_INTERFACE: + if (!validate_iface_type_blob (typelib, simple->offset, + signature_offset, return_type, error)) + return FALSE; + break; + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + if (!validate_param_type_blob (typelib, simple->offset, + signature_offset, return_type, 1, error)) + return FALSE; + break; + case GI_TYPE_TAG_GHASH: + if (!validate_param_type_blob (typelib, simple->offset, + signature_offset, return_type, 2, error)) + return FALSE; + break; + case GI_TYPE_TAG_ERROR: + if (!validate_error_type_blob (typelib, simple->offset, + signature_offset, return_type, error)) + return FALSE; + break; + default: + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong tag in complex type"); + return FALSE; + } + + return TRUE; +} + +static gboolean +validate_arg_blob (GITypelib *typelib, + guint32 offset, + guint32 signature_offset, + GError **error) +{ + ArgBlob *blob; + + if (typelib->len < offset + sizeof (ArgBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (ArgBlob*) &typelib->data[offset]; + + if (!validate_name (typelib, "argument", typelib->data, blob->name, error)) + return FALSE; + + if (!validate_type_blob (typelib, + offset + G_STRUCT_OFFSET (ArgBlob, arg_type), + signature_offset, FALSE, error)) + return FALSE; + + return TRUE; +} + +static SimpleTypeBlob * +return_type_from_signature (GITypelib *typelib, + guint32 offset, + GError **error) +{ + SignatureBlob *blob; + if (typelib->len < offset + sizeof (SignatureBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return NULL; + } + + blob = (SignatureBlob*) &typelib->data[offset]; + if (blob->return_type.offset == 0) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "No return type found in signature"); + return NULL; + } + + return (SimpleTypeBlob *)&typelib->data[offset + G_STRUCT_OFFSET (SignatureBlob, return_type)]; +} + +static gboolean +validate_signature_blob (GITypelib *typelib, + guint32 offset, + GError **error) +{ + SignatureBlob *blob; + gint i; + + if (typelib->len < offset + sizeof (SignatureBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (SignatureBlob*) &typelib->data[offset]; + + if (blob->return_type.offset != 0) + { + if (!validate_type_blob (typelib, + offset + G_STRUCT_OFFSET (SignatureBlob, return_type), + offset, TRUE, error)) + return FALSE; + } + + for (i = 0; i < blob->n_arguments; i++) + { + if (!validate_arg_blob (typelib, + offset + sizeof (SignatureBlob) + + i * sizeof (ArgBlob), + offset, + error)) + return FALSE; + } + + /* FIXME check constraints on return_value */ + /* FIXME check array-length pairs */ + return TRUE; +} + +static gboolean +validate_function_blob (ValidateContext *ctx, + guint32 offset, + guint16 container_type, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + FunctionBlob *blob; + + if (typelib->len < offset + sizeof (FunctionBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (FunctionBlob*) &typelib->data[offset]; + + if (blob->blob_type != BLOB_TYPE_FUNCTION) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong blob type %d, expected function", blob->blob_type); + return FALSE; + } + + if (!validate_name (typelib, "function", typelib->data, blob->name, error)) + return FALSE; + + push_context (ctx, get_string_nofail (typelib, blob->name)); + + if (!validate_name (typelib, "function symbol", typelib->data, blob->symbol, error)) + return FALSE; + + if (blob->constructor) + { + switch (container_type) + { + case BLOB_TYPE_BOXED: + case BLOB_TYPE_STRUCT: + case BLOB_TYPE_UNION: + case BLOB_TYPE_OBJECT: + case BLOB_TYPE_INTERFACE: + break; + default: + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Constructor not allowed"); + return FALSE; + } + } + + if (blob->setter || blob->getter || blob->wraps_vfunc) + { + switch (container_type) + { + case BLOB_TYPE_OBJECT: + case BLOB_TYPE_INTERFACE: + break; + default: + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Setter, getter or wrapper not allowed"); + return FALSE; + } + } + + if (blob->index) + { + if (!(blob->setter || blob->getter || blob->wraps_vfunc)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Must be setter, getter or wrapper"); + return FALSE; + } + } + + /* FIXME: validate index range */ + + if (!validate_signature_blob (typelib, blob->signature, error)) + return FALSE; + + if (blob->constructor) + { + SimpleTypeBlob *simple = return_type_from_signature (typelib, + blob->signature, + error); + InterfaceTypeBlob *iface_type; + + if (!simple) + return FALSE; + iface_type = get_type_blob (typelib, simple, error); + if (!iface_type) + return FALSE; + if (iface_type->tag != GI_TYPE_TAG_INTERFACE && + (container_type == BLOB_TYPE_OBJECT || + container_type == BLOB_TYPE_INTERFACE)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "Invalid return type '%s' for constructor '%s'", + g_type_tag_to_string (iface_type->tag), + get_string_nofail (typelib, blob->symbol)); + return FALSE; + } + } + + pop_context (ctx); + + return TRUE; +} + +static gboolean +validate_callback_blob (ValidateContext *ctx, + guint32 offset, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + CallbackBlob *blob; + + if (typelib->len < offset + sizeof (CallbackBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (CallbackBlob*) &typelib->data[offset]; + + if (blob->blob_type != BLOB_TYPE_CALLBACK) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong blob type"); + return FALSE; + } + + if (!validate_name (typelib, "callback", typelib->data, blob->name, error)) + return FALSE; + + push_context (ctx, get_string_nofail (typelib, blob->name)); + + if (!validate_signature_blob (typelib, blob->signature, error)) + return FALSE; + + pop_context (ctx); + + return TRUE; +} + +static gboolean +validate_constant_blob (GITypelib *typelib, + guint32 offset, + GError **error) +{ + guint value_size[] = { + 0, /* VOID */ + 4, /* BOOLEAN */ + 1, /* INT8 */ + 1, /* UINT8 */ + 2, /* INT16 */ + 2, /* UINT16 */ + 4, /* INT32 */ + 4, /* UINT32 */ + 8, /* INT64 */ + 8, /* UINT64 */ + sizeof (gfloat), + sizeof (gdouble), + 0, /* GTYPE */ + 0, /* UTF8 */ + 0, /* FILENAME */ + 0, /* ARRAY */ + 0, /* INTERFACE */ + 0, /* GLIST */ + 0, /* GSLIST */ + 0, /* GHASH */ + 0, /* ERROR */ + 4 /* UNICHAR */ + }; + ConstantBlob *blob; + SimpleTypeBlob *type; + + g_assert (G_N_ELEMENTS (value_size) == GI_TYPE_TAG_N_TYPES); + + if (typelib->len < offset + sizeof (ConstantBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (ConstantBlob*) &typelib->data[offset]; + + if (blob->blob_type != BLOB_TYPE_CONSTANT) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong blob type"); + return FALSE; + } + + if (!validate_name (typelib, "constant", typelib->data, blob->name, error)) + return FALSE; + + if (!validate_type_blob (typelib, offset + G_STRUCT_OFFSET (ConstantBlob, type), + 0, FALSE, error)) + return FALSE; + + if (!is_aligned (blob->offset)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Misaligned constant value"); + return FALSE; + } + + type = (SimpleTypeBlob *)&typelib->data[offset + G_STRUCT_OFFSET (ConstantBlob, type)]; + if (type->flags.reserved == 0 && type->flags.reserved2 == 0) + { + if (type->flags.tag == 0) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Constant value type void"); + return FALSE; + } + + if (value_size[type->flags.tag] != 0 && + blob->size != value_size[type->flags.tag]) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Constant value size mismatch"); + return FALSE; + } + /* FIXME check string values */ + } + + return TRUE; +} + +static gboolean +validate_value_blob (GITypelib *typelib, + guint32 offset, + GError **error) +{ + ValueBlob *blob; + + if (typelib->len < offset + sizeof (ValueBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (ValueBlob*) &typelib->data[offset]; + + if (!validate_name (typelib, "value", typelib->data, blob->name, error)) + return FALSE; + + return TRUE; +} + +static gboolean +validate_field_blob (ValidateContext *ctx, + guint32 offset, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + Header *header = (Header *)typelib->data; + FieldBlob *blob; + + if (typelib->len < offset + sizeof (FieldBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (FieldBlob*) &typelib->data[offset]; + + if (!validate_name (typelib, "field", typelib->data, blob->name, error)) + return FALSE; + + if (blob->has_embedded_type) + { + if (!validate_callback_blob (ctx, offset + header->field_blob_size, error)) + return FALSE; + } + else if (!validate_type_blob (typelib, + offset + G_STRUCT_OFFSET (FieldBlob, type), + 0, FALSE, error)) + return FALSE; + + return TRUE; +} + +static gboolean +validate_property_blob (GITypelib *typelib, + guint32 offset, + GError **error) +{ + PropertyBlob *blob; + + if (typelib->len < offset + sizeof (PropertyBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (PropertyBlob*) &typelib->data[offset]; + + if (!validate_name (typelib, "property", typelib->data, blob->name, error)) + return FALSE; + + if (!validate_type_blob (typelib, + offset + G_STRUCT_OFFSET (PropertyBlob, type), + 0, FALSE, error)) + return FALSE; + + return TRUE; +} + +static gboolean +validate_signal_blob (GITypelib *typelib, + guint32 offset, + guint32 container_offset, + GError **error) +{ + SignalBlob *blob; + gint n_signals; + + if (typelib->len < offset + sizeof (SignalBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (SignalBlob*) &typelib->data[offset]; + + if (!validate_name (typelib, "signal", typelib->data, blob->name, error)) + return FALSE; + + if ((blob->run_first != 0) + + (blob->run_last != 0) + + (blob->run_cleanup != 0) != 1) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid signal run flags"); + return FALSE; + } + + if (blob->has_class_closure) + { + if (((CommonBlob*)&typelib->data[container_offset])->blob_type == BLOB_TYPE_OBJECT) + { + ObjectBlob *object; + + object = (ObjectBlob*)&typelib->data[container_offset]; + + n_signals = object->n_signals; + } + else + { + InterfaceBlob *iface; + + iface = (InterfaceBlob*)&typelib->data[container_offset]; + + n_signals = iface->n_signals; + } + + if (blob->class_closure >= n_signals) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid class closure index"); + return FALSE; + } + } + + if (!validate_signature_blob (typelib, blob->signature, error)) + return FALSE; + + return TRUE; +} + +static gboolean +validate_vfunc_blob (GITypelib *typelib, + guint32 offset, + guint32 container_offset, + GError **error) +{ + VFuncBlob *blob; + gint n_vfuncs; + + if (typelib->len < offset + sizeof (VFuncBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (VFuncBlob*) &typelib->data[offset]; + + if (!validate_name (typelib, "vfunc", typelib->data, blob->name, error)) + return FALSE; + + if (blob->class_closure) + { + if (((CommonBlob*)&typelib->data[container_offset])->blob_type == BLOB_TYPE_OBJECT) + { + ObjectBlob *object; + + object = (ObjectBlob*)&typelib->data[container_offset]; + + n_vfuncs = object->n_vfuncs; + } + else + { + InterfaceBlob *iface; + + iface = (InterfaceBlob*)&typelib->data[container_offset]; + + n_vfuncs = iface->n_vfuncs; + } + + if (blob->class_closure >= n_vfuncs) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid class closure index"); + return FALSE; + } + } + + if (!validate_signature_blob (typelib, blob->signature, error)) + return FALSE; + + return TRUE; +} + +static gboolean +validate_struct_blob (ValidateContext *ctx, + guint32 offset, + guint16 blob_type, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + StructBlob *blob; + gint i; + guint32 field_offset; + + if (typelib->len < offset + sizeof (StructBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (StructBlob*) &typelib->data[offset]; + + if (blob->blob_type != blob_type) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong blob type"); + return FALSE; + } + + if (!validate_name (typelib, "struct", typelib->data, blob->name, error)) + return FALSE; + + push_context (ctx, get_string_nofail (typelib, blob->name)); + + if (!blob->unregistered) + { + if (!validate_name (typelib, "boxed", typelib->data, blob->gtype_name, error)) + return FALSE; + + if (!validate_name (typelib, "boxed", typelib->data, blob->gtype_init, error)) + return FALSE; + } + else + { + if (blob->gtype_name || blob->gtype_init) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Gtype data in struct"); + return FALSE; + } + } + + if (typelib->len < offset + sizeof (StructBlob) + + blob->n_fields * sizeof (FieldBlob) + + blob->n_methods * sizeof (FunctionBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + field_offset = offset + sizeof (StructBlob); + for (i = 0; i < blob->n_fields; i++) + { + FieldBlob *field_blob = (FieldBlob*) &typelib->data[field_offset]; + + if (!validate_field_blob (ctx, + field_offset, + error)) + return FALSE; + + field_offset += sizeof (FieldBlob); + if (field_blob->has_embedded_type) + field_offset += sizeof (CallbackBlob); + } + + for (i = 0; i < blob->n_methods; i++) + { + if (!validate_function_blob (ctx, + field_offset + + i * sizeof (FunctionBlob), + blob_type, + error)) + return FALSE; + } + + pop_context (ctx); + + return TRUE; +} + +static gboolean +validate_enum_blob (ValidateContext *ctx, + guint32 offset, + guint16 blob_type, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + EnumBlob *blob; + gint i; + guint32 offset2; + + if (typelib->len < offset + sizeof (EnumBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (EnumBlob*) &typelib->data[offset]; + + if (blob->blob_type != blob_type) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong blob type"); + return FALSE; + } + + if (!blob->unregistered) + { + if (!validate_name (typelib, "enum", typelib->data, blob->gtype_name, error)) + return FALSE; + + if (!validate_name (typelib, "enum", typelib->data, blob->gtype_init, error)) + return FALSE; + } + else + { + if (blob->gtype_name || blob->gtype_init) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Gtype data in unregistered enum"); + return FALSE; + } + } + + if (!validate_name (typelib, "enum", typelib->data, blob->name, error)) + return FALSE; + + if (typelib->len < offset + sizeof (EnumBlob) + + blob->n_values * sizeof (ValueBlob) + + blob->n_methods * sizeof (FunctionBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + offset2 = offset + sizeof (EnumBlob); + + push_context (ctx, get_string_nofail (typelib, blob->name)); + + for (i = 0; i < blob->n_values; i++, offset2 += sizeof (ValueBlob)) + { + if (!validate_value_blob (typelib, + offset2, + error)) + return FALSE; + +#if 0 + v1 = (ValueBlob *)&typelib->data[offset2]; + for (j = 0; j < i; j++) + { + v2 = (ValueBlob *)&typelib->data[offset2 + + j * sizeof (ValueBlob)]; + + if (v1->value == v2->value) + { + + /* FIXME should this be an error ? */ + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Duplicate enum value"); + return FALSE; + } + } +#endif + } + + for (i = 0; i < blob->n_methods; i++, offset2 += sizeof (FunctionBlob)) + { + if (!validate_function_blob (ctx, offset2, BLOB_TYPE_ENUM, error)) + return FALSE; + } + + pop_context (ctx); + + return TRUE; +} + +static gboolean +validate_object_blob (ValidateContext *ctx, + guint32 offset, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + Header *header; + ObjectBlob *blob; + gint i; + guint32 offset2; + guint16 n_field_callbacks; + + header = (Header *)typelib->data; + + if (typelib->len < offset + sizeof (ObjectBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (ObjectBlob*) &typelib->data[offset]; + + if (blob->blob_type != BLOB_TYPE_OBJECT) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong blob type"); + return FALSE; + } + + if (!validate_name (typelib, "object", typelib->data, blob->gtype_name, error)) + return FALSE; + + if (!validate_name (typelib, "object", typelib->data, blob->gtype_init, error)) + return FALSE; + + if (!validate_name (typelib, "object", typelib->data, blob->name, error)) + return FALSE; + + if (blob->parent > header->n_entries) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid parent index"); + return FALSE; + } + + if (blob->parent != 0) + { + DirEntry *entry; + + entry = get_dir_entry_checked (typelib, blob->parent, error); + if (!entry) + return FALSE; + if (entry->blob_type != BLOB_TYPE_OBJECT && + (entry->local || entry->blob_type != 0)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Parent not object"); + return FALSE; + } + } + + if (blob->gtype_struct != 0) + { + DirEntry *entry; + + entry = get_dir_entry_checked (typelib, blob->gtype_struct, error); + if (!entry) + return FALSE; + if (entry->blob_type != BLOB_TYPE_STRUCT && entry->local) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Class struct invalid type or not local"); + return FALSE; + } + } + + if (typelib->len < offset + sizeof (ObjectBlob) + + (blob->n_interfaces + blob->n_interfaces % 2) * 2 + + blob->n_fields * sizeof (FieldBlob) + + blob->n_properties * sizeof (PropertyBlob) + + blob->n_methods * sizeof (FunctionBlob) + + blob->n_signals * sizeof (SignalBlob) + + blob->n_vfuncs * sizeof (VFuncBlob) + + blob->n_constants * sizeof (ConstantBlob)) + + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + offset2 = offset + sizeof (ObjectBlob); + + for (i = 0; i < blob->n_interfaces; i++, offset2 += 2) + { + guint16 iface; + DirEntry *entry; + + iface = *(guint16*)&typelib->data[offset2]; + if (iface == 0 || iface > header->n_entries) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid interface index"); + return FALSE; + } + + entry = get_dir_entry_checked (typelib, iface, error); + if (!entry) + return FALSE; + + if (entry->blob_type != BLOB_TYPE_INTERFACE && + (entry->local || entry->blob_type != 0)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Not an interface"); + return FALSE; + } + } + + offset2 += 2 * (blob->n_interfaces %2); + + push_context (ctx, get_string_nofail (typelib, blob->name)); + + n_field_callbacks = 0; + for (i = 0; i < blob->n_fields; i++) + { + FieldBlob *field_blob = (FieldBlob*) &typelib->data[offset2]; + + if (!validate_field_blob (ctx, offset2, error)) + return FALSE; + + offset2 += sizeof (FieldBlob); + /* Special case fields which are callbacks. */ + if (field_blob->has_embedded_type) { + offset2 += sizeof (CallbackBlob); + n_field_callbacks++; + } + } + + if (blob->n_field_callbacks != n_field_callbacks) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Incorrect number of field callbacks; expected " + "%" G_GUINT16_FORMAT ", got %" G_GUINT16_FORMAT, + blob->n_field_callbacks, n_field_callbacks); + return FALSE; + } + + for (i = 0; i < blob->n_properties; i++, offset2 += sizeof (PropertyBlob)) + { + if (!validate_property_blob (typelib, offset2, error)) + return FALSE; + } + + for (i = 0; i < blob->n_methods; i++, offset2 += sizeof (FunctionBlob)) + { + if (!validate_function_blob (ctx, offset2, BLOB_TYPE_OBJECT, error)) + return FALSE; + } + + for (i = 0; i < blob->n_signals; i++, offset2 += sizeof (SignalBlob)) + { + if (!validate_signal_blob (typelib, offset2, offset, error)) + return FALSE; + } + + for (i = 0; i < blob->n_vfuncs; i++, offset2 += sizeof (VFuncBlob)) + { + if (!validate_vfunc_blob (typelib, offset2, offset, error)) + return FALSE; + } + + for (i = 0; i < blob->n_constants; i++, offset2 += sizeof (ConstantBlob)) + { + if (!validate_constant_blob (typelib, offset2, error)) + return FALSE; + } + + pop_context (ctx); + + return TRUE; +} + +static gboolean +validate_interface_blob (ValidateContext *ctx, + guint32 offset, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + Header *header; + InterfaceBlob *blob; + gint i; + guint32 offset2; + + header = (Header *)typelib->data; + + if (typelib->len < offset + sizeof (InterfaceBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + blob = (InterfaceBlob*) &typelib->data[offset]; + + if (blob->blob_type != BLOB_TYPE_INTERFACE) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Wrong blob type; expected interface, got %d", blob->blob_type); + return FALSE; + } + + if (!validate_name (typelib, "interface", typelib->data, blob->gtype_name, error)) + return FALSE; + + if (!validate_name (typelib, "interface", typelib->data, blob->gtype_init, error)) + return FALSE; + + if (!validate_name (typelib, "interface", typelib->data, blob->name, error)) + return FALSE; + + if (typelib->len < offset + sizeof (InterfaceBlob) + + (blob->n_prerequisites + blob->n_prerequisites % 2) * 2 + + blob->n_properties * sizeof (PropertyBlob) + + blob->n_methods * sizeof (FunctionBlob) + + blob->n_signals * sizeof (SignalBlob) + + blob->n_vfuncs * sizeof (VFuncBlob) + + blob->n_constants * sizeof (ConstantBlob)) + + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + offset2 = offset + sizeof (InterfaceBlob); + + for (i = 0; i < blob->n_prerequisites; i++, offset2 += 2) + { + DirEntry *entry; + guint16 req; + + req = *(guint16*)&typelib->data[offset2]; + if (req == 0 || req > header->n_entries) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Invalid prerequisite index"); + return FALSE; + } + + entry = g_typelib_get_dir_entry (typelib, req); + if (entry->blob_type != BLOB_TYPE_INTERFACE && + entry->blob_type != BLOB_TYPE_OBJECT && + (entry->local || entry->blob_type != 0)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_BLOB, + "Not an interface or object"); + return FALSE; + } + } + + offset2 += 2 * (blob->n_prerequisites % 2); + + push_context (ctx, get_string_nofail (typelib, blob->name)); + + for (i = 0; i < blob->n_properties; i++, offset2 += sizeof (PropertyBlob)) + { + if (!validate_property_blob (typelib, offset2, error)) + return FALSE; + } + + for (i = 0; i < blob->n_methods; i++, offset2 += sizeof (FunctionBlob)) + { + if (!validate_function_blob (ctx, offset2, BLOB_TYPE_INTERFACE, error)) + return FALSE; + } + + for (i = 0; i < blob->n_signals; i++, offset2 += sizeof (SignalBlob)) + { + if (!validate_signal_blob (typelib, offset2, offset, error)) + return FALSE; + } + + for (i = 0; i < blob->n_vfuncs; i++, offset2 += sizeof (VFuncBlob)) + { + if (!validate_vfunc_blob (typelib, offset2, offset, error)) + return FALSE; + } + + for (i = 0; i < blob->n_constants; i++, offset2 += sizeof (ConstantBlob)) + { + if (!validate_constant_blob (typelib, offset2, error)) + return FALSE; + } + + pop_context (ctx); + + return TRUE; +} + +static gboolean +validate_union_blob (GITypelib *typelib, + guint32 offset, + GError **error) +{ + return TRUE; +} + +static gboolean +validate_blob (ValidateContext *ctx, + guint32 offset, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + CommonBlob *common; + + if (typelib->len < offset + sizeof (CommonBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + common = (CommonBlob*)&typelib->data[offset]; + + switch (common->blob_type) + { + case BLOB_TYPE_FUNCTION: + if (!validate_function_blob (ctx, offset, 0, error)) + return FALSE; + break; + case BLOB_TYPE_CALLBACK: + if (!validate_callback_blob (ctx, offset, error)) + return FALSE; + break; + case BLOB_TYPE_STRUCT: + case BLOB_TYPE_BOXED: + if (!validate_struct_blob (ctx, offset, common->blob_type, error)) + return FALSE; + break; + case BLOB_TYPE_ENUM: + case BLOB_TYPE_FLAGS: + if (!validate_enum_blob (ctx, offset, common->blob_type, error)) + return FALSE; + break; + case BLOB_TYPE_OBJECT: + if (!validate_object_blob (ctx, offset, error)) + return FALSE; + break; + case BLOB_TYPE_INTERFACE: + if (!validate_interface_blob (ctx, offset, error)) + return FALSE; + break; + case BLOB_TYPE_CONSTANT: + if (!validate_constant_blob (typelib, offset, error)) + return FALSE; + break; + case BLOB_TYPE_UNION: + if (!validate_union_blob (typelib, offset, error)) + return FALSE; + break; + default: + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_ENTRY, + "Invalid blob type"); + return FALSE; + } + + return TRUE; +} + +static gboolean +validate_directory (ValidateContext *ctx, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + Header *header = (Header *)typelib->data; + DirEntry *entry; + gint i; + + if (typelib->len < header->directory + header->n_entries * sizeof (DirEntry)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + for (i = 0; i < header->n_entries; i++) + { + entry = g_typelib_get_dir_entry (typelib, i + 1); + + if (!validate_name (typelib, "entry", typelib->data, entry->name, error)) + return FALSE; + + if ((entry->local && entry->blob_type == BLOB_TYPE_INVALID) || + entry->blob_type > BLOB_TYPE_UNION) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_DIRECTORY, + "Invalid entry type"); + return FALSE; + } + + if (i < header->n_local_entries) + { + if (!entry->local) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_DIRECTORY, + "Too few local directory entries"); + return FALSE; + } + + if (!is_aligned (entry->offset)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_DIRECTORY, + "Misaligned entry"); + return FALSE; + } + + if (!validate_blob (ctx, entry->offset, error)) + return FALSE; + } + else + { + if (entry->local) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID_DIRECTORY, + "Too many local directory entries"); + return FALSE; + } + + if (!validate_name (typelib, "namespace", typelib->data, entry->offset, error)) + return FALSE; + } + } + + return TRUE; +} + +static gboolean +validate_attributes (ValidateContext *ctx, + GError **error) +{ + GITypelib *typelib = ctx->typelib; + Header *header = (Header *)typelib->data; + + if (header->size < header->attributes + header->n_attributes * sizeof (AttributeBlob)) + { + g_set_error (error, + G_TYPELIB_ERROR, + G_TYPELIB_ERROR_INVALID, + "The buffer is too short"); + return FALSE; + } + + return TRUE; +} + +static void +prefix_with_context (GError **error, + const char *section, + ValidateContext *ctx) +{ + GString *str; + GSList *link; + char *buf; + + link = ctx->context_stack; + if (!link) + { + g_prefix_error (error, "In %s:", section); + return; + } + + str = g_string_new (NULL); + + for (; link; link = link->next) + { + g_string_append (str, link->data); + if (link->next) + g_string_append_c (str, '/'); + } + g_string_append_c (str, ')'); + buf = g_string_free (str, FALSE); + g_prefix_error (error, "In %s (Context: %s): ", section, buf); + g_free (buf); +} + +/** + * g_typelib_validate: + * @typelib: TODO + * @error: TODO + * + * TODO + * + * Returns: TODO + */ +gboolean +g_typelib_validate (GITypelib *typelib, + GError **error) +{ + ValidateContext ctx; + ctx.typelib = typelib; + ctx.context_stack = NULL; + + if (!validate_header (&ctx, error)) + { + prefix_with_context (error, "In header", &ctx); + return FALSE; + } + + if (!validate_directory (&ctx, error)) + { + prefix_with_context (error, "directory", &ctx); + return FALSE; + } + + if (!validate_attributes (&ctx, error)) + { + prefix_with_context (error, "attributes", &ctx); + return FALSE; + } + + return TRUE; +} + +/** + * g_typelib_error_quark: + * + * TODO + * + * Returns: TODO + */ +GQuark +g_typelib_error_quark (void) +{ + static GQuark quark = 0; + if (quark == 0) + quark = g_quark_from_static_string ("g-typelib-error-quark"); + return quark; +} + +static GSList *library_paths; + +/** + * g_irepository_prepend_library_path: + * @directory: (type filename): a single directory to scan for shared libraries + * + * Prepends @directory to the search path that is used to + * search shared libraries referenced by imported namespaces. + * Multiple calls to this function all contribute to the final + * list of paths. + * The list of paths is unique and shared for all #GIRepository + * instances across the process, but it doesn't affect namespaces + * imported before the call. + * + * If the library is not found in the directories configured + * in this way, loading will fall back to the system library + * path (ie. LD_LIBRARY_PATH and DT_RPATH in ELF systems). + * See the documentation of your dynamic linker for full details. + * + * Since: 1.36 + */ +void +g_irepository_prepend_library_path (const char *directory) +{ + library_paths = g_slist_prepend (library_paths, + g_strdup (directory)); +} + +/* Note on the GModule flags used by this function: + + * Glade's autoconnect feature and OpenGL's extension mechanism + * as used by Clutter rely on g_module_open(NULL) to work as a means of + * accessing the app's symbols. This keeps us from using + * G_MODULE_BIND_LOCAL. BIND_LOCAL may have other issues as well; + * in general libraries are not expecting multiple copies of + * themselves and are not expecting to be unloaded. So we just + * load modules globally for now. + */ +static GModule * +load_one_shared_library (const char *shlib) +{ + GSList *p; + GModule *m; + +#ifdef __APPLE__ + /* On macOS, @-prefixed shlib paths (@rpath, @executable_path, @loader_path) + need to be treated as absolute; trying to combine them with a + configured library path produces a mangled path that is unresolvable + and may cause unintended side effects (such as loading the library + from a fall-back location on macOS 12.0.1). + */ + if (!g_path_is_absolute (shlib) && !g_str_has_prefix (shlib, "@")) +#else + if (!g_path_is_absolute (shlib)) +#endif + { + /* First try in configured library paths */ + for (p = library_paths; p; p = p->next) + { + char *path = g_build_filename (p->data, shlib, NULL); + + m = g_module_open (path, G_MODULE_BIND_LAZY); + + g_free (path); + if (m != NULL) + return m; + } + } + + /* Then try loading from standard paths */ + /* Do not attempt to fix up shlib to replace .la with .so: + it's done by GModule anyway. + */ + return g_module_open (shlib, G_MODULE_BIND_LAZY); +} + +static void +_g_typelib_do_dlopen (GITypelib *typelib) +{ + Header *header; + const char *shlib_str; + + header = (Header *) typelib->data; + /* note that NULL shlib means to open the main app, which is allowed */ + if (header->shared_library) + shlib_str = g_typelib_get_string (typelib, header->shared_library); + else + shlib_str = NULL; + + if (shlib_str != NULL && shlib_str[0] != '\0') + { + gchar **shlibs; + gint i; + + /* shared-library is a comma-separated list of libraries */ + shlibs = g_strsplit (shlib_str, ",", 0); + + /* We load all passed libs unconditionally as if the same library is loaded + * again with g_module_open(), the same file handle will be returned. See bug: + * http://bugzilla.gnome.org/show_bug.cgi?id=555294 + */ + for (i = 0; shlibs[i]; i++) + { + GModule *module; + + module = load_one_shared_library (shlibs[i]); + + if (module == NULL) + { + g_warning ("Failed to load shared library '%s' referenced by the typelib: %s", + shlibs[i], g_module_error ()); + } + else + { + typelib->modules = g_list_append (typelib->modules, module); + } + } + + g_strfreev (shlibs); + } + else + { + /* If there's no shared-library entry for this module, assume that + * the module is for the application. Some of the hand-written .gir files + * in gobject-introspection don't have shared-library entries, but no one + * is really going to be calling g_module_symbol on them either. + */ + GModule *module = g_module_open (NULL, 0); + if (module == NULL) + g_warning ("gtypelib.c: Failed to g_module_open (NULL): %s", g_module_error ()); + else + typelib->modules = g_list_prepend (typelib->modules, module); + } +} + +static inline void +_g_typelib_ensure_open (GITypelib *typelib) +{ + if (typelib->open_attempted) + return; + typelib->open_attempted = TRUE; + _g_typelib_do_dlopen (typelib); +} + +/** + * g_typelib_new_from_memory: (skip) + * @memory: address of memory chunk containing the typelib + * @len: length of memory chunk containing the typelib + * @error: a #GError + * + * Creates a new #GITypelib from a memory location. The memory block + * pointed to by @typelib will be automatically g_free()d when the + * repository is destroyed. + * + * Returns: the new #GITypelib + */ +GITypelib * +g_typelib_new_from_memory (guint8 *memory, + gsize len, + GError **error) +{ + GITypelib *meta; + + if (!validate_header_basic (memory, len, error)) + return NULL; + + meta = g_slice_new0 (GITypelib); + meta->data = memory; + meta->len = len; + meta->owns_memory = TRUE; + meta->modules = NULL; + + return meta; +} + +/** + * g_typelib_new_from_const_memory: (skip) + * @memory: address of memory chunk containing the typelib + * @len: length of memory chunk containing the typelib + * @error: A #GError + * + * Creates a new #GITypelib from a memory location. + * + * Returns: the new #GITypelib + */ +GITypelib * +g_typelib_new_from_const_memory (const guchar *memory, + gsize len, + GError **error) +{ + GITypelib *meta; + + if (!validate_header_basic (memory, len, error)) + return NULL; + + meta = g_slice_new0 (GITypelib); + meta->data = (guchar *) memory; + meta->len = len; + meta->owns_memory = FALSE; + meta->modules = NULL; + + return meta; +} + +/** + * g_typelib_new_from_mapped_file: (skip) + * @mfile: a #GMappedFile, that will be free'd when the repository is destroyed + * @error: a #GError + * + * Creates a new #GITypelib from a #GMappedFile. + * + * Returns: the new #GITypelib + */ +GITypelib * +g_typelib_new_from_mapped_file (GMappedFile *mfile, + GError **error) +{ + GITypelib *meta; + guint8 *data = (guint8 *) g_mapped_file_get_contents (mfile); + gsize len = g_mapped_file_get_length (mfile); + + if (!validate_header_basic (data, len, error)) + return NULL; + + meta = g_slice_new0 (GITypelib); + meta->mfile = mfile; + meta->owns_memory = FALSE; + meta->data = data; + meta->len = len; + + return meta; +} + +/** + * g_typelib_free: + * @typelib: a #GITypelib + * + * Free a #GITypelib. + */ +void +g_typelib_free (GITypelib *typelib) +{ + if (typelib->mfile) + g_mapped_file_unref (typelib->mfile); + else + if (typelib->owns_memory) + g_free (typelib->data); + if (typelib->modules) + { + g_list_foreach (typelib->modules, (GFunc) (void *) g_module_close, NULL); + g_list_free (typelib->modules); + } + g_slice_free (GITypelib, typelib); +} + +/** + * g_typelib_get_namespace: + * @typelib: TODO + * + * TODO + * + * Returns: TODO + */ +const gchar * +g_typelib_get_namespace (GITypelib *typelib) +{ + return g_typelib_get_string (typelib, ((Header *) typelib->data)->namespace); +} + +/** + * g_typelib_symbol: + * @typelib: the typelib + * @symbol_name: name of symbol to be loaded + * @symbol: returns a pointer to the symbol value + * + * Loads a symbol from #GITypelib. + * + * Returns: #TRUE on success + */ +gboolean +g_typelib_symbol (GITypelib *typelib, const char *symbol_name, gpointer *symbol) +{ + GList *l; + + _g_typelib_ensure_open (typelib); + + /* + * The reason for having multiple modules dates from gir-repository + * when it was desired to inject code (accessors, etc.) into an + * existing library. In that situation, the first module listed + * will be the custom one, which overrides the main one. A bit + * inefficient, but the problem will go away when gir-repository + * does. + * + * For modules with no shared library, we dlopen'd the current + * process above. + */ + for (l = typelib->modules; l; l = l->next) + { + GModule *module = l->data; + + if (g_module_symbol (module, symbol_name, symbol)) + return TRUE; + } + + return FALSE; +} diff --git a/girepository/gitypelib.h b/girepository/gitypelib.h new file mode 100644 index 000000000..e6f46bcff --- /dev/null +++ b/girepository/gitypelib.h @@ -0,0 +1,80 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Public typelib API + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GITYPELIB_H__ +#define __GITYPELIB_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +#include + +G_BEGIN_DECLS + +/** + * SECTION:gitypelib + * @title: GITypelib + * @short_description: TODO + * + * TODO + */ + +/** + * GITypelib: + * + * TODO + */ +typedef struct _GITypelib GITypelib; + +GI_AVAILABLE_IN_ALL +GITypelib * g_typelib_new_from_memory (guint8 *memory, + gsize len, + GError **error); + +GI_AVAILABLE_IN_ALL +GITypelib * g_typelib_new_from_const_memory (const guint8 *memory, + gsize len, + GError **error); + +GI_AVAILABLE_IN_ALL +GITypelib * g_typelib_new_from_mapped_file (GMappedFile *mfile, + GError **error); + +GI_AVAILABLE_IN_ALL +void g_typelib_free (GITypelib *typelib); + +GI_AVAILABLE_IN_ALL +gboolean g_typelib_symbol (GITypelib *typelib, + const gchar *symbol_name, + gpointer *symbol); + +GI_AVAILABLE_IN_ALL +const gchar * g_typelib_get_namespace (GITypelib *typelib); + + +G_END_DECLS + +#endif /* __GITYPELIB_H__ */ + diff --git a/girepository/gitypes.h b/girepository/gitypes.h new file mode 100644 index 000000000..9a9cb1e18 --- /dev/null +++ b/girepository/gitypes.h @@ -0,0 +1,516 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: types + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GITYPES_H__ +#define __GITYPES_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +typedef struct _GIBaseInfoStub { + /*< private >*/ + gint32 dummy1; + gint32 dummy2; + gpointer dummy3; + gpointer dummy4; + gpointer dummy5; + guint32 dummy6; + guint32 dummy7; + gpointer padding[4]; +} GIBaseInfo; + +/** + * GICallableInfo: + * + * Represents a callable, either #GIFunctionInfo, #GICallbackInfo or + * #GIVFuncInfo. + */ +typedef GIBaseInfo GICallableInfo; + +/** + * GIFunctionInfo: + * + * Represents a function, eg arguments and return value. + */ +typedef GIBaseInfo GIFunctionInfo; + +/** + * SECTION:gicallbackinfo + * @title: GICallbackInfo + * @short_description: Struct representing a callback + * + * GICallbackInfo represents a callback. + */ + +/** + * GICallbackInfo: + * + * Represents a callback, eg arguments and return value. + */ +typedef GIBaseInfo GICallbackInfo; + +/** + * GIRegisteredTypeInfo: + * + * Represent a registered type. + */ +typedef GIBaseInfo GIRegisteredTypeInfo; + +/** + * GIStructInfo: + * + * Represents a struct. + */ +typedef GIBaseInfo GIStructInfo; + +/** + * GIUnionInfo: + * + * Represents a union. + */ +typedef GIBaseInfo GIUnionInfo; + +/** + * GIEnumInfo: + * + * Represents an enum or a flag. + */ +typedef GIBaseInfo GIEnumInfo; + +/** + * GIObjectInfo: + * + * Represents an object. + */ +typedef GIBaseInfo GIObjectInfo; + +/** + * GIInterfaceInfo: + * + * Represents an interface. + */ +typedef GIBaseInfo GIInterfaceInfo; + +/** + * GIConstantInfo: + * + * Represents a constant. + */ +typedef GIBaseInfo GIConstantInfo; + +/** + * SECTION:givalueinfo + * @title: GIValueInfo + * @short_description: Struct representing a value + * + * GIValueInfo represents a value. + */ + +/** + * GIValueInfo: + * + * Represents a enum value of a #GIEnumInfo. + */ +typedef GIBaseInfo GIValueInfo; + +/** + * GISignalInfo: + * + * Represents a signal. + */ +typedef GIBaseInfo GISignalInfo; + +/** + * GIVFuncInfo: + * + * Represents a virtual function. + */ +typedef GIBaseInfo GIVFuncInfo; + +/** + * GIPropertyInfo: + * + * Represents a property of a #GIObjectInfo or a #GIInterfaceInfo. + */ +typedef GIBaseInfo GIPropertyInfo; + +/** + * GIFieldInfo: + * + * Represents a field of a #GIStructInfo or a #GIUnionInfo. + */ +typedef GIBaseInfo GIFieldInfo; + +/** + * GIArgInfo: + * + * Represents an argument. + */ +typedef GIBaseInfo GIArgInfo; + +/** + * GITypeInfo: + * + * Represents type information, direction, transfer etc. + */ +typedef GIBaseInfo GITypeInfo; + +/** + * GIUnresolvedInfo: + * + * Represents a unresolved type in a typelib. + */ +typedef struct _GIUnresolvedInfo GIUnresolvedInfo; + +union _GIArgument +{ + gboolean v_boolean; + gint8 v_int8; + guint8 v_uint8; + gint16 v_int16; + guint16 v_uint16; + gint32 v_int32; + guint32 v_uint32; + gint64 v_int64; + guint64 v_uint64; + gfloat v_float; + gdouble v_double; + gshort v_short; + gushort v_ushort; + gint v_int; + guint v_uint; + glong v_long; + gulong v_ulong; + gssize v_ssize; + gsize v_size; + gchar * v_string; + gpointer v_pointer; +}; + +/** + * GIArgument: + * @v_boolean: TODO + * @v_int8: TODO + * @v_uint8: TODO + * @v_int16: TODO + * @v_uint16: TODO + * @v_int32: TODO + * @v_uint32: TODO + * @v_int64: TODO + * @v_uint64: TODO + * @v_float: TODO + * @v_double: TODO + * @v_short: TODO + * @v_ushort: TODO + * @v_int: TODO + * @v_uint: TODO + * @v_long: TODO + * @v_ulong: TODO + * @v_ssize: TODO + * @v_size: TODO + * @v_string: TODO + * @v_pointer: TODO + * + * Stores an argument of varying type + */ +typedef union _GIArgument GIArgument; + +/** + * GIInfoType: + * @GI_INFO_TYPE_INVALID: invalid type + * @GI_INFO_TYPE_FUNCTION: function, see #GIFunctionInfo + * @GI_INFO_TYPE_CALLBACK: callback, see #GIFunctionInfo + * @GI_INFO_TYPE_STRUCT: struct, see #GIStructInfo + * @GI_INFO_TYPE_BOXED: boxed, see #GIStructInfo or #GIUnionInfo + * @GI_INFO_TYPE_ENUM: enum, see #GIEnumInfo + * @GI_INFO_TYPE_FLAGS: flags, see #GIEnumInfo + * @GI_INFO_TYPE_OBJECT: object, see #GIObjectInfo + * @GI_INFO_TYPE_INTERFACE: interface, see #GIInterfaceInfo + * @GI_INFO_TYPE_CONSTANT: contant, see #GIConstantInfo + * @GI_INFO_TYPE_INVALID_0: deleted, used to be GI_INFO_TYPE_ERROR_DOMAIN. + * @GI_INFO_TYPE_UNION: union, see #GIUnionInfo + * @GI_INFO_TYPE_VALUE: enum value, see #GIValueInfo + * @GI_INFO_TYPE_SIGNAL: signal, see #GISignalInfo + * @GI_INFO_TYPE_VFUNC: virtual function, see #GIVFuncInfo + * @GI_INFO_TYPE_PROPERTY: GObject property, see #GIPropertyInfo + * @GI_INFO_TYPE_FIELD: struct or union field, see #GIFieldInfo + * @GI_INFO_TYPE_ARG: argument of a function or callback, see #GIArgInfo + * @GI_INFO_TYPE_TYPE: type information, see #GITypeInfo + * @GI_INFO_TYPE_UNRESOLVED: unresolved type, a type which is not present in + * the typelib, or any of its dependencies. + * + * The type of a GIBaseInfo struct. + */ +typedef enum +{ + GI_INFO_TYPE_INVALID, + GI_INFO_TYPE_FUNCTION, + GI_INFO_TYPE_CALLBACK, + GI_INFO_TYPE_STRUCT, + GI_INFO_TYPE_BOXED, + GI_INFO_TYPE_ENUM, /* 5 */ + GI_INFO_TYPE_FLAGS, + GI_INFO_TYPE_OBJECT, + GI_INFO_TYPE_INTERFACE, + GI_INFO_TYPE_CONSTANT, + GI_INFO_TYPE_INVALID_0, /* 10 */ + GI_INFO_TYPE_UNION, + GI_INFO_TYPE_VALUE, + GI_INFO_TYPE_SIGNAL, + GI_INFO_TYPE_VFUNC, + GI_INFO_TYPE_PROPERTY, /* 15 */ + GI_INFO_TYPE_FIELD, + GI_INFO_TYPE_ARG, + GI_INFO_TYPE_TYPE, + GI_INFO_TYPE_UNRESOLVED +} GIInfoType; + +/** + * GITransfer: + * @GI_TRANSFER_NOTHING: transfer nothing from the callee (function or the type + * instance the property belongs to) to the caller. The callee retains the + * ownership of the transfer and the caller doesn't need to do anything to free + * up the resources of this transfer. + * @GI_TRANSFER_CONTAINER: transfer the container (list, array, hash table) from + * the callee to the caller. The callee retains the ownership of the individual + * items in the container and the caller has to free up the container resources + * (g_list_free()/g_hash_table_destroy() etc) of this transfer. + * @GI_TRANSFER_EVERYTHING: transfer everything, eg the container and its + * contents from the callee to the caller. This is the case when the callee + * creates a copy of all the data it returns. The caller is responsible for + * cleaning up the container and item resources of this transfer. + * + * The transfer is the exchange of data between two parts, from the callee to + * the caller. The callee is either a function/method/signal or an + * object/interface where a property is defined. The caller is the side + * accessing a property or calling a function. + * #GITransfer specifies who's responsible for freeing the resources after the + * ownership transfer is complete. In case of a containing type such as a list, + * an array or a hash table the container itself is specified differently from + * the items within the container itself. Each container is freed differently, + * check the documentation for the types themselves for information on how to + * free them. + */ +typedef enum { + GI_TRANSFER_NOTHING, + GI_TRANSFER_CONTAINER, + GI_TRANSFER_EVERYTHING +} GITransfer; + +/** + * GIDirection: + * @GI_DIRECTION_IN: in argument. + * @GI_DIRECTION_OUT: out argument. + * @GI_DIRECTION_INOUT: in and out argument. + * + * The direction of a #GIArgInfo. + */ +typedef enum { + GI_DIRECTION_IN, + GI_DIRECTION_OUT, + GI_DIRECTION_INOUT +} GIDirection; + +/** + * GIScopeType: + * @GI_SCOPE_TYPE_INVALID: The argument is not of callback type. + * @GI_SCOPE_TYPE_CALL: The callback and associated user_data is only + * used during the call to this function. + * @GI_SCOPE_TYPE_ASYNC: The callback and associated user_data is + * only used until the callback is invoked, and the callback. + * is invoked always exactly once. + * @GI_SCOPE_TYPE_NOTIFIED: The callback and associated + * user_data is used until the caller is notfied via the destroy_notify. + * @GI_SCOPE_TYPE_FOREVER: The callback and associated user_data is + * used until the process terminates + * + * Scope type of a #GIArgInfo representing callback, determines how the + * callback is invoked and is used to decided when the invoke structs + * can be freed. + */ +typedef enum { + GI_SCOPE_TYPE_INVALID, + GI_SCOPE_TYPE_CALL, + GI_SCOPE_TYPE_ASYNC, + GI_SCOPE_TYPE_NOTIFIED, + GI_SCOPE_TYPE_FOREVER +} GIScopeType; + +/** + * GITypeTag: + * @GI_TYPE_TAG_VOID: void + * @GI_TYPE_TAG_BOOLEAN: boolean + * @GI_TYPE_TAG_INT8: 8-bit signed integer + * @GI_TYPE_TAG_UINT8: 8-bit unsigned integer + * @GI_TYPE_TAG_INT16: 16-bit signed integer + * @GI_TYPE_TAG_UINT16: 16-bit unsigned integer + * @GI_TYPE_TAG_INT32: 32-bit signed integer + * @GI_TYPE_TAG_UINT32: 32-bit unsigned integer + * @GI_TYPE_TAG_INT64: 64-bit signed integer + * @GI_TYPE_TAG_UINT64: 64-bit unsigned integer + * @GI_TYPE_TAG_FLOAT: float + * @GI_TYPE_TAG_DOUBLE: double floating point + * @GI_TYPE_TAG_GTYPE: a #GType + * @GI_TYPE_TAG_UTF8: a UTF-8 encoded string + * @GI_TYPE_TAG_FILENAME: a filename, encoded in the same encoding + * as the native filesystem is using. + * @GI_TYPE_TAG_ARRAY: an array + * @GI_TYPE_TAG_INTERFACE: an extended interface object + * @GI_TYPE_TAG_GLIST: a #GList + * @GI_TYPE_TAG_GSLIST: a #GSList + * @GI_TYPE_TAG_GHASH: a #GHashTable + * @GI_TYPE_TAG_ERROR: a #GError + * @GI_TYPE_TAG_UNICHAR: Unicode character + * + * The type tag of a #GITypeInfo. + */ +typedef enum { + /* Basic types */ + GI_TYPE_TAG_VOID = 0, + GI_TYPE_TAG_BOOLEAN = 1, + GI_TYPE_TAG_INT8 = 2, /* Start of GI_TYPE_TAG_IS_NUMERIC types */ + GI_TYPE_TAG_UINT8 = 3, + GI_TYPE_TAG_INT16 = 4, + GI_TYPE_TAG_UINT16 = 5, + GI_TYPE_TAG_INT32 = 6, + GI_TYPE_TAG_UINT32 = 7, + GI_TYPE_TAG_INT64 = 8, + GI_TYPE_TAG_UINT64 = 9, + GI_TYPE_TAG_FLOAT = 10, + GI_TYPE_TAG_DOUBLE = 11, /* End of numeric types */ + GI_TYPE_TAG_GTYPE = 12, + GI_TYPE_TAG_UTF8 = 13, + GI_TYPE_TAG_FILENAME = 14, + /* Non-basic types; compare with GI_TYPE_TAG_IS_BASIC */ + GI_TYPE_TAG_ARRAY = 15, /* container (see GI_TYPE_TAG_IS_CONTAINER) */ + GI_TYPE_TAG_INTERFACE = 16, + GI_TYPE_TAG_GLIST = 17, /* container */ + GI_TYPE_TAG_GSLIST = 18, /* container */ + GI_TYPE_TAG_GHASH = 19, /* container */ + GI_TYPE_TAG_ERROR = 20, + /* Another basic type */ + GI_TYPE_TAG_UNICHAR = 21 + /* Note - there is currently only room for 32 tags */ +} GITypeTag; + +/** + * GI_TYPE_TAG_N_TYPES: + * + * TODO + */ +#define GI_TYPE_TAG_N_TYPES (GI_TYPE_TAG_UNICHAR+1) + +#ifndef __GTK_DOC_IGNORE__ +/* These were removed and no longer appear in the typelib; + * instead, the machine-specific versions like INT32 are + * always used. + */ +#define GI_TYPE_TAG_SHORT GI_TYPE_TAG_SHORT_WAS_REMOVED +#define GI_TYPE_TAG_INT GI_TYPE_TAG_INT_WAS_REMOVED +#define GI_TYPE_TAG_LONG GI_TYPE_TAG_LONG_WAS_REMOVED +#endif + +/** + * GIArrayType: + * @GI_ARRAY_TYPE_C: a C array, char[] for instance + * @GI_ARRAY_TYPE_ARRAY: a @GArray array + * @GI_ARRAY_TYPE_PTR_ARRAY: a #GPtrArray array + * @GI_ARRAY_TYPE_BYTE_ARRAY: a #GByteArray array + * + * The type of array in a #GITypeInfo. + */ +typedef enum { + GI_ARRAY_TYPE_C, + GI_ARRAY_TYPE_ARRAY, + GI_ARRAY_TYPE_PTR_ARRAY, + GI_ARRAY_TYPE_BYTE_ARRAY +} GIArrayType; + +/** + * GIFieldInfoFlags: + * @GI_FIELD_IS_READABLE: field is readable. + * @GI_FIELD_IS_WRITABLE: field is writable. + * + * Flags for a #GIFieldInfo. + */ + +typedef enum +{ + GI_FIELD_IS_READABLE = 1 << 0, + GI_FIELD_IS_WRITABLE = 1 << 1 +} GIFieldInfoFlags; + +/** + * GIVFuncInfoFlags: + * @GI_VFUNC_MUST_CHAIN_UP: chains up to the parent type + * @GI_VFUNC_MUST_OVERRIDE: overrides + * @GI_VFUNC_MUST_NOT_OVERRIDE: does not override + * @GI_VFUNC_THROWS: Includes a #GError + * + * Flags of a #GIVFuncInfo struct. + */ +typedef enum +{ + GI_VFUNC_MUST_CHAIN_UP = 1 << 0, + GI_VFUNC_MUST_OVERRIDE = 1 << 1, + GI_VFUNC_MUST_NOT_OVERRIDE = 1 << 2, + GI_VFUNC_THROWS = 1 << 3 +} GIVFuncInfoFlags; + +/** + * GIFunctionInfoFlags: + * @GI_FUNCTION_IS_METHOD: is a method. + * @GI_FUNCTION_IS_CONSTRUCTOR: is a constructor. + * @GI_FUNCTION_IS_GETTER: is a getter of a #GIPropertyInfo. + * @GI_FUNCTION_IS_SETTER: is a setter of a #GIPropertyInfo. + * @GI_FUNCTION_WRAPS_VFUNC: represents a virtual function. + * @GI_FUNCTION_THROWS: the function may throw an error. + * + * Flags for a #GIFunctionInfo struct. + */ +typedef enum +{ + GI_FUNCTION_IS_METHOD = 1 << 0, + GI_FUNCTION_IS_CONSTRUCTOR = 1 << 1, + GI_FUNCTION_IS_GETTER = 1 << 2, + GI_FUNCTION_IS_SETTER = 1 << 3, + GI_FUNCTION_WRAPS_VFUNC = 1 << 4, + GI_FUNCTION_THROWS = 1 << 5 +} GIFunctionInfoFlags; + +#ifndef __GI_SCANNER__ +#ifndef __GTK_DOC_IGNORE__ +/* backwards compatibility */ +typedef GIArgument GArgument; +typedef struct _GITypelib GTypelib; +#endif +#endif + +G_END_DECLS + +#endif /* __GITYPES_H__ */ + diff --git a/girepository/giunioninfo.c b/girepository/giunioninfo.c new file mode 100644 index 000000000..32a2e3072 --- /dev/null +++ b/girepository/giunioninfo.c @@ -0,0 +1,323 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Union implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" + +/** + * SECTION:giunioninfo + * @title: GIUnionInfo + * @short_description: Struct representing a union. + * + * GIUnionInfo represents a union type. + * + * A union has methods and fields. Unions can optionally have a + * discriminator, which is a field deciding what type of real union + * fields is valid for specified instance. + */ + +/** + * g_union_info_get_n_fields: + * @info: a #GIUnionInfo + * + * Obtain the number of fields this union has. + * + * Returns: number of fields + */ +gint +g_union_info_get_n_fields (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_fields; +} + +/** + * g_union_info_get_field: + * @info: a #GIUnionInfo + * @n: a field index + * + * Obtain the type information for field with specified index. + * + * Returns: (transfer full): the #GIFieldInfo, free it with g_base_info_unref() + * when done. + */ +GIFieldInfo * +g_union_info_get_field (GIUnionInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + + return (GIFieldInfo *) g_info_new (GI_INFO_TYPE_FIELD, (GIBaseInfo*)info, rinfo->typelib, + rinfo->offset + header->union_blob_size + + n * header->field_blob_size); +} + +/** + * g_union_info_get_n_methods: + * @info: a #GIUnionInfo + * + * Obtain the number of methods this union has. + * + * Returns: number of methods + */ +gint +g_union_info_get_n_methods (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->n_functions; +} + +/** + * g_union_info_get_method: + * @info: a #GIUnionInfo + * @n: a method index + * + * Obtain the type information for method with specified index. + * + * Returns: (transfer full): the #GIFunctionInfo, free it with g_base_info_unref() + * when done. + */ +GIFunctionInfo * +g_union_info_get_method (GIUnionInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + Header *header = (Header *)rinfo->typelib->data; + gint offset; + + offset = rinfo->offset + header->union_blob_size + + blob->n_fields * header->field_blob_size + + n * header->function_blob_size; + return (GIFunctionInfo *) g_info_new (GI_INFO_TYPE_FUNCTION, (GIBaseInfo*)info, + rinfo->typelib, offset); +} + +/** + * g_union_info_is_discriminated: + * @info: a #GIUnionInfo + * + * Return true if this union contains discriminator field. + * + * Returns: %TRUE if this is a discriminated union, %FALSE otherwise + */ +gboolean +g_union_info_is_discriminated (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->discriminated; +} + +/** + * g_union_info_get_discriminator_offset: + * @info: a #GIUnionInfo + * + * Returns offset of the discriminator field in the structure. + * + * Returns: offset in bytes of the discriminator + */ +gint +g_union_info_get_discriminator_offset (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->discriminator_offset; +} + +/** + * g_union_info_get_discriminator_type: + * @info: a #GIUnionInfo + * + * Obtain the type information of the union discriminator. + * + * Returns: (transfer full): the #GITypeInfo, free it with g_base_info_unref() + * when done. + */ +GITypeInfo * +g_union_info_get_discriminator_type (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + + return _g_type_info_new ((GIBaseInfo*)info, rinfo->typelib, rinfo->offset + 24); +} + +/** + * g_union_info_get_discriminator: + * @info: a #GIUnionInfo + * @n: a union field index + * + * Obtain discriminator value assigned for n-th union field, i.e. n-th + * union field is the active one if discriminator contains this + * constant. + * + * Returns: (transfer full): the #GIConstantInfo, free it with g_base_info_unref() + * when done. + */ +GIConstantInfo * +g_union_info_get_discriminator (GIUnionInfo *info, + gint n) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->discriminated) + { + Header *header = (Header *)rinfo->typelib->data; + gint offset; + + offset = rinfo->offset + header->union_blob_size + + blob->n_fields * header->field_blob_size + + blob->n_functions * header->function_blob_size + + n * header->constant_blob_size; + + return (GIConstantInfo *) g_info_new (GI_INFO_TYPE_CONSTANT, (GIBaseInfo*)info, + rinfo->typelib, offset); + } + + return NULL; +} + +/** + * g_union_info_find_method: + * @info: a #GIUnionInfo + * @name: a method name + * + * Obtain the type information for method named @name. + * + * Returns: (transfer full): the #GIFunctionInfo, free it with g_base_info_unref() + * when done. + */ +GIFunctionInfo * +g_union_info_find_method (GIUnionInfo *info, + const gchar *name) +{ + gint offset; + GIRealInfo *rinfo = (GIRealInfo *)info; + Header *header = (Header *)rinfo->typelib->data; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + offset = rinfo->offset + header->union_blob_size + + blob->n_fields * header->field_blob_size; + + return _g_base_info_find_method ((GIBaseInfo*)info, offset, blob->n_functions, name); +} + +/** + * g_union_info_get_size: + * @info: a #GIUnionInfo + * + * Obtain the total size of the union. + * + * Returns: size of the union in bytes + */ +gsize +g_union_info_get_size (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->size; +} + +/** + * g_union_info_get_alignment: + * @info: a #GIUnionInfo + * + * Obtain the required alignment of the union. + * + * Returns: required alignment in bytes + */ +gsize +g_union_info_get_alignment (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->alignment; +} + +/** + * g_union_info_get_copy_function: + * @info: a union information blob + * + * Retrieves the name of the copy function for @info, if any is set. + * + * Returns: (transfer none) (nullable): the name of the copy function + * + * Since: 1.76 + */ +const char * +g_union_info_get_copy_function (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_UNION_INFO (info), NULL); + + blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->copy_func) + return g_typelib_get_string (rinfo->typelib, blob->copy_func); + + return NULL; +} + +/** + * g_union_info_get_free_function: + * @info: a union information blob + * + * Retrieves the name of the free function for @info, if any is set. + * + * Returns: (transfer none) (nullable): the name of the free function + * + * Since: 1.76 + */ +const char * +g_union_info_get_free_function (GIUnionInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + UnionBlob *blob; + + g_return_val_if_fail (info != NULL, NULL); + g_return_val_if_fail (GI_IS_UNION_INFO (info), NULL); + + blob = (UnionBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->free_func) + return g_typelib_get_string (rinfo->typelib, blob->free_func); + + return NULL; +} diff --git a/girepository/giunioninfo.h b/girepository/giunioninfo.h new file mode 100644 index 000000000..4087ed4dc --- /dev/null +++ b/girepository/giunioninfo.h @@ -0,0 +1,90 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Union + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIUNIONINFO_H__ +#define __GIUNIONINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_UNION_INFO + * @info: an info structure + * + * Checks if @info is a #GIUnionInfo. + */ +#define GI_IS_UNION_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_UNION) + +GI_AVAILABLE_IN_ALL +gint g_union_info_get_n_fields (GIUnionInfo *info); + +GI_AVAILABLE_IN_ALL +GIFieldInfo * g_union_info_get_field (GIUnionInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gint g_union_info_get_n_methods (GIUnionInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_union_info_get_method (GIUnionInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +gboolean g_union_info_is_discriminated (GIUnionInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_union_info_get_discriminator_offset (GIUnionInfo *info); + +GI_AVAILABLE_IN_ALL +GITypeInfo * g_union_info_get_discriminator_type (GIUnionInfo *info); + +GI_AVAILABLE_IN_ALL +GIConstantInfo * g_union_info_get_discriminator (GIUnionInfo *info, + gint n); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_union_info_find_method (GIUnionInfo *info, + const gchar *name); + +GI_AVAILABLE_IN_ALL +gsize g_union_info_get_size (GIUnionInfo *info); + +GI_AVAILABLE_IN_ALL +gsize g_union_info_get_alignment (GIUnionInfo *info); + +GI_AVAILABLE_IN_1_76 +const char * g_union_info_get_copy_function (GIUnionInfo *info); + +GI_AVAILABLE_IN_1_76 +const char * g_union_info_get_free_function (GIUnionInfo *info); + +G_END_DECLS + + +#endif /* __GIUNIONINFO_H__ */ + diff --git a/girepository/giversion.c b/girepository/giversion.c new file mode 100644 index 000000000..927190fb4 --- /dev/null +++ b/girepository/giversion.c @@ -0,0 +1,114 @@ +/* Copyright (C) 2018 Christoph Reiter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +/** + * SECTION:giversion + * @Title: Version Information + * @Short_description: macros and functions to check the girepository version + */ + +/** + * GI_MAJOR_VERSION: + * + * The major version number of the girepository library. + * + * Since: 1.60 + */ + +/** + * GI_MINOR_VERSION: + * + * The minor version number of the girepository library. + * + * Since: 1.60 + */ + +/** + * GI_MICRO_VERSION: + * + * The micro version number of the girepository library. + * + * Since: 1.60 + */ + +/** + * GI_CHECK_VERSION: + * @major: the major version to check for + * @minor: the minor version to check for + * @micro: the micro version to check for + * + * Checks the version of the girepository library that is being compiled + * against. + * + * Returns: %TRUE if the version of the girepository header files + * is the same as or newer than the passed-in version. + * + * Since: 1.60 + */ + +/** + * gi_get_major_version: + * + * Returns the major version number of the girepository library. + * (e.g. in version 1.58.2 this is 1.) + * + * Returns: the major version number of the girepository library + * + * Since: 1.60 + */ +guint +gi_get_major_version (void) +{ + return GI_MAJOR_VERSION; +} + +/** + * gi_get_minor_version: + * + * Returns the minor version number of the girepository library. + * (e.g. in version 1.58.2 this is 58.) + * + * Returns: the minor version number of the girepository library + * + * Since: 1.60 + */ +guint +gi_get_minor_version (void) +{ + return GI_MINOR_VERSION; +} + +/** + * gi_get_micro_version: + * + * Returns the micro version number of the girepository library. + * (e.g. in version 1.58.2 this is 2.) + * + * Returns: the micro version number of the girepository library + * + * Since: 1.60 + */ +guint +gi_get_micro_version (void) +{ + return GI_MICRO_VERSION; +} diff --git a/girepository/giversion.h.in b/girepository/giversion.h.in new file mode 100644 index 000000000..177d33d19 --- /dev/null +++ b/girepository/giversion.h.in @@ -0,0 +1,47 @@ +/* Copyright (C) 2018 Christoph Reiter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIVERISON_H__ +#define __GIVERISON_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +G_BEGIN_DECLS + +#define GI_MAJOR_VERSION @GI_MAJOR_VERSION@ +#define GI_MINOR_VERSION @GI_MINOR_VERSION@ +#define GI_MICRO_VERSION @GI_MICRO_VERSION@ + +#define GI_CHECK_VERSION(major,minor,micro) \ + (GI_MAJOR_VERSION > (major) || \ + (GI_MAJOR_VERSION == (major) && GI_MINOR_VERSION > (minor)) || \ + (GI_MAJOR_VERSION == (major) && GI_MINOR_VERSION == (minor) && \ + GI_MICRO_VERSION >= (micro))) + +GI_AVAILABLE_IN_1_60 +guint gi_get_major_version (void) G_GNUC_CONST; +GI_AVAILABLE_IN_1_60 +guint gi_get_minor_version (void) G_GNUC_CONST; +GI_AVAILABLE_IN_1_60 +guint gi_get_micro_version (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __GIVERISON_H__ */ diff --git a/girepository/giversionmacros.h b/girepository/giversionmacros.h new file mode 100644 index 000000000..372889e4c --- /dev/null +++ b/girepository/giversionmacros.h @@ -0,0 +1,226 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Versioning and export macros + * + * Copyright (C) 2014 Chun-wei Fan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#ifndef __GIVERSIONMACROS_H__ +#define __GIVERSIONMACROS_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef _GI_EXTERN +#define _GI_EXTERN extern +#endif + +#ifndef GI_DISABLE_DEPRECATION_WARNINGS +# define GI_DEPRECATED G_DEPRECATED _GI_EXTERN +# define GI_DEPRECATED_FOR(f) G_DEPRECATED_FOR(f) _GI_EXTERN +# define GI_UNAVAILABLE(maj,min) G_UNAVAILABLE(maj, min) _GI_EXTERN +#else +# define GI_DEPRECATED _GI_EXTERN +# define GI_DEPRECATED_FOR(f) _GI_EXTERN +# define GI_UNAVAILABLE(maj,min) _GI_EXTERN +#endif + +#define GI_AVAILABLE_IN_ALL _GI_EXTERN + +/* XXX: Every new stable minor release should add a set of macros here + * + * We are using the GLib versions here as the G-I minor versions + * need to be in sync with the GLib minor versions. Api's added + * at or before 1.30 are marked as GI_AVAILABLE_IN_ALL + */ + +#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_32 +# define GI_DEPRECATED_IN_1_32 GI_DEPRECATED +# define GI_DEPRECATED_IN_1_32_FOR(f) GI_DEPRECATED_FOR(f) +#else +# define GI_DEPRECATED_IN_1_32 _GI_EXTERN +# define GI_DEPRECATED_IN_1_32_FOR(f) _GI_EXTERN +#endif + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_32 +# define GI_AVAILABLE_IN_1_32 GI_UNAVAILABLE(2, 32) +#else +# define GI_AVAILABLE_IN_1_32 _GI_EXTERN +#endif + +#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_34 +# define GI_DEPRECATED_IN_1_34 GI_DEPRECATED +# define GI_DEPRECATED_IN_1_34_FOR(f) GI_DEPRECATED_FOR(f) +#else +# define GI_DEPRECATED_IN_1_34 _GI_EXTERN +# define GI_DEPRECATED_IN_1_34_FOR(f) _GI_EXTERN +#endif + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_34 +# define GI_AVAILABLE_IN_1_34 GI_UNAVAILABLE(2, 34) +#else +# define GI_AVAILABLE_IN_1_34 _GI_EXTERN +#endif + +#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_36 +# define GI_DEPRECATED_IN_1_36 GI_DEPRECATED +# define GI_DEPRECATED_IN_1_36_FOR(f) GI_DEPRECATED_FOR(f) +#else +# define GI_DEPRECATED_IN_1_36 _GI_EXTERN +# define GI_DEPRECATED_IN_1_36_FOR(f) _GI_EXTERN +#endif + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_36 +# define GI_AVAILABLE_IN_1_36 GI_UNAVAILABLE(2, 36) +#else +# define GI_AVAILABLE_IN_1_36 _GI_EXTERN +#endif + +#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_38 +# define GI_DEPRECATED_IN_1_38 GI_DEPRECATED +# define GI_DEPRECATED_IN_1_38_FOR(f) GI_DEPRECATED_FOR(f) +#else +# define GI_DEPRECATED_IN_1_38 _GI_EXTERN +# define GI_DEPRECATED_IN_1_38_FOR(f) _GI_EXTERN +#endif + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_38 +# define GI_AVAILABLE_IN_1_38 GI_UNAVAILABLE(2, 38) +#else +# define GI_AVAILABLE_IN_1_38 _GI_EXTERN +#endif + +#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_40 +# define GI_DEPRECATED_IN_1_40 GI_DEPRECATED +# define GI_DEPRECATED_IN_1_40_FOR(f) GI_DEPRECATED_FOR(f) +#else +# define GI_DEPRECATED_IN_1_40 _GI_EXTERN +# define GI_DEPRECATED_IN_1_40_FOR(f) _GI_EXTERN +#endif + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_40 +# define GI_AVAILABLE_IN_1_40 GI_UNAVAILABLE(2, 40) +#else +# define GI_AVAILABLE_IN_1_40 _GI_EXTERN +#endif + +#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_42 +# define GI_DEPRECATED_IN_1_42 GI_DEPRECATED +# define GI_DEPRECATED_IN_1_42_FOR(f) GI_DEPRECATED_FOR(f) +#else +# define GI_DEPRECATED_IN_1_42 _GI_EXTERN +# define GI_DEPRECATED_IN_1_42_FOR(f) _GI_EXTERN +#endif + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_42 +# define GI_AVAILABLE_IN_1_42 GI_UNAVAILABLE(2, 42) +#else +# define GI_AVAILABLE_IN_1_42 _GI_EXTERN +#endif + +#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_44 +# define GI_DEPRECATED_IN_1_44 GI_DEPRECATED +# define GI_DEPRECATED_IN_1_44_FOR(f) GI_DEPRECATED_FOR(f) +#else +# define GI_DEPRECATED_IN_1_44 _GI_EXTERN +# define GI_DEPRECATED_IN_1_44_FOR(f) _GI_EXTERN +#endif + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_44 +# define GI_AVAILABLE_IN_1_44 GI_UNAVAILABLE(2, 44) +#else +# define GI_AVAILABLE_IN_1_44 _GI_EXTERN +#endif + +#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_46 +# define GI_DEPRECATED_IN_1_46 GI_DEPRECATED +# define GI_DEPRECATED_IN_1_46_FOR(f) GI_DEPRECATED_FOR(f) +#else +# define GI_DEPRECATED_IN_1_46 _GI_EXTERN +# define GI_DEPRECATED_IN_1_46_FOR(f) _GI_EXTERN +#endif + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_46 +# define GI_AVAILABLE_IN_1_46 GI_UNAVAILABLE(2, 46) +#else +# define GI_AVAILABLE_IN_1_46 _GI_EXTERN +#endif + +#if defined(GLIB_VERSION_2_60) && GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_60 +# define GI_AVAILABLE_IN_1_60 GI_UNAVAILABLE(2, 60) +#else +# define GI_AVAILABLE_IN_1_60 _GI_EXTERN +#endif + +#if defined(GLIB_VERSION_2_66) && GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_66 +# define GI_AVAILABLE_IN_1_66 GI_UNAVAILABLE(2, 66) +#else +# define GI_AVAILABLE_IN_1_66 _GI_EXTERN +#endif + +#if defined(GLIB_VERSION_2_70) && GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_70 +# define GI_AVAILABLE_IN_1_70 GI_UNAVAILABLE(2, 70) +#else +# define GI_AVAILABLE_IN_1_70 _GI_EXTERN +#endif + +#if defined(GLIB_VERSION_2_72) && GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_72 +# define GI_DEPRECATED_IN_1_72 GI_DEPRECATED +# define GI_DEPRECATED_IN_1_72_FOR(f) GI_DEPRECATED_FOR(f) +#else +# define GI_DEPRECATED_IN_1_72 _GI_EXTERN +# define GI_DEPRECATED_IN_1_72_FOR(f) _GI_EXTERN +#endif + +#if defined(GLIB_VERSION_2_72) && GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_72 +# define GI_AVAILABLE_IN_1_72 GI_UNAVAILABLE(2, 72) +#else +# define GI_AVAILABLE_IN_1_72 _GI_EXTERN +#endif + +#if defined(GLIB_VERSION_2_76) && GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_76 +# define GI_DEPRECATED_IN_1_76 GI_DEPRECATED +# define GI_DEPRECATED_IN_1_76_FOR(f) GI_DEPRECATED_FOR(f) +#else +# define GI_DEPRECATED_IN_1_76 _GI_EXTERN +# define GI_DEPRECATED_IN_1_76_FOR(f) _GI_EXTERN +#endif + +#if defined(GLIB_VERSION_2_76) && GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_76 +# define GI_AVAILABLE_IN_1_76 GI_UNAVAILABLE(2, 76) +#else +# define GI_AVAILABLE_IN_1_76 _GI_EXTERN +#endif + +#if defined(GLIB_VERSION_2_78) && GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_78 +# define GI_DEPRECATED_IN_1_78 GI_DEPRECATED +# define GI_DEPRECATED_IN_1_78_FOR(f) GI_DEPRECATED_FOR(f) +#else +# define GI_DEPRECATED_IN_1_78 _GI_EXTERN +# define GI_DEPRECATED_IN_1_78_FOR(f) _GI_EXTERN +#endif + +#if defined(GLIB_VERSION_2_78) && GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_78 +# define GI_AVAILABLE_IN_1_78 GI_UNAVAILABLE(2, 78) +#else +# define GI_AVAILABLE_IN_1_78 _GI_EXTERN +#endif + +#endif /* __GIVERSIONMACROS_H__ */ diff --git a/girepository/givfuncinfo.c b/girepository/givfuncinfo.c new file mode 100644 index 000000000..5c3997c5e --- /dev/null +++ b/girepository/givfuncinfo.c @@ -0,0 +1,345 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Virtual Function implementation + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include + +#include + +#include +#include "girepository-private.h" +#include "gitypelib-internal.h" + +/** + * SECTION:givfuncinfo + * @title: GIVFuncInfo + * @short_description: Struct representing a virtual function + * + * GIVfuncInfo represents a virtual function. + * + * A virtual function is a callable object that belongs to either a + * #GIObjectInfo or a #GIInterfaceInfo. + */ + +GIVFuncInfo * +_g_base_info_find_vfunc (GIRealInfo *rinfo, + guint32 offset, + gint n_vfuncs, + const gchar *name) +{ + /* FIXME hash */ + Header *header = (Header *)rinfo->typelib->data; + gint i; + + for (i = 0; i < n_vfuncs; i++) + { + VFuncBlob *fblob = (VFuncBlob *)&rinfo->typelib->data[offset]; + const gchar *fname = (const gchar *)&rinfo->typelib->data[fblob->name]; + + if (strcmp (name, fname) == 0) + return (GIVFuncInfo *) g_info_new (GI_INFO_TYPE_VFUNC, (GIBaseInfo*) rinfo, + rinfo->typelib, offset); + + offset += header->vfunc_blob_size; + } + + return NULL; +} + +/** + * g_vfunc_info_get_flags: + * @info: a #GIVFuncInfo + * + * Obtain the flags for this virtual function info. See #GIVFuncInfoFlags for + * more information about possible flag values. + * + * Returns: the flags + */ +GIVFuncInfoFlags +g_vfunc_info_get_flags (GIVFuncInfo *info) +{ + GIVFuncInfoFlags flags; + GIRealInfo *rinfo = (GIRealInfo *)info; + VFuncBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_VFUNC_INFO (info), 0); + + blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset]; + + flags = 0; + + if (blob->must_chain_up) + flags = flags | GI_VFUNC_MUST_CHAIN_UP; + + if (blob->must_be_implemented) + flags = flags | GI_VFUNC_MUST_OVERRIDE; + + if (blob->must_not_be_implemented) + flags = flags | GI_VFUNC_MUST_NOT_OVERRIDE; + + if (blob->throws) + flags = flags | GI_VFUNC_THROWS; + + return flags; +} + +/** + * g_vfunc_info_get_offset: + * @info: a #GIVFuncInfo + * + * Obtain the offset of the function pointer in the class struct. The value + * 0xFFFF indicates that the struct offset is unknown. + * + * Returns: the struct offset or 0xFFFF if it's unknown + */ +gint +g_vfunc_info_get_offset (GIVFuncInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + VFuncBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_VFUNC_INFO (info), 0); + + blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset]; + + return blob->struct_offset; +} + +/** + * g_vfunc_info_get_signal: + * @info: a #GIVFuncInfo + * + * Obtain the signal for the virtual function if one is set. + * The signal comes from the object or interface to which + * this virtual function belongs. + * + * Returns: (transfer full): the signal or %NULL if none set + */ +GISignalInfo * +g_vfunc_info_get_signal (GIVFuncInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + VFuncBlob *blob; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_VFUNC_INFO (info), 0); + + blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset]; + + if (blob->class_closure) + return g_interface_info_get_signal ((GIInterfaceInfo *)rinfo->container, blob->signal); + + return NULL; +} + +/** + * g_vfunc_info_get_invoker: + * @info: a #GIVFuncInfo + * + * If this virtual function has an associated invoker method, this + * method will return it. An invoker method is a C entry point. + * + * Not all virtuals will have invokers. + * + * Returns: (transfer full): the #GIVFuncInfo or %NULL. Free it with + * g_base_info_unref() when done. + */ +GIFunctionInfo * +g_vfunc_info_get_invoker (GIVFuncInfo *info) +{ + GIRealInfo *rinfo = (GIRealInfo *)info; + VFuncBlob *blob; + GIBaseInfo *container; + GIInfoType parent_type; + + g_return_val_if_fail (info != NULL, 0); + g_return_val_if_fail (GI_IS_VFUNC_INFO (info), 0); + + blob = (VFuncBlob *)&rinfo->typelib->data[rinfo->offset]; + + /* 1023 = 0x3ff is the maximum of the 10 bits for invoker index */ + if (blob->invoker == 1023) + return NULL; + + container = rinfo->container; + parent_type = g_base_info_get_type (container); + if (parent_type == GI_INFO_TYPE_OBJECT) + return g_object_info_get_method ((GIObjectInfo*)container, blob->invoker); + else if (parent_type == GI_INFO_TYPE_INTERFACE) + return g_interface_info_get_method ((GIInterfaceInfo*)container, blob->invoker); + else + g_assert_not_reached (); +} + +/** + * g_vfunc_info_get_address: + * @info: a #GIVFuncInfo + * @implementor_gtype: #GType implementing this virtual function + * @error: return location for a #GError + * + * This method will look up where inside the type struct of @implementor_gtype + * is the implementation for @info. + * + * Returns: address to a function or %NULL if an error happened + */ +gpointer +g_vfunc_info_get_address (GIVFuncInfo *vfunc_info, + GType implementor_gtype, + GError **error) +{ + GIBaseInfo *container_info; + GIInterfaceInfo *interface_info; + GIObjectInfo *object_info; + GIStructInfo *struct_info; + GIFieldInfo *field_info = NULL; + int length, i, offset; + gpointer implementor_class, implementor_vtable; + gpointer func = NULL; + + container_info = g_base_info_get_container (vfunc_info); + if (g_base_info_get_type (container_info) == GI_INFO_TYPE_OBJECT) + { + object_info = (GIObjectInfo*) container_info; + interface_info = NULL; + struct_info = g_object_info_get_class_struct (object_info); + } + else + { + interface_info = (GIInterfaceInfo*) container_info; + object_info = NULL; + struct_info = g_interface_info_get_iface_struct (interface_info); + } + + length = g_struct_info_get_n_fields (struct_info); + for (i = 0; i < length; i++) + { + field_info = g_struct_info_get_field (struct_info, i); + + if (strcmp (g_base_info_get_name ( (GIBaseInfo*) field_info), + g_base_info_get_name ( (GIBaseInfo*) vfunc_info)) != 0) { + g_base_info_unref (field_info); + field_info = NULL; + continue; + } + + break; + } + + if (field_info == NULL) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_SYMBOL_NOT_FOUND, + "Couldn't find struct field for this vfunc"); + goto out; + } + + implementor_class = g_type_class_ref (implementor_gtype); + + if (object_info) + { + implementor_vtable = implementor_class; + } + else + { + GType interface_type; + + interface_type = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo*) interface_info); + implementor_vtable = g_type_interface_peek (implementor_class, interface_type); + } + + offset = g_field_info_get_offset (field_info); + func = *(gpointer*) G_STRUCT_MEMBER_P (implementor_vtable, offset); + g_type_class_unref (implementor_class); + g_base_info_unref (field_info); + + if (func == NULL) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_SYMBOL_NOT_FOUND, + "Class %s doesn't implement %s", + g_type_name (implementor_gtype), + g_base_info_get_name ( (GIBaseInfo*) vfunc_info)); + goto out; + } + + out: + g_base_info_unref ((GIBaseInfo*) struct_info); + + return func; +} + +/** + * g_vfunc_info_invoke: (skip) + * @info: a #GIVFuncInfo describing the virtual function to invoke + * @implementor: #GType of the type that implements this virtual function + * @in_args: (array length=n_in_args): an array of #GIArguments, one for each in + * parameter of @info. If there are no in parameter, @in_args + * can be %NULL + * @n_in_args: the length of the @in_args array + * @out_args: (array length=n_out_args): an array of #GIArguments, one for each out + * parameter of @info. If there are no out parameters, @out_args + * may be %NULL + * @n_out_args: the length of the @out_args array + * @return_value: return location for the return value of the + * function. If the function returns void, @return_value may be + * %NULL + * @error: return location for detailed error information, or %NULL + * + * Invokes the function described in @info with the given + * arguments. Note that inout parameters must appear in both + * argument lists. + * + * Returns: %TRUE if the function has been invoked, %FALSE if an + * error occurred. + */ +gboolean +g_vfunc_info_invoke (GIVFuncInfo *info, + GType implementor, + const GIArgument *in_args, + int n_in_args, + const GIArgument *out_args, + int n_out_args, + GIArgument *return_value, + GError **error) +{ + gpointer func; + + func = g_vfunc_info_get_address (info, implementor, error); + if (*error != NULL) + return FALSE; + + return g_callable_info_invoke ((GICallableInfo*) info, + func, + in_args, + n_in_args, + out_args, + n_out_args, + return_value, + TRUE, + FALSE, + error); +} diff --git a/girepository/givfuncinfo.h b/girepository/givfuncinfo.h new file mode 100644 index 000000000..725880690 --- /dev/null +++ b/girepository/givfuncinfo.h @@ -0,0 +1,73 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Virtual Functions + * + * Copyright (C) 2005 Matthias Clasen + * Copyright (C) 2008,2009 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GIVFUNCINFO_H__ +#define __GIVFUNCINFO_H__ + +#if !defined (__GIREPOSITORY_H_INSIDE__) && !defined (GI_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +/** + * GI_IS_VFUNC_INFO + * @info: an info structure + * + * Checks if @info is a #GIVfuncInfo. + */ +#define GI_IS_VFUNC_INFO(info) \ + (g_base_info_get_type((GIBaseInfo*)info) == GI_INFO_TYPE_VFUNC) + +GI_AVAILABLE_IN_ALL +GIVFuncInfoFlags g_vfunc_info_get_flags (GIVFuncInfo *info); + +GI_AVAILABLE_IN_ALL +gint g_vfunc_info_get_offset (GIVFuncInfo *info); + +GI_AVAILABLE_IN_ALL +GISignalInfo * g_vfunc_info_get_signal (GIVFuncInfo *info); + +GI_AVAILABLE_IN_ALL +GIFunctionInfo * g_vfunc_info_get_invoker (GIVFuncInfo *info); + +GI_AVAILABLE_IN_ALL +gpointer g_vfunc_info_get_address (GIVFuncInfo *info, + GType implementor_gtype, + GError **error); + +GI_AVAILABLE_IN_ALL +gboolean g_vfunc_info_invoke (GIVFuncInfo *info, + GType implementor, + const GIArgument *in_args, + int n_in_args, + const GIArgument *out_args, + int n_out_args, + GIArgument *return_value, + GError **error); + +G_END_DECLS + + +#endif /* __GIVFUNCINFO_H__ */ diff --git a/girepository/gthash-test.c b/girepository/gthash-test.c new file mode 100644 index 000000000..faeb2dc5b --- /dev/null +++ b/girepository/gthash-test.c @@ -0,0 +1,65 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Test typelib hashing + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gitypelib-internal.h" + +static void +test_build_retrieve (void) +{ + GITypelibHashBuilder *builder; + guint32 bufsize; + guint8* buf; + + builder = _gi_typelib_hash_builder_new (); + + _gi_typelib_hash_builder_add_string (builder, "Action", 0); + _gi_typelib_hash_builder_add_string (builder, "ZLibDecompressor", 42); + _gi_typelib_hash_builder_add_string (builder, "VolumeMonitor", 9); + _gi_typelib_hash_builder_add_string (builder, "FileMonitorFlags", 31); + + if (!_gi_typelib_hash_builder_prepare (builder)) + g_assert_not_reached (); + + bufsize = _gi_typelib_hash_builder_get_buffer_size (builder); + + buf = g_malloc (bufsize); + + _gi_typelib_hash_builder_pack (builder, buf, bufsize); + + _gi_typelib_hash_builder_destroy (builder); + + g_assert (_gi_typelib_hash_search (buf, "Action", 4) == 0); + g_assert (_gi_typelib_hash_search (buf, "ZLibDecompressor", 4) == 42); + g_assert (_gi_typelib_hash_search (buf, "VolumeMonitor", 4) == 9); + g_assert (_gi_typelib_hash_search (buf, "FileMonitorFlags", 4) == 31); +} + +int +main(int argc, char **argv) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/gthash/build-retrieve", test_build_retrieve); + + return g_test_run (); +} + diff --git a/girepository/gthash.c b/girepository/gthash.c new file mode 100644 index 000000000..16bfb4b8a --- /dev/null +++ b/girepository/gthash.c @@ -0,0 +1,223 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * GObject introspection: Typelib hashing + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "cmph/cmph.h" +#include "gitypelib-internal.h" + +#define ALIGN_VALUE(this, boundary) \ + (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1))) + +/* + * String hashing in the typelib. We have a set of static (fixed) strings, + * and given one, we need to find its index number. This problem is perfect + * hashing: http://en.wikipedia.org/wiki/Perfect_hashing + * + * I chose CMPH (http://cmph.sourceforge.net/) as it seemed high + * quality, well documented, and easy to embed. + * + * CMPH provides a number of algorithms; I chose BDZ, because while CHD + * appears to be the "best", the simplicitly of BDZ appealed, and really, + * we're only talking about thousands of strings here, not millions, so + * a few microseconds is no big deal. + * + * In memory, the format is: + * INT32 mph_size + * MPH (mph_size bytes) + * (padding for alignment to uint32 if necessary) + * INDEX (array of guint16) + * + * Because BDZ is not order preserving, we need a lookaside table which + * maps the hash value into the directory index. + */ + +struct _GITypelibHashBuilder { + gboolean prepared; + gboolean buildable; + cmph_t *c; + GHashTable *strings; + guint32 dirmap_offset; + guint32 packed_size; +}; + +GITypelibHashBuilder * +_gi_typelib_hash_builder_new (void) +{ + GITypelibHashBuilder *builder = g_slice_new0 (GITypelibHashBuilder); + builder->c = NULL; + builder->strings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + return builder; +} + +void +_gi_typelib_hash_builder_add_string (GITypelibHashBuilder *builder, + const char *str, + guint16 value) +{ + g_return_if_fail (builder->c == NULL); + g_hash_table_insert (builder->strings, g_strdup (str), GUINT_TO_POINTER ((guint) value)); +} + +gboolean +_gi_typelib_hash_builder_prepare (GITypelibHashBuilder *builder) +{ + char **strs; + GHashTableIter hashiter; + gpointer key, value; + cmph_io_adapter_t *io; + cmph_config_t *config; + guint32 num_elts; + guint32 offset; + guint i; + + if (builder->prepared) + return builder->buildable; + g_assert (builder->c == NULL); + + num_elts = g_hash_table_size (builder->strings); + g_assert (num_elts <= 65536); + + strs = (char**) g_new (char *, num_elts + 1); + + i = 0; + g_hash_table_iter_init (&hashiter, builder->strings); + while (g_hash_table_iter_next (&hashiter, &key, &value)) + { + const char *str = key; + + strs[i++] = g_strdup (str); + } + strs[i++] = NULL; + + io = cmph_io_vector_adapter (strs, num_elts); + config = cmph_config_new (io); + cmph_config_set_algo (config, CMPH_BDZ); + + builder->c = cmph_new (config); + builder->prepared = TRUE; + if (!builder->c) + { + builder->buildable = FALSE; + goto out; + } + builder->buildable = TRUE; + g_assert (cmph_size (builder->c) == num_elts); + + /* Pack a size counter at front */ + offset = sizeof(guint32) + cmph_packed_size (builder->c); + builder->dirmap_offset = ALIGN_VALUE (offset, 4); + builder->packed_size = builder->dirmap_offset + (num_elts * sizeof(guint16)); + out: + cmph_config_destroy (config); + cmph_io_vector_adapter_destroy (io); + return builder->buildable; +} + +guint32 +_gi_typelib_hash_builder_get_buffer_size (GITypelibHashBuilder *builder) +{ + g_return_val_if_fail (builder != NULL, 0); + g_return_val_if_fail (builder->prepared, 0); + g_return_val_if_fail (builder->buildable, 0 ); + + return builder->packed_size; +} + +void +_gi_typelib_hash_builder_pack (GITypelibHashBuilder *builder, guint8* mem, guint32 len) +{ + guint16 *table; + GHashTableIter hashiter; + gpointer key, value; + guint32 num_elts; + guint8 *packed_mem; + + g_return_if_fail (builder != NULL); + g_return_if_fail (builder->prepared); + g_return_if_fail (builder->buildable); + + g_assert (len >= builder->packed_size); + g_assert ((((size_t)mem) & 0x3) == 0); + + memset (mem, 0, len); + + *((guint32*) mem) = builder->dirmap_offset; + packed_mem = (guint8*)(mem + sizeof(guint32)); + cmph_pack (builder->c, packed_mem); + + table = (guint16*) (mem + builder->dirmap_offset); + + num_elts = g_hash_table_size (builder->strings); + g_hash_table_iter_init (&hashiter, builder->strings); + while (g_hash_table_iter_next (&hashiter, &key, &value)) + { + const char *str = key; + guint16 strval = (guint16)GPOINTER_TO_UINT(value); + guint32 hashv; + + hashv = cmph_search_packed (packed_mem, str, strlen (str)); + g_assert (hashv < num_elts); + table[hashv] = strval; + } +} + +void +_gi_typelib_hash_builder_destroy (GITypelibHashBuilder *builder) +{ + if (builder->c) + { + cmph_destroy (builder->c); + builder->c = NULL; + } + g_hash_table_destroy (builder->strings); + g_slice_free (GITypelibHashBuilder, builder); +} + +guint16 +_gi_typelib_hash_search (guint8* memory, const char *str, guint n_entries) +{ + guint32 *mph; + guint16 *table; + guint32 dirmap_offset; + guint32 offset; + + g_assert ((((size_t)memory) & 0x3) == 0); + mph = ((guint32*)memory)+1; + + offset = cmph_search_packed (mph, str, strlen (str)); + + /* Make sure that offset always lies in the entries array. cmph + cometimes generates offset larger than number of entries (for + 'str' argument which is not in the hashed list). In this case, + fake the correct result and depend on caller's final check that + the entry is really the one that the caller wanted. */ + if (offset >= n_entries) + offset = 0; + + dirmap_offset = *((guint32*)memory); + table = (guint16*) (memory + dirmap_offset); + + return table[offset]; +} + diff --git a/girepository/meson.build b/girepository/meson.build new file mode 100644 index 000000000..786749a9e --- /dev/null +++ b/girepository/meson.build @@ -0,0 +1,203 @@ +subdir('cmph') + +custom_c_args = [] + +if cc.get_id() != 'msvc' + custom_c_args = cc.get_supported_arguments([ + '-Wno-old-style-definition', + '-Wno-cast-align', + ]) +endif + +girepo_gthash_lib = static_library('girepository-gthash', + sources: 'gthash.c', + include_directories : configinc, + c_args: gi_hidden_visibility_cflags + custom_c_args, + dependencies: [ + cmph_dep, + glib_dep, + gmodule_dep, + gobject_dep, + ], +) + +girepo_gthash_dep = declare_dependency( + link_with: girepo_gthash_lib, + dependencies: [glib_dep, gmodule_dep, gobject_dep], + include_directories: include_directories('.'), +) + +if cc.get_id() != 'msvc' + custom_c_args = cc.get_supported_arguments([ + '-Wno-unused-parameter', + '-Wno-duplicated-branches', + '-Wno-cast-align', + ]) +endif + +girepo_internals_lib = static_library('girepository-internals', + sources: [ + 'girmodule.c', + 'girnode.c', + 'giroffsets.c', + 'girparser.c', + 'girwriter.c', + ], + c_args: gi_hidden_visibility_cflags + custom_c_args, + include_directories : configinc, + dependencies: [girepo_gthash_dep, libffi_dep], +) + +girepo_internals_dep = declare_dependency( + link_with: girepo_internals_lib, + dependencies: libffi_dep, + include_directories: include_directories('.'), +) + +gi_version_h = configure_file( + configuration: config, + input: 'giversion.h.in', + output: 'giversion.h', +) + +girepo_headers = [ + 'giarginfo.h', + 'gibaseinfo.h', + 'gicallableinfo.h', + 'giconstantinfo.h', + 'gienuminfo.h', + 'gifieldinfo.h', + 'gifunctioninfo.h', + 'giinterfaceinfo.h', + 'giobjectinfo.h', + 'gipropertyinfo.h', + 'giregisteredtypeinfo.h', + 'girepository.h', + 'girffi.h', + 'gisignalinfo.h', + 'gistructinfo.h', + 'gitypeinfo.h', + 'gitypelib.h', + 'gitypes.h', + 'giunioninfo.h', + 'giversionmacros.h', + 'givfuncinfo.h', + gi_version_h, +] + +girepo_sources = [ + 'gdump.c', + 'giarginfo.c', + 'gibaseinfo.c', + 'gicallableinfo.c', + 'giconstantinfo.c', + 'gienuminfo.c', + 'gifieldinfo.c', + 'gifunctioninfo.c', + 'ginvoke.c', + 'giinterfaceinfo.c', + 'giobjectinfo.c', + 'gipropertyinfo.c', + 'giregisteredtypeinfo.c', + 'girepository.c', + 'girffi.c', + 'gisignalinfo.c', + 'gistructinfo.c', + 'gitypeinfo.c', + 'gitypelib.c', + 'giunioninfo.c', + 'giversion.c', + 'givfuncinfo.c', +] + +# Used in gir/meson.build +girepo_gir_sources = files( + 'giarginfo.c', + 'gibaseinfo.c', + 'gicallableinfo.c', + 'giconstantinfo.c', + 'gienuminfo.c', + 'gifieldinfo.c', + 'gifunctioninfo.c', + 'giinterfaceinfo.c', + 'giobjectinfo.c', + 'gipropertyinfo.c', + 'giregisteredtypeinfo.c', + 'girepository.c', + 'gisignalinfo.c', + 'gistructinfo.c', + 'gitypeinfo.c', + 'giunioninfo.c', + 'giversion.c', + 'givfuncinfo.c', + 'giarginfo.h', + 'gibaseinfo.h', + 'gicallableinfo.h', + 'giconstantinfo.h', + 'gienuminfo.h', + 'gifieldinfo.h', + 'gifunctioninfo.h', + 'giinterfaceinfo.h', + 'giobjectinfo.h', + 'gipropertyinfo.h', + 'giregisteredtypeinfo.h', + 'girepository.h', + 'gisignalinfo.h', + 'gistructinfo.h', + 'gitypeinfo.h', + 'gitypelib.h', + 'gitypes.h', + 'giunioninfo.h', + 'givfuncinfo.h', +) + [gi_version_h] + + +install_headers(girepo_headers, subdir: 'gobject-introspection-1.0') + +if cc.get_id() != 'msvc' + custom_c_args = cc.get_supported_arguments([ + '-Wno-unused-parameter', + '-Wno-duplicated-branches', + '-Wno-type-limits', + '-Wno-cast-align', + '-Wno-missing-field-initializers', + ]) +endif + +lib_version = '1.0.0' +lib_version_arr = lib_version.split('.') +lib_version_major = lib_version_arr[0].to_int() +lib_version_minor = lib_version_arr[1].to_int() +lib_version_micro = lib_version_arr[2].to_int() + +osx_current = lib_version_major + 1 +lib_osx_version = [osx_current, '@0@.@1@'.format(osx_current, lib_version_minor)] + +girepo_lib = shared_library('girepository-1.0', + sources: girepo_sources, + include_directories : configinc, + c_args: gi_hidden_visibility_cflags + ['-DG_IREPOSITORY_COMPILATION'] + + custom_c_args, + dependencies: [glib_dep, gobject_dep, gmodule_dep, + gio_dep, girepo_internals_dep], + version: lib_version, + darwin_versions: lib_osx_version, + install: true, +) + +girepo_dep = declare_dependency( + link_with: girepo_lib, + dependencies: [glib_dep, gobject_dep, gio_dep, gmodule_dep], + include_directories: include_directories('.'), +) + +gthash_test = executable('gthash-test', 'gthash-test.c', + dependencies: girepo_gthash_dep, +) + +test('gthash-test', gthash_test) + +if giounix_dep.found() or giowin_dep.found() + executable('gi-dump-types', 'gi-dump-types.c', + dependencies: [girepo_dep, giounix_dep, giowin_dep]) +endif