aboutsummaryrefslogtreecommitdiffstats
path: root/libibex/wordindexmem.c
diff options
context:
space:
mode:
Diffstat (limited to 'libibex/wordindexmem.c')
-rw-r--r--libibex/wordindexmem.c721
1 files changed, 0 insertions, 721 deletions
diff --git a/libibex/wordindexmem.c b/libibex/wordindexmem.c
deleted file mode 100644
index f0cc7336f8..0000000000
--- a/libibex/wordindexmem.c
+++ /dev/null
@@ -1,721 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- *
- * Copyright (C) 2000 Helix Code, Inc.
- *
- * Authors: Michael Zucchi <notzed@helixcode.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library 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
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with the Gnome Library; see the file COPYING.LIB. If not,
- * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/* this is the same as wordindex.c, but it doesn't have an LRU cache
- for word names. it has a lookup tab le that is only loaded if
- index-pre is called, otherwise it always hits disk */
-
-/* code to manage a word index */
-/* includes a cache for word index writes,
- but not for name index writes (currently), or any reads.
-
-Note the word cache is only needed during indexing of lots
-of words, and could then be discarded (:flush()).
-
-*/
-
-#include <stdio.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <glib.h>
-
-#include "block.h"
-#include "index.h"
-#include "wordindex.h"
-
-/*#define MALLOC_CHECK*/
-
-#ifdef MALLOC_CHECK
-#include <mcheck.h>
-#endif
-
-#define d(x)
-
-/*#define WORDCACHE_SIZE (256)*/
-#define WORDCACHE_SIZE (4096)
-
-extern struct _IBEXStoreClass ibex_diskarray_class;
-extern struct _IBEXIndexClass ibex_hash_class;
-
-/* need 2 types of hash key?
- one that just stores the wordid / wordblock
- and one that stores the filecount/files?
-*/
-
-
-#define CACHE_FILE_COUNT (62)
-
-struct _wordcache {
- nameid_t wordid; /* disk wordid */
- blockid_t wordblock; /* head of disk list */
- blockid_t wordtail; /* and the tail data */
- short filecount; /* how many valid items in files[] */
- short filealloc; /* how much allocated space in files[] */
- union {
- nameid_t *files; /* memory cache of files */
- nameid_t file0; /* if filecount == 1 && filealloc == 0, store directly */
- } file;
- char word[1]; /* actual word follows */
-};
-
-static void unindex_name(struct _IBEXWord *, const char *name); /* unindex all entries for name */
-static gboolean contains_name(struct _IBEXWord *, const char *name); /* index contains data for name */
-static GPtrArray *find(struct _IBEXWord *, const char *word); /* returns all matches for word */
-static gboolean find_name(struct _IBEXWord *, const char *name, const char *word); /* find if name contains word */
-static void add(struct _IBEXWord *, const char *name, const char *word); /* adds a single word to name (slow) */
-static void add_list(struct _IBEXWord *, const char *name, GPtrArray *words);/* adds a bunch of words to a given name */
-static int word_sync(struct _IBEXWord *idx);
-static int word_flush(struct _IBEXWord *idx);
-static int word_close(struct _IBEXWord *idx);
-static void word_index_pre(struct _IBEXWord *idx);
-static void word_index_post(struct _IBEXWord *idx);
-
-static void sync_cache_entry(struct _IBEXWord *idx, struct _wordcache *cache);
-
-struct _IBEXWordClass ibex_word_index_mem_class = {
- word_sync, word_flush, word_close,
- word_index_pre, word_index_post,
- unindex_name, contains_name,
- find, find_name,
- add, add_list
-};
-
-#ifdef MALLOC_CHECK
-static void
-checkmem(void *p)
-{
- if (p) {
- int status = mprobe(p);
-
- switch (status) {
- case MCHECK_HEAD:
- printf("Memory underrun at %p\n", p);
- abort();
- case MCHECK_TAIL:
- printf("Memory overrun at %p\n", p);
- abort();
- case MCHECK_FREE:
- printf("Double free %p\n", p);
- abort();
- }
- }
-}
-#endif
-
-/* this interface isn't the best, but it'll do for now */
-struct _IBEXWord *
-ibex_create_word_index_mem(struct _memcache *bc, blockid_t *wordroot, blockid_t *nameroot)
-{
- struct _IBEXWord *idx;
-
- idx = g_malloc0(sizeof(*idx));
- idx->wordcache = g_hash_table_new(g_str_hash, g_str_equal);
- ibex_list_new(&idx->wordnodes);
- idx->wordcount = 0;
- idx->precount = 0;
- idx->klass = &ibex_word_index_mem_class;
-
- /* we use the same block array storage for both indexes at the moment */
- idx->wordstore = ibex_diskarray_class.create(bc);
- idx->namestore = idx->wordstore;
-
- /* but not the same indexes! */
- if (*wordroot) {
- printf("opening wordindex root = %d\n", *wordroot);
- idx->wordindex = ibex_hash_class.open(bc, *wordroot);
- } else {
- idx->wordindex = ibex_hash_class.create(bc, 2048);
- *wordroot = idx->wordindex->root;
- printf("creating wordindex root = %d\n", *wordroot);
- }
- if (*nameroot) {
- printf("opening nameindex root = %d\n", *nameroot);
- idx->nameindex = ibex_hash_class.open(bc, *nameroot);
- } else {
- idx->nameindex = ibex_hash_class.create(bc, 2048);
- *nameroot = idx->nameindex->root;
- printf("creating nameindex root = %d\n", *nameroot);
- }
- return idx;
-}
-
-static void
-node_sanity(char *key, struct _wordcache *node, void *data)
-{
- g_assert(node->filecount <= node->filealloc || (node->filecount == 1 && node->filealloc == 0));
- g_assert(strlen(node->word) != 0);
-#ifdef MALLOC_CHECK
- checkmem(node);
- if (node->filealloc)
- checkmem(node->file.files);
-#endif
-}
-
-static void
-cache_sanity(struct _IBEXWord *idx)
-{
-#ifdef MALLOC_CHECK
- checkmem(idx);
-#endif
- g_hash_table_foreach(idx->wordcache, (GHFunc)node_sanity, idx);
-}
-
-static void word_index_pre(struct _IBEXWord *idx)
-{
- struct _IBEXCursor *idc;
- struct _wordcache *cache;
- nameid_t wordid;
- char *key;
- int len;
-
- idx->precount ++;
- if (idx->precount > 1)
- return;
-
- /* want to load all words into the cache lookup table */
- printf("pre-loading all word info into memory\n");
- idc = idx->wordindex->klass->get_cursor(idx->wordindex);
- while ( (wordid = idc->klass->next(idc)) ) {
- key = idc->index->klass->get_key(idc->index, wordid, &len);
- /*d(printf("Adding word %s\n", key));*/
- cache = g_malloc0(sizeof(*cache) + strlen(key));
- strcpy(cache->word, key);
- g_free(key);
- cache->wordid = wordid;
- cache->wordblock = idc->index->klass->get_data(idc->index, wordid, &cache->wordtail);
- cache->filecount = 0;
- cache->filealloc = 0;
- g_hash_table_insert(idx->wordcache, cache->word, cache);
- idx->wordcount++;
- }
-
-#ifdef MALLOC_CHECK
- cache_sanity(idx);
-#endif
-
- idc->klass->close(idc);
-
- printf("done\n");
-}
-
-static gboolean
-sync_free_value(void *key, void *value, void *data)
-{
- struct _wordcache *cache = (struct _wordcache *)value;
- struct _IBEXWord *idx = (struct _IBEXWord *)data;
-
- sync_cache_entry(idx, cache);
- if (cache->filealloc)
- g_free(cache->file.files);
- g_free(cache);
-
- return TRUE;
-}
-
-static void
-sync_value(void *key, void *value, void *data)
-{
- struct _wordcache *cache = (struct _wordcache *)value;
- struct _IBEXWord *idx = (struct _IBEXWord *)data;
-
- sync_cache_entry(idx, cache);
-}
-
-static void word_index_post(struct _IBEXWord *idx)
-{
- idx->precount--;
- if (idx->precount > 0)
- return;
- idx->precount = 0;
-
-#ifdef MALLOC_CHECK
- cache_sanity(idx);
-#endif
-
- g_hash_table_foreach_remove(idx->wordcache, sync_free_value, idx);
- idx->wordcount = 0;
-}
-
-/* unindex all entries for name */
-static void unindex_name(struct _IBEXWord *idx, const char *name)
-{
- GArray *words;
- int i;
- nameid_t nameid, wordid;
- blockid_t nameblock, wordblock, newblock, nametail, wordtail, newtail;
- char *word;
- struct _wordcache *cache;
-
- d(printf("unindexing %s\n", name));
-
- /* lookup the hash key */
- nameid = idx->nameindex->klass->find(idx->nameindex, name, strlen(name));
- /* get the block for this key */
- nameblock = idx->nameindex->klass->get_data(idx->nameindex, nameid, &nametail);
- /* and the data for this block */
- words = idx->namestore->klass->get(idx->namestore, nameblock, nametail);
- /* walk it ... */
- for (i=0;i<words->len;i++) {
- /* get the word */
- wordid = g_array_index(words, nameid_t, i);
- d(printf(" word %d\n", wordid));
- /* get the data block */
- wordblock = idx->wordindex->klass->get_data(idx->wordindex, wordid, &wordtail);
- /* clear this name from it */
- newblock = wordblock;
- newtail = wordtail;
- idx->wordstore->klass->remove(idx->wordstore, &newblock, &newtail, nameid);
- if (newblock != wordblock || newtail != wordtail)
- idx->wordindex->klass->set_data(idx->wordindex, wordid, newblock, newtail);
-
- /* now check the cache as well */
- word = idx->nameindex->klass->get_key(idx->wordindex, wordid, NULL);
- if (word) {
- cache = g_hash_table_lookup(idx->wordcache, word);
- if (cache) {
- /* its there, update our head/tail pointers */
- cache->wordblock = newblock;
- cache->wordtail = newtail;
-
- /* now check that we have a data entry in it */
- if (cache->filealloc == 0 && cache->filecount == 1) {
- if (cache->file.file0 == nameid) {
- cache->filecount = 0;
- }
- } else {
- int j;
-
- for (j=0;j<cache->filecount;j++) {
- if (cache->file.files[j] == nameid) {
- cache->file.files[j] = cache->file.files[cache->filecount-1];
- cache->filecount--;
- break;
- }
- }
- }
- }
- g_free(word);
- }
- }
- g_array_free(words, TRUE);
-
- /* and remove name data and itself */
- idx->namestore->klass->free(idx->namestore, nameblock, nametail);
- idx->nameindex->klass->remove(idx->nameindex, name, strlen(name));
-}
-
-/* index contains (any) data for name */
-static gboolean contains_name(struct _IBEXWord *idx, const char *name)
-{
- return idx->nameindex->klass->find(idx->nameindex, name, strlen(name)) != 0;
-}
-
-/* returns all matches for word */
-static GPtrArray *find(struct _IBEXWord *idx, const char *word)
-{
- nameid_t wordid, nameid;
- GPtrArray *res;
- GArray *names;
- int i;
- char *new;
- struct _wordcache *cache;
- blockid_t wordblock, wordtail;
-
- res = g_ptr_array_new();
-
- cache = g_hash_table_lookup(idx->wordcache, word);
- if (cache) {
-#if 0
- /* freshen cache entry if we touch it */
- ibex_list_remove((struct _listnode *)cache);
- ibex_list_addtail(&idx->wordnodes, (struct _listnode *)cache);
-#endif
- wordid = cache->wordid;
- wordblock = cache->wordblock;
- wordtail = cache->wordtail;
- } else {
- /* lookup the hash key */
- wordid = idx->wordindex->klass->find(idx->wordindex, word, strlen(word));
- /* get the block for this key */
- wordblock = idx->wordindex->klass->get_data(idx->wordindex, wordid, &wordtail);
- }
- /* and the data for this block */
- names = idx->wordstore->klass->get(idx->wordstore, wordblock, wordtail);
- /* .. including any memory-only data */
- if (cache) {
- if (cache->filealloc == 0 && cache->filecount == 1)
- g_array_append_val(names, cache->file.file0);
- else
- g_array_append_vals(names, cache->file.files, cache->filecount);
- }
-
- /* walk it ... converting id's back to strings */
- g_ptr_array_set_size(res, names->len);
- for (i=0;i<names->len;i++) {
- nameid = g_array_index(names, nameid_t, i);
- new = idx->nameindex->klass->get_key(idx->nameindex, nameid, NULL);
- res->pdata[i] = new;
- }
- g_array_free(names, TRUE);
- return res;
-}
-
-/* find if name contains word */
-static gboolean find_name(struct _IBEXWord *idx, const char *name, const char *word)
-{
- nameid_t wordid, nameid;
- blockid_t nameblock, nametail;
- struct _wordcache *cache;
- int i;
-
- /* lookup the hash key for the name */
- nameid = idx->nameindex->klass->find(idx->nameindex, name, strlen(name));
- /* get the block for this name */
- nameblock = idx->nameindex->klass->get_data(idx->nameindex, nameid, &nametail);
-
- /* check if there is an in-memory cache for this word, check its file there first */
- cache = g_hash_table_lookup(idx->wordcache, word);
- if (cache) {
-#if 0
- /* freshen cache entry if we touch it */
- ibex_list_remove((struct _listnode *)cache);
- ibex_list_addtail(&idx->wordnodes, (struct _listnode *)cache);
-#endif
- if (cache->filecount == 1 && cache->filealloc == 0) {
- if (cache->file.file0 == nameid)
- return TRUE;
- } else {
- for (i=0;i<cache->filecount;i++) {
- if (cache->file.files[i] == nameid)
- return TRUE;
- }
- }
- /* not there? well we can use the wordid anyway */
- wordid = cache->wordid;
- } else {
- /* lookup the hash key for word */
- wordid = idx->wordindex->klass->find(idx->wordindex, word, strlen(word));
- }
-
- /* see if wordid is in nameblock */
- return idx->namestore->klass->find(idx->namestore, nameblock, nametail, wordid);
-}
-
-/* cache helper functions */
-/* flush a cache entry to disk, and empty it out */
-static void
-sync_cache_entry(struct _IBEXWord *idx, struct _wordcache *cache)
-{
- GArray array; /* just use this as a header */
- blockid_t oldblock, oldtail;
-
- if (cache->filecount == 0)
- return;
-
- d(printf("syncing cache entry '%s' used %d\n", cache->word, cache->filecount));
- if (cache->filecount == 1 && cache->filealloc == 0)
- array.data = (char *)&cache->file.file0;
- else
- array.data = (char *)cache->file.files;
- array.len = cache->filecount;
- oldblock = cache->wordblock;
- oldtail = cache->wordtail;
- idx->wordstore->klass->add_list(idx->wordstore, &cache->wordblock, &cache->wordtail, &array);
- if (oldblock != cache->wordblock || oldtail != cache->wordtail) {
- idx->wordindex->klass->set_data(idx->wordindex, cache->wordid, cache->wordblock, cache->wordtail);
- }
- cache->filecount = 0;
-}
-
-/* create a new key in an index, returning its id and head block */
-static void
-add_index_key(struct _IBEXIndex *wordindex, const char *word, nameid_t *wordid, blockid_t *wordblock, blockid_t *wordtail)
-{
- /* initialise cache entry - id of word entry and head block */
- *wordid = wordindex->klass->find(wordindex, word, strlen(word));
-
- if (*wordid == 0) {
- *wordid = wordindex->klass->insert(wordindex, word, strlen(word));
- *wordblock = 0;
- *wordtail = 0;
- } else {
- *wordblock = wordindex->klass->get_data(wordindex, *wordid, wordtail);
- }
-}
-
-/* create a new key in a cached index (only word cache so far), flushing old keys
- if too much space is being used */
-static struct _wordcache *
-add_index_cache(struct _IBEXWord *idx, const char *word)
-{
- struct _wordcache *cache;
-
- cache = g_hash_table_lookup(idx->wordcache, word);
- if (cache == 0) {
- /*d(printf("adding %s to cache\n", word));*/
-
-#if 0
- /* see if we have to flush off the last entry */
- if (idx->wordcount >= WORDCACHE_SIZE) {
- struct _wordcache *mincache;
- int min, count=0;
- /* remove last entry, and flush it */
- cache = (struct _wordcache *)idx->wordnodes.tailpred;
- mincache = cache;
- min = mincache->filecount;
-
- d(printf("flushing word from cache %s\n", cache->word));
- /* instead of just using the last entry, we try and find an entry with
- with only 1 item (failing that, the smallest in the range we look at) */
- /* this could probably benefit greatly from a more sophisticated aging algorithm */
- while (cache->next && count < 100) {
- if (cache->filecount == 1) {
- mincache = cache;
- break;
- }
- if (cache->filecount > 0 && cache->filecount < min) {
- mincache = cache;
- min = cache->filecount;
- }
- cache = cache->next;
- count++;
- }
- ibex_list_remove((struct _listnode *)mincache);
- g_hash_table_remove(idx->wordcache, mincache->word);
- sync_cache_entry(idx, mincache);
- if (mincache->filealloc)
- g_free(mincache->file.files);
- g_free(mincache);
- idx->wordcount--;
- }
-#endif
- cache = g_malloc0(sizeof(*cache)+strlen(word));
- /* if we're in an index state, we can assume we dont have it if we dont have it in memory */
- if (idx->precount == 0) {
- /* initialise cache entry - id of word entry and head block */
- add_index_key(idx->wordindex, word, &cache->wordid, &cache->wordblock, &cache->wordtail);
- } else {
- cache->wordid = idx->wordindex->klass->insert(idx->wordindex, word, strlen(word));
- }
- /* other fields */
- strcpy(cache->word, word);
- cache->filecount = 0;
- g_hash_table_insert(idx->wordcache, cache->word, cache);
-#if 0
- ibex_list_addhead(&idx->wordnodes, (struct _listnode *)cache);
-#endif
- idx->wordcount++;
- } else {
- /*d(printf("already have %s in cache\n", word));*/
-#if 0
- /* move cache bucket ot the head of the cache list */
- ibex_list_remove((struct _listnode *)cache);
- ibex_list_addhead(&idx->wordnodes, (struct _listnode *)cache);
-#endif
- }
- return cache;
-}
-
-/* adds a single word to name (slow) */
-static void add(struct _IBEXWord *idx, const char *name, const char *word)
-{
- nameid_t nameid;
- blockid_t nameblock, newblock, nametail, newtail;
- struct _wordcache *cache;
-
- g_error("Dont use wordindex::add()");
- abort();
-
- cache = add_index_cache(idx, word);
-
- /* get the nameid and block start for this name */
- add_index_key(idx->nameindex, name, &nameid, &nameblock, &nametail);
-
- /* check for repeats of the last name - dont add anything */
- if (cache->filecount == 1 && cache->filealloc == 0) {
- if (cache->file.file0 == nameid)
- return;
- } else {
- if (cache->file.files[cache->filecount] == nameid)
- return;
- }
-
- /* see if we are setting the first, drop it in the union */
- if (cache->filecount == 0 && cache->filealloc == 0) {
- cache->file.file0 = nameid;
- } else if (cache->filecount == 1 && cache->filealloc == 0) {
- nameid_t saveid;
- /* we need to allocate space for words */
- saveid = cache->file.file0;
- cache->file.files = g_malloc(sizeof(cache->file.files[0]) * CACHE_FILE_COUNT);
- /* this could possibly grow as needed, but i wont for now */
- cache->filealloc = CACHE_FILE_COUNT;
- cache->file.files[0] = saveid;
- cache->file.files[1] = nameid;
- } else {
- cache->file.files[cache->filecount] = nameid;
- }
-
- cache->filecount++;
-
- /* if we are full, force a flush now */
- if (cache->filealloc && cache->filecount >= cache->filealloc) {
- sync_cache_entry(idx, cache);
- }
-
- newtail = nametail;
- newblock = nameblock;
- idx->namestore->klass->add(idx->namestore, &newblock, &newtail, cache->wordid);
- if (newblock != nameblock || newtail != nametail) {
- idx->nameindex->klass->set_data(idx->nameindex, nameid, newblock, newtail);
- }
-}
-
-/* adds a bunch of words to a given name */
-static void add_list(struct _IBEXWord *idx, const char *name, GPtrArray *words)
-{
- int i;
- GArray *data = g_array_new(0, 0, sizeof(nameid_t));
- blockid_t nameblock, newblock, nametail, newtail;
- nameid_t nameid;
- struct _wordcache *cache;
-
- d(printf("Adding words to name %s\n", name));
-
- d(cache_sanity(idx));
-
- /* get the nameid and block start for this name */
- add_index_key(idx->nameindex, name, &nameid, &nameblock, &nametail);
-
- d(cache_sanity(idx));
-
- for (i=0;i<words->len;i++) {
- char *word = words->pdata[i];
-
- cache = add_index_cache(idx, word);
-
- /*d(cache_sanity(idx));*/
-
- /* check for duplicates; doesn't catch duplicates over an overflow boundary. Watch me care. */
- if (cache->filecount == 0
- /* the 1 item case */
- || (cache->filecount == 1 && cache->filealloc == 0 && cache->file.file0 != nameid)
- /* the normal case */
- || (cache->filealloc > 0 && cache->file.files[cache->filecount-1] != nameid)) {
-
- /* see if we are setting the first, drop it in the union */
- if (cache->filecount == 0 && cache->filealloc == 0) {
- cache->file.file0 = nameid;
- } else if (cache->filecount == 1 && cache->filealloc == 0) {
- nameid_t saveid;
- /* we need to allocate space for words */
- saveid = cache->file.file0;
- cache->file.files = g_malloc(sizeof(cache->file.files[0]) * CACHE_FILE_COUNT);
- /* this could possibly grow as needed, but i wont for now */
- cache->filealloc = CACHE_FILE_COUNT;
- cache->file.files[0] = saveid;
- cache->file.files[1] = nameid;
- } else {
- cache->file.files[cache->filecount] = nameid;
- }
-
- cache->filecount++;
-
- /* if we are full, force a flush now */
- if (cache->filealloc && cache->filecount >= cache->filealloc) {
- sync_cache_entry(idx, cache);
- }
-
- /*d(cache_sanity(idx));*/
-
- /* and append this wordid for this name in memory */
- g_array_append_val(data, cache->wordid);
- }
-
- /*d(cache_sanity(idx));*/
- }
-
- d(cache_sanity(idx));
-
- /* and append these word id's in one go */
- newblock = nameblock;
- newtail = nametail;
- idx->namestore->klass->add_list(idx->namestore, &newblock, &newtail, data);
- if (newblock != nameblock || newtail != nametail) {
- idx->nameindex->klass->set_data(idx->nameindex, nameid, newblock, newtail);
- }
-
- d(cache_sanity(idx));
-
- g_array_free(data, TRUE);
-}
-
-/* sync any in-memory data to disk */
-static int
-word_sync(struct _IBEXWord *idx)
-{
- /* we just flush also, save memory */
- word_flush(idx);
-
-#if 0
- struct _wordcache *cache = (struct _wordcache *)idx->wordnodes.head;
-
- while (cache->next) {
- sync_cache_entry(idx, cache);
- cache = cache->next;
- }
-
- /*ibex_hash_dump(idx->wordindex);*/
- /*ibex_hash_dump(idx->nameindex);*/
-#endif
- return 0;
-}
-
-/* sync and flush any in-memory data to disk and free it */
-static int
-word_flush(struct _IBEXWord *idx)
-{
- d(cache_sanity(idx));
-
- g_hash_table_foreach_remove(idx->wordcache, sync_free_value, idx);
- idx->wordcount = 0;
- return 0;
-}
-
-static int word_close(struct _IBEXWord *idx)
-{
- idx->klass->flush(idx);
-
- idx->namestore->klass->close(idx->namestore);
- idx->nameindex->klass->close(idx->nameindex);
- /*same as namestore:
- idx->wordstore->klass->close(idx->wordstore);*/
- idx->wordindex->klass->close(idx->wordindex);
- g_hash_table_destroy(idx->wordcache);
- g_free(idx);
-
- return 0;
-}