~ubuntu-branches/ubuntu/quantal/evolution-data-server/quantal

« back to all changes in this revision

Viewing changes to .pc/remove_g_thread_init.patch/camel/camel-text-index.c

  • Committer: Package Import Robot
  • Author(s): Jordi Mallach
  • Date: 2012-05-18 19:32:20 UTC
  • mfrom: (1.1.98) (1.6.16 experimental)
  • Revision ID: package-import@ubuntu.com-20120518193220-v4re47nsbqy2ko3t
Tags: 3.4.2-1
* New upstream version.
* Add list-missing target.
* Install gsettings schemas and gconf conversion scripts.
* Include buildflags.mk, and adjust rules to use all hardening flags.
* Move all gtk-doc API documentation to a new evolution-data-server-doc
  binary, and let it break/replace obsolete libedataserverui1.2-dev
  (closes: #672253).
* Build-Depend on libglib2.0-doc and libdbus-glib-1-doc to ensure
  proper cross-reference links in generated gtk-doc API documentation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
2
 
/*
3
 
 *  Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4
 
 *
5
 
 *  Authors: Michael Zucchi <notzed@ximian.com>
6
 
 *
7
 
 * This program is free software; you can redistribute it and/or
8
 
 * modify it under the terms of version 2 of the GNU Lesser General Public
9
 
 * License as published by the Free Software Foundation.
10
 
 *
11
 
 * This program is distributed in the hope that it will be useful,
12
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 
 * General Public License for more details.
15
 
 *
16
 
 * You should have received a copy of the GNU Lesser General Public
17
 
 * License along with this program; if not, write to the
18
 
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
 
 * Boston, MA 02110-1301, USA.
20
 
 */
21
 
 
22
 
#ifdef HAVE_CONFIG_H
23
 
#include <config.h>
24
 
#endif
25
 
 
26
 
#include <ctype.h>
27
 
#include <errno.h>
28
 
#include <fcntl.h>
29
 
#include <stdio.h>
30
 
#include <stdlib.h>
31
 
#include <string.h>
32
 
#include <unistd.h>
33
 
#include <sys/stat.h>
34
 
#include <sys/types.h>
35
 
 
36
 
#include <glib/gstdio.h>
37
 
 
38
 
#include "camel-block-file.h"
39
 
#include "camel-list-utils.h"
40
 
#include "camel-mempool.h"
41
 
#include "camel-object.h"
42
 
#include "camel-partition-table.h"
43
 
#include "camel-text-index.h"
44
 
 
45
 
#define w(x)
46
 
#define io(x)
47
 
#define d(x) /*(printf ("%s (%d):%s: ",  __FILE__, __LINE__, __PRETTY_FUNCTION__),(x))*/
48
 
 
49
 
/* cursor debug */
50
 
#define c(x)
51
 
 
52
 
#define CAMEL_TEXT_INDEX_MAX_WORDLEN  (36)
53
 
 
54
 
#define CAMEL_TEXT_INDEX_LOCK(kf, lock) \
55
 
        (g_static_rec_mutex_lock (&((CamelTextIndex *) kf)->priv->lock))
56
 
#define CAMEL_TEXT_INDEX_UNLOCK(kf, lock) \
57
 
        (g_static_rec_mutex_unlock (&((CamelTextIndex *) kf)->priv->lock))
58
 
 
59
 
static gint text_index_compress_nosync (CamelIndex *idx);
60
 
 
61
 
/* ********************************************************************** */
62
 
 
63
 
struct _CamelTextIndexNamePrivate {
64
 
        GString *buffer;
65
 
        camel_key_t nameid;
66
 
        CamelMemPool *pool;
67
 
};
68
 
 
69
 
CamelTextIndexName *camel_text_index_name_new (CamelTextIndex *idx, const gchar *name, camel_key_t nameid);
70
 
 
71
 
/* ****************************** */
72
 
 
73
 
struct _CamelTextIndexCursorPrivate {
74
 
        camel_block_t first;
75
 
        camel_block_t next;
76
 
 
77
 
        gint record_index;
78
 
 
79
 
        gsize record_count;
80
 
        camel_key_t *records;
81
 
 
82
 
        gchar *current;
83
 
};
84
 
 
85
 
CamelTextIndexCursor *camel_text_index_cursor_new (CamelTextIndex *idx, camel_block_t data);
86
 
 
87
 
/* ****************************** */
88
 
 
89
 
struct _CamelTextIndexKeyCursorPrivate {
90
 
        CamelKeyTable *table;
91
 
 
92
 
        camel_key_t keyid;
93
 
        guint flags;
94
 
        camel_block_t data;
95
 
        gchar *current;
96
 
};
97
 
 
98
 
CamelTextIndexKeyCursor *camel_text_index_key_cursor_new (CamelTextIndex *idx, CamelKeyTable *table);
99
 
 
100
 
/* ********************************************************************** */
101
 
 
102
 
#define CAMEL_TEXT_INDEX_VERSION "TEXT.000"
103
 
#define CAMEL_TEXT_INDEX_KEY_VERSION "KEYS.000"
104
 
 
105
 
struct _CamelTextIndexPrivate {
106
 
        CamelBlockFile *blocks;
107
 
        CamelKeyFile *links;
108
 
 
109
 
        CamelKeyTable *word_index;
110
 
        CamelPartitionTable *word_hash;
111
 
 
112
 
        CamelKeyTable *name_index;
113
 
        CamelPartitionTable *name_hash;
114
 
 
115
 
        /* Cache of words to write */
116
 
        gint word_cache_limit;
117
 
        gint word_cache_count;
118
 
        CamelDList word_cache;
119
 
        GHashTable *words;
120
 
        GStaticRecMutex lock;
121
 
};
122
 
 
123
 
/* Root block of text index */
124
 
struct _CamelTextIndexRoot {
125
 
        struct _CamelBlockRoot root;
126
 
 
127
 
        /* FIXME: the index root could contain a pointer to the hash root */
128
 
        camel_block_t word_index_root; /* a keyindex containing the keyid -> word mapping */
129
 
        camel_block_t word_hash_root; /* a partitionindex containing word -> keyid mapping */
130
 
 
131
 
        camel_block_t name_index_root; /* same, for names */
132
 
        camel_block_t name_hash_root;
133
 
 
134
 
        guint32 words;          /* total words */
135
 
        guint32 names;          /* total names */
136
 
        guint32 deleted;        /* deleted names */
137
 
        guint32 keys;           /* total key 'chunks' written, used with deleted to determine fragmentation */
138
 
};
139
 
 
140
 
struct _CamelTextIndexWord {
141
 
        struct _CamelTextIndexWord *next;
142
 
        struct _CamelTextIndexWord *prev;
143
 
 
144
 
        camel_block_t data;     /* where the data starts */
145
 
        camel_key_t wordid;
146
 
        gchar *word;
147
 
        guint used;
148
 
        camel_key_t names[32];
149
 
};
150
 
 
151
 
/* ********************************************************************** */
152
 
/* CamelTextIndex */
153
 
/* ********************************************************************** */
154
 
 
155
 
G_DEFINE_TYPE (CamelTextIndex, camel_text_index, CAMEL_TYPE_INDEX)
156
 
 
157
 
static void
158
 
text_index_dispose (GObject *object)
159
 
{
160
 
        CamelTextIndexPrivate *priv;
161
 
 
162
 
        priv = CAMEL_TEXT_INDEX (object)->priv;
163
 
 
164
 
        /* Only run this the first time. */
165
 
        if (priv->word_index != NULL)
166
 
                camel_index_sync (CAMEL_INDEX (object));
167
 
 
168
 
        if (priv->word_index != NULL) {
169
 
                g_object_unref (priv->word_index);
170
 
                priv->word_index = NULL;
171
 
        }
172
 
 
173
 
        if (priv->word_hash != NULL) {
174
 
                g_object_unref (priv->word_hash);
175
 
                priv->word_hash = NULL;
176
 
        }
177
 
 
178
 
        if (priv->name_index != NULL) {
179
 
                g_object_unref (priv->name_index);
180
 
                priv->name_index = NULL;
181
 
        }
182
 
 
183
 
        if (priv->name_hash != NULL) {
184
 
                g_object_unref (priv->name_hash);
185
 
                priv->name_hash = NULL;
186
 
        }
187
 
 
188
 
        if (priv->blocks != NULL) {
189
 
                g_object_unref (priv->blocks);
190
 
                priv->blocks = NULL;
191
 
        }
192
 
 
193
 
        if (priv->links != NULL) {
194
 
                g_object_unref (priv->links);
195
 
                priv->links = NULL;
196
 
        }
197
 
 
198
 
        /* Chain up to parent's dispose () method. */
199
 
        G_OBJECT_CLASS (camel_text_index_parent_class)->dispose (object);
200
 
}
201
 
 
202
 
static void
203
 
text_index_finalize (GObject *object)
204
 
{
205
 
        CamelTextIndexPrivate *priv;
206
 
 
207
 
        priv = CAMEL_TEXT_INDEX (object)->priv;
208
 
 
209
 
        g_assert (camel_dlist_empty (&priv->word_cache));
210
 
        g_assert (g_hash_table_size (priv->words) == 0);
211
 
 
212
 
        g_hash_table_destroy (priv->words);
213
 
 
214
 
        g_static_rec_mutex_free (&priv->lock);
215
 
 
216
 
        /* Chain up to parent's finalize () method. */
217
 
        G_OBJECT_CLASS (camel_text_index_parent_class)->finalize (object);
218
 
}
219
 
 
220
 
/* call locked */
221
 
static void
222
 
text_index_add_name_to_word (CamelIndex *idx,
223
 
                             const gchar *word,
224
 
                             camel_key_t nameid)
225
 
{
226
 
        struct _CamelTextIndexWord *w, *wp, *ww;
227
 
        CamelTextIndexPrivate *p = CAMEL_TEXT_INDEX (idx)->priv;
228
 
        camel_key_t wordid;
229
 
        camel_block_t data;
230
 
        struct _CamelTextIndexRoot *rb = (struct _CamelTextIndexRoot *) p->blocks->root;
231
 
 
232
 
        w = g_hash_table_lookup (p->words, word);
233
 
        if (w == NULL) {
234
 
                wordid = camel_partition_table_lookup (p->word_hash, word);
235
 
                if (wordid == 0) {
236
 
                        data = 0;
237
 
                        wordid = camel_key_table_add (p->word_index, word, 0, 0);
238
 
                        if (wordid == 0) {
239
 
                                g_warning ("Could not create key entry for word '%s': %s\n",
240
 
                                           word, g_strerror (errno));
241
 
                                return;
242
 
                        }
243
 
                        if (camel_partition_table_add (p->word_hash, word, wordid) == -1) {
244
 
                                g_warning ("Could not create hash entry for word '%s': %s\n",
245
 
                                           word, g_strerror (errno));
246
 
                                return;
247
 
                        }
248
 
                        rb->words++;
249
 
                        camel_block_file_touch_block (p->blocks, p->blocks->root_block);
250
 
                } else {
251
 
                        data = camel_key_table_lookup (p->word_index, wordid, NULL, NULL);
252
 
                        if (data == 0) {
253
 
                                g_warning ("Could not find key entry for word '%s': %s\n",
254
 
                                           word, g_strerror (errno));
255
 
                                return;
256
 
                        }
257
 
                }
258
 
 
259
 
                w = g_malloc0 (sizeof (*w));
260
 
                w->word = g_strdup (word);
261
 
                w->wordid = wordid;
262
 
                w->used = 1;
263
 
                w->data = data;
264
 
 
265
 
                w->names[0] = nameid;
266
 
                g_hash_table_insert (p->words, w->word, w);
267
 
                camel_dlist_addhead (&p->word_cache, (CamelDListNode *) w);
268
 
                p->word_cache_count++;
269
 
                ww = (struct _CamelTextIndexWord *) p->word_cache.tailpred;
270
 
                wp = ww->prev;
271
 
                while (wp && p->word_cache_count > p->word_cache_limit) {
272
 
                        io (printf ("writing key file entry '%s' [%x]\n", ww->word, ww->data));
273
 
                        if (camel_key_file_write (p->links, &ww->data, ww->used, ww->names) != -1) {
274
 
                                io (printf ("  new data [%x]\n", ww->data));
275
 
                                rb->keys++;
276
 
                                camel_block_file_touch_block (p->blocks, p->blocks->root_block);
277
 
                                /* if this call fails - we still point to the old data - not fatal */
278
 
                                camel_key_table_set_data (
279
 
                                        p->word_index, ww->wordid, ww->data);
280
 
                                camel_dlist_remove ((CamelDListNode *) ww);
281
 
                                g_hash_table_remove (p->words, ww->word);
282
 
                                g_free (ww->word);
283
 
                                g_free (ww);
284
 
                                p->word_cache_count--;
285
 
                        }
286
 
                        ww = wp;
287
 
                        wp = wp->prev;
288
 
                }
289
 
        } else {
290
 
                camel_dlist_remove ((CamelDListNode *) w);
291
 
                camel_dlist_addhead (&p->word_cache, (CamelDListNode *) w);
292
 
                w->names[w->used] = nameid;
293
 
                w->used++;
294
 
                if (w->used == G_N_ELEMENTS (w->names)) {
295
 
                        io (printf ("writing key file entry '%s' [%x]\n", w->word, w->data));
296
 
                        if (camel_key_file_write (p->links, &w->data, w->used, w->names) != -1) {
297
 
                                rb->keys++;
298
 
                                camel_block_file_touch_block (p->blocks, p->blocks->root_block);
299
 
                                /* if this call fails - we still point to the old data - not fatal */
300
 
                                camel_key_table_set_data (
301
 
                                        p->word_index, w->wordid, w->data);
302
 
                        }
303
 
                        /* FIXME: what to on error?  lost data? */
304
 
                        w->used = 0;
305
 
                }
306
 
        }
307
 
}
308
 
 
309
 
static gint
310
 
text_index_sync (CamelIndex *idx)
311
 
{
312
 
        CamelTextIndexPrivate *p = CAMEL_TEXT_INDEX (idx)->priv;
313
 
        struct _CamelTextIndexWord *ww;
314
 
        struct _CamelTextIndexRoot *rb;
315
 
        gint ret = 0, wfrag, nfrag;
316
 
 
317
 
        d (printf ("sync: blocks = %p\n", p->blocks));
318
 
 
319
 
        if (p->blocks == NULL || p->links == NULL
320
 
            || p->word_index == NULL || p->word_hash == NULL
321
 
            || p->name_index == NULL || p->name_hash == NULL)
322
 
                return 0;
323
 
 
324
 
        rb = (struct _CamelTextIndexRoot *) p->blocks->root;
325
 
 
326
 
        /* sync/flush word cache */
327
 
 
328
 
        CAMEL_TEXT_INDEX_LOCK (idx, lock);
329
 
 
330
 
        /* we sync, bump down the cache limits since we dont need them for reading */
331
 
        p->blocks->block_cache_limit = 128;
332
 
        /* this doesn't really need to be dropped, its only used in updates anyway */
333
 
        p->word_cache_limit = 1024;
334
 
 
335
 
        while ((ww = (struct _CamelTextIndexWord *) camel_dlist_remhead (&p->word_cache))) {
336
 
                if (ww->used > 0) {
337
 
                        io (printf ("writing key file entry '%s' [%x]\n", ww->word, ww->data));
338
 
                        if (camel_key_file_write (p->links, &ww->data, ww->used, ww->names) != -1) {
339
 
                                io (printf ("  new data [%x]\n", ww->data));
340
 
                                rb->keys++;
341
 
                                camel_block_file_touch_block (p->blocks, p->blocks->root_block);
342
 
                                camel_key_table_set_data (
343
 
                                        p->word_index, ww->wordid, ww->data);
344
 
                        } else {
345
 
                                ret = -1;
346
 
                        }
347
 
                        ww->used = 0;
348
 
                }
349
 
                g_hash_table_remove (p->words, ww->word);
350
 
                g_free (ww->word);
351
 
                g_free (ww);
352
 
        }
353
 
 
354
 
        if (camel_key_table_sync (p->word_index) == -1
355
 
            || camel_key_table_sync (p->name_index) == -1
356
 
            || camel_partition_table_sync (p->word_hash) == -1
357
 
            || camel_partition_table_sync (p->name_hash) == -1)
358
 
                ret = -1;
359
 
 
360
 
        /* only do the frag/compress check if we did some new writes on this index */
361
 
        wfrag = rb->words ? (((rb->keys - rb->words) * 100)/ rb->words) : 0;
362
 
        nfrag = rb->names ? ((rb->deleted * 100) / rb->names) : 0;
363
 
        d (printf ("  words = %d, keys = %d\n", rb->words, rb->keys));
364
 
 
365
 
        if (ret == 0) {
366
 
                if (wfrag > 30 || nfrag > 20)
367
 
                        ret = text_index_compress_nosync (idx);
368
 
        }
369
 
 
370
 
        ret = camel_block_file_sync (p->blocks);
371
 
 
372
 
        CAMEL_TEXT_INDEX_UNLOCK (idx, lock);
373
 
 
374
 
        return ret;
375
 
}
376
 
 
377
 
static void tmp_name (const gchar *in, gchar *o)
378
 
{
379
 
        gchar *s;
380
 
 
381
 
        s = strrchr (in, '/');
382
 
        if (s) {
383
 
                memcpy (o, in, s - in + 1);
384
 
                memcpy (o+(s-in+1), ".#", 2);
385
 
                strcpy (o + (s - in + 3), s + 1);
386
 
        } else {
387
 
                sprintf (o, ".#%s", in);
388
 
        }
389
 
}
390
 
 
391
 
static gint
392
 
text_index_compress (CamelIndex *idx)
393
 
{
394
 
        gint ret;
395
 
 
396
 
        CAMEL_TEXT_INDEX_LOCK (idx, lock);
397
 
 
398
 
        ret = camel_index_sync (idx);
399
 
        if (ret != -1)
400
 
                ret = text_index_compress_nosync (idx);
401
 
 
402
 
        CAMEL_TEXT_INDEX_UNLOCK (idx, lock);
403
 
 
404
 
        return ret;
405
 
}
406
 
 
407
 
/* Attempt to recover index space by compressing the indices */
408
 
static gint
409
 
text_index_compress_nosync (CamelIndex *idx)
410
 
{
411
 
        CamelTextIndex *newidx;
412
 
        CamelTextIndexPrivate *newp, *oldp;
413
 
        camel_key_t oldkeyid, newkeyid;
414
 
        GHashTable *remap;
415
 
        guint deleted;
416
 
        camel_block_t data, newdata;
417
 
        gint i, ret = -1;
418
 
        gchar *name = NULL;
419
 
        guint flags;
420
 
        gchar *newpath, *savepath, *oldpath;
421
 
        gsize count, newcount;
422
 
        camel_key_t *records, newrecords[256];
423
 
        struct _CamelTextIndexRoot *rb;
424
 
 
425
 
        i = strlen (idx->path) + 16;
426
 
        oldpath = alloca (i);
427
 
        newpath = alloca (i);
428
 
        savepath = alloca (i);
429
 
 
430
 
        strcpy (oldpath, idx->path);
431
 
        oldpath[strlen (oldpath)-strlen (".index")] = 0;
432
 
 
433
 
        tmp_name (oldpath, newpath);
434
 
        sprintf (savepath, "%s~", oldpath);
435
 
 
436
 
        d (printf ("Old index: %s\n", idx->path));
437
 
        d (printf ("Old path: %s\n", oldpath));
438
 
        d (printf ("New: %s\n", newpath));
439
 
        d (printf ("Save: %s\n", savepath));
440
 
 
441
 
        newidx = camel_text_index_new (newpath, O_RDWR | O_CREAT);
442
 
        if (newidx == NULL)
443
 
                return -1;
444
 
 
445
 
        newp = CAMEL_TEXT_INDEX (newidx)->priv;
446
 
        oldp = CAMEL_TEXT_INDEX (idx)->priv;
447
 
 
448
 
        CAMEL_TEXT_INDEX_LOCK (idx, lock);
449
 
 
450
 
        rb = (struct _CamelTextIndexRoot *) newp->blocks->root;
451
 
 
452
 
        rb->words = 0;
453
 
        rb->names = 0;
454
 
        rb->deleted = 0;
455
 
        rb->keys = 0;
456
 
 
457
 
        /* Process:
458
 
         * For each name we still have:
459
 
         * Add it to the new index & setup remap table
460
 
         *
461
 
         * For each word:
462
 
         * Copy word's data to a new file
463
 
         * Add new word to index (*) (can we just copy blocks?) */
464
 
 
465
 
        /* Copy undeleted names to new index file, creating new indices */
466
 
        io (printf ("Copying undeleted names to new file\n"));
467
 
        remap = g_hash_table_new (NULL, NULL);
468
 
        oldkeyid = 0;
469
 
        deleted = 0;
470
 
        while ((oldkeyid = camel_key_table_next (oldp->name_index, oldkeyid, &name, &flags, &data))) {
471
 
                if ((flags&1) == 0) {
472
 
                        io (printf ("copying name '%s'\n", name));
473
 
                        newkeyid = camel_key_table_add (
474
 
                                newp->name_index, name, data, flags);
475
 
                        if (newkeyid == 0)
476
 
                                goto fail;
477
 
                        rb->names++;
478
 
                        camel_partition_table_add (
479
 
                                newp->name_hash, name, newkeyid);
480
 
                        g_hash_table_insert (remap, GINT_TO_POINTER (oldkeyid), GINT_TO_POINTER (newkeyid));
481
 
                } else {
482
 
                        io (printf ("deleted name '%s'\n", name));
483
 
                }
484
 
                g_free (name);
485
 
                name = NULL;
486
 
                deleted |= flags;
487
 
        }
488
 
 
489
 
        /* Copy word data across, remapping/deleting and create new index for it */
490
 
        /* We re-block the data into 256 entry lots while we're at it, since we only
491
 
         * have to do 1 at a time and its cheap */
492
 
        oldkeyid = 0;
493
 
        while ((oldkeyid = camel_key_table_next (oldp->word_index, oldkeyid, &name, &flags, &data))) {
494
 
                io(printf ("copying word '%s'\n", name));
495
 
                newdata = 0;
496
 
                newcount = 0;
497
 
                if (data) {
498
 
                        rb->words++;
499
 
                        rb->keys++;
500
 
                }
501
 
                while (data) {
502
 
                        if (camel_key_file_read (oldp->links, &data, &count, &records) == -1) {
503
 
                                io (printf ("could not read from old keys at %d for word '%s'\n", (gint)data, name));
504
 
                                goto fail;
505
 
                        }
506
 
                        for (i = 0; i < count; i++) {
507
 
                                newkeyid = (camel_key_t) GPOINTER_TO_INT (g_hash_table_lookup (remap, GINT_TO_POINTER (records[i])));
508
 
                                if (newkeyid) {
509
 
                                        newrecords[newcount++] = newkeyid;
510
 
                                        if (newcount == G_N_ELEMENTS (newrecords)) {
511
 
                                                if (camel_key_file_write (newp->links, &newdata, newcount, newrecords) == -1) {
512
 
                                                        g_free (records);
513
 
                                                        goto fail;
514
 
                                                }
515
 
                                                newcount = 0;
516
 
                                        }
517
 
                                }
518
 
                        }
519
 
                        g_free (records);
520
 
                }
521
 
 
522
 
                if (newcount > 0) {
523
 
                        if (camel_key_file_write (newp->links, &newdata, newcount, newrecords) == -1)
524
 
                                goto fail;
525
 
                }
526
 
 
527
 
                if (newdata != 0) {
528
 
                        newkeyid = camel_key_table_add (
529
 
                                newp->word_index, name, newdata, flags);
530
 
                        if (newkeyid == 0)
531
 
                                goto fail;
532
 
                        camel_partition_table_add (
533
 
                                newp->word_hash, name, newkeyid);
534
 
                }
535
 
                g_free (name);
536
 
                name = NULL;
537
 
        }
538
 
 
539
 
        camel_block_file_touch_block (newp->blocks, newp->blocks->root_block);
540
 
 
541
 
        if (camel_index_sync (CAMEL_INDEX (newidx)) == -1)
542
 
                goto fail;
543
 
 
544
 
        /* Rename underlying files to match */
545
 
        ret = camel_index_rename (idx, savepath);
546
 
        if (ret == -1)
547
 
                goto fail;
548
 
 
549
 
        /* If this fails, we'll pick up something during restart? */
550
 
        ret = camel_index_rename ((CamelIndex *) newidx, oldpath);
551
 
 
552
 
#define myswap(a, b) { gpointer tmp = a; a = b; b = tmp; }
553
 
        /* Poke the private data across to the new object */
554
 
        /* And change the fd's over, etc? */
555
 
        /* Yes: This is a hack */
556
 
        myswap (newp->blocks, oldp->blocks);
557
 
        myswap (newp->links, oldp->links);
558
 
        myswap (newp->word_index, oldp->word_index);
559
 
        myswap (newp->word_hash, oldp->word_hash);
560
 
        myswap (newp->name_index, oldp->name_index);
561
 
        myswap (newp->name_hash, oldp->name_hash);
562
 
        myswap (((CamelIndex *) newidx)->path, ((CamelIndex *) idx)->path);
563
 
#undef myswap
564
 
 
565
 
        ret = 0;
566
 
fail:
567
 
        CAMEL_TEXT_INDEX_UNLOCK (idx, lock);
568
 
 
569
 
        camel_index_delete ((CamelIndex *) newidx);
570
 
 
571
 
        g_object_unref (newidx);
572
 
        g_free (name);
573
 
        g_hash_table_destroy (remap);
574
 
 
575
 
        /* clean up temp files always */
576
 
        sprintf (savepath, "%s~.index", oldpath);
577
 
        g_unlink (savepath);
578
 
        sprintf (newpath, "%s.data", savepath);
579
 
        g_unlink (newpath);
580
 
 
581
 
        return ret;
582
 
}
583
 
 
584
 
static gint
585
 
text_index_delete (CamelIndex *idx)
586
 
{
587
 
        CamelTextIndexPrivate *p = CAMEL_TEXT_INDEX (idx)->priv;
588
 
        gint ret = 0;
589
 
 
590
 
        if (camel_block_file_delete (p->blocks) == -1)
591
 
                ret = -1;
592
 
        if (camel_key_file_delete (p->links) == -1)
593
 
                ret = -1;
594
 
 
595
 
        return ret;
596
 
}
597
 
 
598
 
static gint
599
 
text_index_rename (CamelIndex *idx,
600
 
                   const gchar *path)
601
 
{
602
 
        CamelTextIndexPrivate *p = CAMEL_TEXT_INDEX (idx)->priv;
603
 
        gchar *newlink, *newblock;
604
 
        gint err, ret;
605
 
 
606
 
        CAMEL_TEXT_INDEX_LOCK (idx, lock);
607
 
 
608
 
        newblock = alloca (strlen (path) + 8);
609
 
        sprintf (newblock, "%s.index", path);
610
 
        ret = camel_block_file_rename (p->blocks, newblock);
611
 
        if (ret == -1) {
612
 
                CAMEL_TEXT_INDEX_UNLOCK (idx, lock);
613
 
                return -1;
614
 
        }
615
 
 
616
 
        newlink = alloca (strlen (path) + 16);
617
 
        sprintf (newlink, "%s.index.data", path);
618
 
        ret = camel_key_file_rename (p->links, newlink);
619
 
        if (ret == -1) {
620
 
                err = errno;
621
 
                camel_block_file_rename (p->blocks, idx->path);
622
 
                CAMEL_TEXT_INDEX_UNLOCK (idx, lock);
623
 
                errno = err;
624
 
                return -1;
625
 
        }
626
 
 
627
 
        g_free (idx->path);
628
 
        idx->path = g_strdup (newblock);
629
 
 
630
 
        CAMEL_TEXT_INDEX_UNLOCK (idx, lock);
631
 
 
632
 
        return 0;
633
 
}
634
 
 
635
 
static gint
636
 
text_index_has_name (CamelIndex *idx,
637
 
                     const gchar *name)
638
 
{
639
 
        CamelTextIndexPrivate *p = CAMEL_TEXT_INDEX (idx)->priv;
640
 
 
641
 
        return camel_partition_table_lookup (p->name_hash, name) != 0;
642
 
}
643
 
 
644
 
static CamelIndexName *
645
 
text_index_add_name (CamelIndex *idx,
646
 
                     const gchar *name)
647
 
{
648
 
        CamelTextIndexPrivate *p = CAMEL_TEXT_INDEX (idx)->priv;
649
 
        camel_key_t keyid;
650
 
        CamelIndexName *idn;
651
 
        struct _CamelTextIndexRoot *rb = (struct _CamelTextIndexRoot *) p->blocks->root;
652
 
 
653
 
        CAMEL_TEXT_INDEX_LOCK (idx, lock);
654
 
 
655
 
        /* if we're adding words, up the cache limits a lot */
656
 
        if (p->blocks) {
657
 
                p->blocks->block_cache_limit = 1024;
658
 
                p->word_cache_limit = 8192;
659
 
        }
660
 
 
661
 
        /* If we have it already replace it */
662
 
        keyid = camel_partition_table_lookup (p->name_hash, name);
663
 
        if (keyid != 0) {
664
 
                /* TODO: We could just update the partition table's
665
 
                 * key pointer rather than having to delete it */
666
 
                rb->deleted++;
667
 
                camel_key_table_set_flags (p->name_index, keyid, 1, 1);
668
 
                camel_partition_table_remove (p->name_hash, name);
669
 
        }
670
 
 
671
 
        keyid = camel_key_table_add (p->name_index, name, 0, 0);
672
 
        if (keyid != 0) {
673
 
                camel_partition_table_add (p->name_hash, name, keyid);
674
 
                rb->names++;
675
 
        }
676
 
 
677
 
        camel_block_file_touch_block (p->blocks, p->blocks->root_block);
678
 
 
679
 
        /* TODO: if keyid == 0, we had a failure, we should somehow flag that, but for
680
 
         * now just return a valid object but discard its results, see text_index_write_name */
681
 
 
682
 
        CAMEL_TEXT_INDEX_UNLOCK (idx, lock);
683
 
 
684
 
        idn = (CamelIndexName *) camel_text_index_name_new ((CamelTextIndex *) idx, name, keyid);
685
 
 
686
 
        return idn;
687
 
}
688
 
 
689
 
/* call locked */
690
 
static void
691
 
hash_write_word (gchar *word,
692
 
                 gpointer data,
693
 
                 CamelIndexName *idn)
694
 
{
695
 
        CamelTextIndexName *tin = (CamelTextIndexName *) idn;
696
 
 
697
 
        text_index_add_name_to_word (idn->index, word, tin->priv->nameid);
698
 
}
699
 
 
700
 
static gint
701
 
text_index_write_name (CamelIndex *idx,
702
 
                       CamelIndexName *idn)
703
 
{
704
 
        /* force 'flush' of any outstanding data */
705
 
        camel_index_name_add_buffer (idn, NULL, 0);
706
 
 
707
 
        /* see text_index_add_name for when this can be 0 */
708
 
        if (((CamelTextIndexName *) idn)->priv->nameid != 0) {
709
 
                CAMEL_TEXT_INDEX_LOCK (idx, lock);
710
 
 
711
 
                g_hash_table_foreach (idn->words, (GHFunc) hash_write_word, idn);
712
 
 
713
 
                CAMEL_TEXT_INDEX_UNLOCK (idx, lock);
714
 
        }
715
 
 
716
 
        return 0;
717
 
}
718
 
 
719
 
static CamelIndexCursor *
720
 
text_index_find_name (CamelIndex *idx,
721
 
                      const gchar *name)
722
 
{
723
 
        /* what was this for, umm */
724
 
        return NULL;
725
 
}
726
 
 
727
 
static void
728
 
text_index_delete_name (CamelIndex *idx,
729
 
                        const gchar *name)
730
 
{
731
 
        CamelTextIndexPrivate *p = CAMEL_TEXT_INDEX (idx)->priv;
732
 
        camel_key_t keyid;
733
 
        struct _CamelTextIndexRoot *rb = (struct _CamelTextIndexRoot *) p->blocks->root;
734
 
 
735
 
        d (printf ("Delete name: %s\n", name));
736
 
 
737
 
        /* probably doesn't really need locking, but oh well */
738
 
        CAMEL_TEXT_INDEX_LOCK (idx, lock);
739
 
 
740
 
        /* We just mark the key deleted, and remove it from the hash table */
741
 
        keyid = camel_partition_table_lookup (p->name_hash, name);
742
 
        if (keyid != 0) {
743
 
                rb->deleted++;
744
 
                camel_block_file_touch_block (p->blocks, p->blocks->root_block);
745
 
                camel_key_table_set_flags (p->name_index, keyid, 1, 1);
746
 
                camel_partition_table_remove (p->name_hash, name);
747
 
        }
748
 
 
749
 
        CAMEL_TEXT_INDEX_UNLOCK (idx, lock);
750
 
}
751
 
 
752
 
static CamelIndexCursor *
753
 
text_index_find (CamelIndex *idx,
754
 
                 const gchar *word)
755
 
{
756
 
        CamelTextIndexPrivate *p = CAMEL_TEXT_INDEX (idx)->priv;
757
 
        camel_key_t keyid;
758
 
        camel_block_t data = 0;
759
 
        guint flags;
760
 
        CamelIndexCursor *idc;
761
 
 
762
 
        CAMEL_TEXT_INDEX_LOCK (idx, lock);
763
 
 
764
 
        keyid = camel_partition_table_lookup (p->word_hash, word);
765
 
        if (keyid != 0) {
766
 
                data = camel_key_table_lookup (
767
 
                        p->word_index, keyid, NULL, &flags);
768
 
                if (flags & 1)
769
 
                        data = 0;
770
 
        }
771
 
 
772
 
        CAMEL_TEXT_INDEX_UNLOCK (idx, lock);
773
 
 
774
 
        idc = (CamelIndexCursor *) camel_text_index_cursor_new ((CamelTextIndex *) idx, data);
775
 
 
776
 
        return idc;
777
 
}
778
 
 
779
 
static CamelIndexCursor *
780
 
text_index_words (CamelIndex *idx)
781
 
{
782
 
        CamelTextIndexPrivate *p = CAMEL_TEXT_INDEX (idx)->priv;
783
 
 
784
 
        return (CamelIndexCursor *) camel_text_index_key_cursor_new ((CamelTextIndex *) idx, p->word_index);
785
 
}
786
 
 
787
 
static CamelIndexCursor *
788
 
text_index_names (CamelIndex *idx)
789
 
{
790
 
        CamelTextIndexPrivate *p = CAMEL_TEXT_INDEX (idx)->priv;
791
 
 
792
 
        return (CamelIndexCursor *) camel_text_index_key_cursor_new ((CamelTextIndex *) idx, p->name_index);
793
 
}
794
 
 
795
 
static void
796
 
camel_text_index_class_init (CamelTextIndexClass *class)
797
 
{
798
 
        GObjectClass *object_class;
799
 
        CamelIndexClass *index_class;
800
 
 
801
 
        g_type_class_add_private (class, sizeof (CamelTextIndexPrivate));
802
 
 
803
 
        object_class = G_OBJECT_CLASS (class);
804
 
        object_class->dispose = text_index_dispose;
805
 
        object_class->finalize = text_index_finalize;
806
 
 
807
 
        index_class = CAMEL_INDEX_CLASS (class);
808
 
        index_class->sync = text_index_sync;
809
 
        index_class->compress = text_index_compress;
810
 
        index_class->delete = text_index_delete;
811
 
        index_class->rename = text_index_rename;
812
 
        index_class->has_name = text_index_has_name;
813
 
        index_class->add_name = text_index_add_name;
814
 
        index_class->write_name = text_index_write_name;
815
 
        index_class->find_name = text_index_find_name;
816
 
        index_class->delete_name = text_index_delete_name;
817
 
        index_class->find = text_index_find;
818
 
        index_class->words = text_index_words;
819
 
        index_class->names = text_index_names;
820
 
}
821
 
 
822
 
static void
823
 
camel_text_index_init (CamelTextIndex *text_index)
824
 
{
825
 
        text_index->priv = G_TYPE_INSTANCE_GET_PRIVATE (
826
 
                text_index, CAMEL_TYPE_TEXT_INDEX, CamelTextIndexPrivate);
827
 
 
828
 
        camel_dlist_init (&text_index->priv->word_cache);
829
 
        text_index->priv->words = g_hash_table_new (g_str_hash, g_str_equal);
830
 
        text_index->priv->word_cache_count = 0;
831
 
 
832
 
        /* This cache size and the block cache size have been tuned for
833
 
         * about the best with moderate memory usage.  Doubling the memory
834
 
         * usage barely affects performance. */
835
 
        text_index->priv->word_cache_limit = 4096; /* 1024 = 128K */
836
 
 
837
 
        g_static_rec_mutex_init (&text_index->priv->lock);
838
 
}
839
 
 
840
 
static gchar *
841
 
text_index_normalize (CamelIndex *idx,
842
 
                      const gchar *in,
843
 
                      gpointer data)
844
 
{
845
 
        gchar *word;
846
 
 
847
 
        /* Sigh, this is really expensive */
848
 
        /*g_utf8_normalize (in, strlen (in), G_NORMALIZE_ALL);*/
849
 
        word = g_utf8_strdown (in, -1);
850
 
 
851
 
        return word;
852
 
}
853
 
 
854
 
CamelTextIndex *
855
 
camel_text_index_new (const gchar *path,
856
 
                      gint flags)
857
 
{
858
 
        CamelTextIndex *idx = g_object_new (CAMEL_TYPE_TEXT_INDEX, NULL);
859
 
        CamelTextIndexPrivate *p = idx->priv;
860
 
        struct _CamelTextIndexRoot *rb;
861
 
        gchar *link;
862
 
        CamelBlock *bl;
863
 
 
864
 
        camel_index_construct ((CamelIndex *) idx, path, flags);
865
 
        camel_index_set_normalize ((CamelIndex *) idx, text_index_normalize, NULL);
866
 
 
867
 
        p->blocks = camel_block_file_new (
868
 
                idx->parent.path, flags, CAMEL_TEXT_INDEX_VERSION, CAMEL_BLOCK_SIZE);
869
 
        if (p->blocks == NULL)
870
 
                goto fail;
871
 
 
872
 
        link = alloca (strlen (idx->parent.path) + 7);
873
 
        sprintf (link, "%s.data", idx->parent.path);
874
 
        p->links = camel_key_file_new (link, flags, CAMEL_TEXT_INDEX_KEY_VERSION);
875
 
 
876
 
        if (p->links == NULL)
877
 
                goto fail;
878
 
 
879
 
        rb = (struct _CamelTextIndexRoot *) p->blocks->root;
880
 
 
881
 
        if (rb->word_index_root == 0) {
882
 
                bl = camel_block_file_new_block (p->blocks);
883
 
 
884
 
                if (bl == NULL)
885
 
                        goto fail;
886
 
 
887
 
                rb->word_index_root = bl->id;
888
 
                camel_block_file_unref_block (p->blocks, bl);
889
 
                camel_block_file_touch_block (p->blocks, p->blocks->root_block);
890
 
        }
891
 
 
892
 
        if (rb->word_hash_root == 0) {
893
 
                bl = camel_block_file_new_block (p->blocks);
894
 
 
895
 
                if (bl == NULL)
896
 
                        goto fail;
897
 
 
898
 
                rb->word_hash_root = bl->id;
899
 
                camel_block_file_unref_block (p->blocks, bl);
900
 
                camel_block_file_touch_block (p->blocks, p->blocks->root_block);
901
 
        }
902
 
 
903
 
        if (rb->name_index_root == 0) {
904
 
                bl = camel_block_file_new_block (p->blocks);
905
 
 
906
 
                if (bl == NULL)
907
 
                        goto fail;
908
 
 
909
 
                rb->name_index_root = bl->id;
910
 
                camel_block_file_unref_block (p->blocks, bl);
911
 
                camel_block_file_touch_block (p->blocks, p->blocks->root_block);
912
 
        }
913
 
 
914
 
        if (rb->name_hash_root == 0) {
915
 
                bl = camel_block_file_new_block (p->blocks);
916
 
 
917
 
                if (bl == NULL)
918
 
                        goto fail;
919
 
 
920
 
                rb->name_hash_root = bl->id;
921
 
                camel_block_file_unref_block (p->blocks, bl);
922
 
                camel_block_file_touch_block (p->blocks, p->blocks->root_block);
923
 
        }
924
 
 
925
 
        p->word_index = camel_key_table_new (p->blocks, rb->word_index_root);
926
 
        p->word_hash = camel_partition_table_new (p->blocks, rb->word_hash_root);
927
 
        p->name_index = camel_key_table_new (p->blocks, rb->name_index_root);
928
 
        p->name_hash = camel_partition_table_new (p->blocks, rb->name_hash_root);
929
 
 
930
 
        if (p->word_index == NULL || p->word_hash == NULL
931
 
            || p->name_index == NULL || p->name_hash == NULL) {
932
 
                g_object_unref (idx);
933
 
                idx = NULL;
934
 
        }
935
 
 
936
 
        return idx;
937
 
 
938
 
fail:
939
 
        g_object_unref (idx);
940
 
        return NULL;
941
 
}
942
 
 
943
 
/* returns 0 if the index exists, is valid, and synced, -1 otherwise */
944
 
gint
945
 
camel_text_index_check (const gchar *path)
946
 
{
947
 
        gchar *block, *key;
948
 
        CamelBlockFile *blocks;
949
 
        CamelKeyFile *keys;
950
 
 
951
 
        block = alloca (strlen (path) + 7);
952
 
        sprintf (block, "%s.index", path);
953
 
        blocks = camel_block_file_new (block, O_RDONLY, CAMEL_TEXT_INDEX_VERSION, CAMEL_BLOCK_SIZE);
954
 
        if (blocks == NULL) {
955
 
                io (printf ("Check failed: No block file: %s\n", g_strerror (errno)));
956
 
                return -1;
957
 
        }
958
 
        key = alloca (strlen (path) + 12);
959
 
        sprintf (key, "%s.index.data", path);
960
 
        keys = camel_key_file_new (key, O_RDONLY, CAMEL_TEXT_INDEX_KEY_VERSION);
961
 
        if (keys == NULL) {
962
 
                io (printf ("Check failed: No key file: %s\n", g_strerror (errno)));
963
 
                g_object_unref (blocks);
964
 
                return -1;
965
 
        }
966
 
 
967
 
        g_object_unref (keys);
968
 
        g_object_unref (blocks);
969
 
 
970
 
        return 0;
971
 
}
972
 
 
973
 
gint
974
 
camel_text_index_rename (const gchar *old,
975
 
                         const gchar *new)
976
 
{
977
 
        gchar *oldname, *newname;
978
 
        gint err;
979
 
 
980
 
        /* TODO: camel_text_index_rename should find out if we have an active index and use that instead */
981
 
 
982
 
        oldname = alloca (strlen (old) + 12);
983
 
        newname = alloca (strlen (new) + 12);
984
 
        sprintf (oldname, "%s.index", old);
985
 
        sprintf (newname, "%s.index", new);
986
 
 
987
 
        if (g_rename (oldname, newname) == -1 && errno != ENOENT)
988
 
                return -1;
989
 
 
990
 
        sprintf (oldname, "%s.index.data", old);
991
 
        sprintf (newname, "%s.index.data", new);
992
 
 
993
 
        if (g_rename (oldname, newname) == -1 && errno != ENOENT) {
994
 
                err = errno;
995
 
                sprintf (oldname, "%s.index", old);
996
 
                sprintf (newname, "%s.index", new);
997
 
                g_rename (newname, oldname);
998
 
                errno = err;
999
 
                return -1;
1000
 
        }
1001
 
 
1002
 
        return 0;
1003
 
}
1004
 
 
1005
 
gint
1006
 
camel_text_index_remove (const gchar *old)
1007
 
{
1008
 
        gchar *block, *key;
1009
 
        gint ret = 0;
1010
 
 
1011
 
        /* TODO: needs to poke any active indices to remain unlinked */
1012
 
 
1013
 
        block = alloca (strlen (old) + 12);
1014
 
        key = alloca (strlen (old) + 12);
1015
 
        sprintf (block, "%s.index", old);
1016
 
        sprintf (key, "%s.index.data", old);
1017
 
 
1018
 
        if (g_unlink (block) == -1 && errno != ENOENT && errno != ENOTDIR)
1019
 
                ret = -1;
1020
 
        if (g_unlink (key) == -1 && errno != ENOENT && errno != ENOTDIR)
1021
 
                ret = -1;
1022
 
 
1023
 
        if (ret == 0)
1024
 
                errno = 0;
1025
 
 
1026
 
        return ret;
1027
 
}
1028
 
 
1029
 
/* Debug */
1030
 
void
1031
 
camel_text_index_info (CamelTextIndex *idx)
1032
 
{
1033
 
        CamelTextIndexPrivate *p = idx->priv;
1034
 
        struct _CamelTextIndexRoot *rb = (struct _CamelTextIndexRoot *) p->blocks->root;
1035
 
        gint frag;
1036
 
 
1037
 
        printf ("Path: '%s'\n", idx->parent.path);
1038
 
        printf ("Version: %u\n", idx->parent.version);
1039
 
        printf ("Flags: %08x\n", idx->parent.flags);
1040
 
        printf ("Total words: %u\n", rb->words);
1041
 
        printf ("Total names: %u\n", rb->names);
1042
 
        printf ("Total deleted: %u\n", rb->deleted);
1043
 
        printf ("Total key blocks: %u\n", rb->keys);
1044
 
 
1045
 
        if (rb->words > 0) {
1046
 
                frag = ((rb->keys - rb->words) * 100)/ rb->words;
1047
 
                printf ("Word fragmentation: %d%%\n", frag);
1048
 
        }
1049
 
 
1050
 
        if (rb->names > 0) {
1051
 
                frag = (rb->deleted * 100)/ rb->names;
1052
 
                printf ("Name fragmentation: %d%%\n", frag);
1053
 
        }
1054
 
}
1055
 
 
1056
 
/* #define DUMP_RAW */
1057
 
 
1058
 
#ifdef DUMP_RAW
1059
 
enum { KEY_ROOT = 1, KEY_DATA = 2, PARTITION_MAP = 4, PARTITION_DATA = 8 };
1060
 
 
1061
 
static void
1062
 
add_type (GHashTable *map,
1063
 
          camel_block_t id,
1064
 
          gint type)
1065
 
{
1066
 
        camel_block_t old;
1067
 
 
1068
 
        old = g_hash_table_lookup (map, id);
1069
 
        if (old == type)
1070
 
                return;
1071
 
 
1072
 
        if (old != 0 && old != type)
1073
 
                g_warning ("block %x redefined as type %d, already type %d\n", id, type, old);
1074
 
        g_hash_table_insert (map, id, GINT_TO_POINTER (type | old));
1075
 
}
1076
 
 
1077
 
static void
1078
 
add_partition (GHashTable *map,
1079
 
               CamelBlockFile *blocks,
1080
 
               camel_block_t id)
1081
 
{
1082
 
        CamelBlock *bl;
1083
 
        CamelPartitionMapBlock *pm;
1084
 
        gint i;
1085
 
 
1086
 
        while (id) {
1087
 
                add_type (map, id, PARTITION_MAP);
1088
 
                bl = camel_block_file_get_block (blocks, id);
1089
 
                if (bl == NULL) {
1090
 
                        g_warning ("couldn't get parition: %x\n", id);
1091
 
                        return;
1092
 
                }
1093
 
 
1094
 
                pm = (CamelPartitionMapBlock *) &bl->data;
1095
 
                if (pm->used > G_N_ELEMENTS (pm->partition)) {
1096
 
                        g_warning ("Partition block %x invalid\n", id);
1097
 
                        camel_block_file_unref_block (blocks, bl);
1098
 
                        return;
1099
 
                }
1100
 
 
1101
 
                for (i = 0; i < pm->used; i++)
1102
 
                        add_type (map, pm->partition[i].blockid, PARTITION_DATA);
1103
 
 
1104
 
                id = pm->next;
1105
 
                camel_block_file_unref_block (blocks, bl);
1106
 
        }
1107
 
}
1108
 
 
1109
 
static void
1110
 
add_keys (GHashTable *map,
1111
 
          CamelBlockFile *blocks,
1112
 
          camel_block_t id)
1113
 
{
1114
 
        CamelBlock *rbl, *bl;
1115
 
        CamelKeyRootBlock *root;
1116
 
        CamelKeyBlock *kb;
1117
 
 
1118
 
        add_type (map, id, KEY_ROOT);
1119
 
        rbl = camel_block_file_get_block (blocks, id);
1120
 
        if (rbl == NULL) {
1121
 
                g_warning ("couldn't get key root: %x\n", id);
1122
 
                return;
1123
 
        }
1124
 
        root = (CamelKeyRootBlock *) &rbl->data;
1125
 
        id = root->first;
1126
 
 
1127
 
        while (id) {
1128
 
                add_type (map, id, KEY_DATA);
1129
 
                bl = camel_block_file_get_block (blocks, id);
1130
 
                if (bl == NULL) {
1131
 
                        g_warning ("couldn't get key: %x\n", id);
1132
 
                        break;
1133
 
                }
1134
 
 
1135
 
                kb = (CamelKeyBlock *) &bl->data;
1136
 
                id = kb->next;
1137
 
                camel_block_file_unref_block (blocks, bl);
1138
 
        }
1139
 
 
1140
 
        camel_block_file_unref_block (blocks, rbl);
1141
 
}
1142
 
 
1143
 
static void
1144
 
dump_raw (GHashTable *map,
1145
 
          gchar *path)
1146
 
{
1147
 
        gchar buf[1024];
1148
 
        gchar line[256];
1149
 
        gchar *p, c, *e, *a, *o;
1150
 
        gint v, n, len, i, type;
1151
 
        gchar hex[16] = "0123456789ABCDEF";
1152
 
        gint fd;
1153
 
        camel_block_t id, total;
1154
 
 
1155
 
        fd = g_open (path, O_RDONLY | O_BINARY, 0);
1156
 
        if (fd == -1)
1157
 
                return;
1158
 
 
1159
 
        total = 0;
1160
 
        while ((len = read (fd, buf, 1024)) == 1024) {
1161
 
                id = total;
1162
 
 
1163
 
                type = g_hash_table_lookup (map, id);
1164
 
                switch (type) {
1165
 
                case 0:
1166
 
                        printf (" - unknown -\n");
1167
 
                        break;
1168
 
                default:
1169
 
                        printf (" - invalid -\n");
1170
 
                        break;
1171
 
                case KEY_ROOT: {
1172
 
                        CamelKeyRootBlock *r = (CamelKeyRootBlock *) buf;
1173
 
                        printf ("Key root:\n");
1174
 
                        printf ("First: %08x     Last: %08x     Free: %08x\n", r->first, r->last, r->free);
1175
 
                } break;
1176
 
                case KEY_DATA: {
1177
 
                        CamelKeyBlock *k = (CamelKeyBlock *) buf;
1178
 
                        printf ("Key data:\n");
1179
 
                        printf ("Next: %08x      Used: %u\n", k->next, k->used);
1180
 
                        for (i = 0; i < k->used; i++) {
1181
 
                                if (i == 0)
1182
 
                                        len = sizeof (k->u.keydata);
1183
 
                                else
1184
 
                                        len = k->u.keys[i - 1].offset;
1185
 
                                len -= k->u.keys[i].offset;
1186
 
                                printf ("[%03d]: %08x %5d %06x %3d '%.*s'\n", i,
1187
 
                                       k->u.keys[i].data, k->u.keys[i].offset, k->u.keys[i].flags,
1188
 
                                       len, len, k->u.keydata + k->u.keys[i].offset);
1189
 
                        }
1190
 
                } break;
1191
 
                case PARTITION_MAP: {
1192
 
                        CamelPartitionMapBlock *m = (CamelPartitionMapBlock *) buf;
1193
 
                        printf ("Partition map\n");
1194
 
                        printf ("Next: %08x      Used: %u\n", m->next, m->used);
1195
 
                        for (i = 0; i < m->used; i++) {
1196
 
                                printf ("[%03d]: %08x -> %08x\n", i, m->partition[i].hashid, m->partition[i].blockid);
1197
 
                        }
1198
 
                } break;
1199
 
                case PARTITION_DATA: {
1200
 
                        CamelPartitionKeyBlock *k = (CamelPartitionKeyBlock *) buf;
1201
 
                        printf ("Partition data\n");
1202
 
                        printf ("Used: %u\n", k->used);
1203
 
                } break;
1204
 
                }
1205
 
 
1206
 
                printf ("--raw--\n");
1207
 
 
1208
 
                len = 1024;
1209
 
                p = buf;
1210
 
                do {
1211
 
                        sprintf (line, "%08x:                                                                      ", total);
1212
 
                        total += 16;
1213
 
                        o = line + 10;
1214
 
                        a = o + 16 * 2 + 2;
1215
 
                        i = 0;
1216
 
                        while (len && i < 16) {
1217
 
                                c = *p++;
1218
 
                                *a++ = isprint (c)?c:'.';
1219
 
                                *o++ = hex[(c>>4)&0x0f];
1220
 
                                *o++ = hex[c&0x0f];
1221
 
                                i++;
1222
 
                                if (i == 8)
1223
 
                                        *o++ = ' ';
1224
 
                                len--;
1225
 
                        }
1226
 
                        *a = 0;
1227
 
                        printf ("%s\n", line);
1228
 
                } while (len);
1229
 
                printf ("\n");
1230
 
        }
1231
 
        close (fd);
1232
 
}
1233
 
#endif
1234
 
 
1235
 
/* Debug */
1236
 
void
1237
 
camel_text_index_dump (CamelTextIndex *idx)
1238
 
{
1239
 
        CamelTextIndexPrivate *p = idx->priv;
1240
 
#ifndef DUMP_RAW
1241
 
        camel_key_t keyid;
1242
 
        gchar *word;
1243
 
        const gchar *name;
1244
 
        guint flags;
1245
 
        camel_block_t data;
1246
 
 
1247
 
        /* Iterate over all names in the file first */
1248
 
 
1249
 
        printf ("UID's in index\n");
1250
 
 
1251
 
        keyid = 0;
1252
 
        while ((keyid = camel_key_table_next (p->name_index, keyid, &word, &flags, &data))) {
1253
 
                if ((flags & 1) == 0)
1254
 
                        printf (" %s\n", word);
1255
 
                else
1256
 
                        printf (" %s (deleted)\n", word);
1257
 
                g_free (word);
1258
 
        }
1259
 
 
1260
 
        printf ("Word's in index\n");
1261
 
 
1262
 
        keyid = 0;
1263
 
        while ((keyid = camel_key_table_next (p->word_index, keyid, &word, &flags, &data))) {
1264
 
                CamelIndexCursor *idc;
1265
 
 
1266
 
                printf ("Word: '%s':\n", word);
1267
 
 
1268
 
                idc = camel_index_find ((CamelIndex *) idx, word);
1269
 
                while ((name = camel_index_cursor_next (idc))) {
1270
 
                        printf (" %s", name);
1271
 
                }
1272
 
                printf ("\n");
1273
 
                g_object_unref (idc);
1274
 
                g_free (word);
1275
 
        }
1276
 
#else
1277
 
        /* a more low-level dump routine */
1278
 
        GHashTable *block_type = g_hash_table_new (NULL, NULL);
1279
 
        camel_block_t id;
1280
 
        struct stat st;
1281
 
        gint type;
1282
 
 
1283
 
        add_keys (block_type, p->blocks, p->word_index->rootid);
1284
 
        add_keys (block_type, p->blocks, p->name_index->rootid);
1285
 
 
1286
 
        add_partition (block_type, p->blocks, p->word_hash->rootid);
1287
 
        add_partition (block_type, p->blocks, p->name_hash->rootid);
1288
 
 
1289
 
        dump_raw (block_type, p->blocks->path);
1290
 
        g_hash_table_destroy (block_type);
1291
 
#endif
1292
 
}
1293
 
 
1294
 
/* more debug stuff */
1295
 
void
1296
 
camel_text_index_validate (CamelTextIndex *idx)
1297
 
{
1298
 
        CamelTextIndexPrivate *p = idx->priv;
1299
 
        camel_key_t keyid;
1300
 
        gchar *word;
1301
 
        const gchar *name;
1302
 
        guint flags;
1303
 
        camel_block_t data;
1304
 
        gchar *oldword;
1305
 
        camel_key_t *records;
1306
 
        gsize count;
1307
 
 
1308
 
        GHashTable *names, *deleted, *words, *keys, *name_word, *word_word;
1309
 
 
1310
 
        names = g_hash_table_new (NULL, NULL);
1311
 
        deleted = g_hash_table_new (NULL, NULL);
1312
 
 
1313
 
        name_word = g_hash_table_new (g_str_hash, g_str_equal);
1314
 
 
1315
 
        words = g_hash_table_new (NULL, NULL);
1316
 
        keys = g_hash_table_new (NULL, NULL);
1317
 
 
1318
 
        word_word = g_hash_table_new (g_str_hash, g_str_equal);
1319
 
 
1320
 
        /* Iterate over all names in the file first */
1321
 
 
1322
 
        printf ("Checking UID consistency\n");
1323
 
 
1324
 
        keyid = 0;
1325
 
        while ((keyid = camel_key_table_next (p->name_index, keyid, &word, &flags, &data))) {
1326
 
                if ((oldword = g_hash_table_lookup (names, GINT_TO_POINTER (keyid))) != NULL
1327
 
                    || (oldword = g_hash_table_lookup (deleted, GINT_TO_POINTER (keyid))) != NULL) {
1328
 
                        printf ("Warning, name '%s' duplicates key (%x) with name '%s'\n", word, keyid, oldword);
1329
 
                        g_free (word);
1330
 
                } else {
1331
 
                        g_hash_table_insert (name_word, word, GINT_TO_POINTER (1));
1332
 
                        if ((flags & 1) == 0) {
1333
 
                                g_hash_table_insert (names, GINT_TO_POINTER (keyid), word);
1334
 
                        } else {
1335
 
                                g_hash_table_insert (deleted, GINT_TO_POINTER (keyid), word);
1336
 
                        }
1337
 
                }
1338
 
        }
1339
 
 
1340
 
        printf ("Checking WORD member consistency\n");
1341
 
 
1342
 
        keyid = 0;
1343
 
        while ((keyid = camel_key_table_next (p->word_index, keyid, &word, &flags, &data))) {
1344
 
                CamelIndexCursor *idc;
1345
 
                GHashTable *used;
1346
 
 
1347
 
                /* first, check for duplicates of keyid, and data */
1348
 
                if ((oldword = g_hash_table_lookup (words, GINT_TO_POINTER (keyid))) != NULL) {
1349
 
                        printf ("Warning, word '%s' duplicates key (%x) with name '%s'\n", word, keyid, oldword);
1350
 
                        g_free (word);
1351
 
                        word = oldword;
1352
 
                } else {
1353
 
                        g_hash_table_insert (words, GINT_TO_POINTER (keyid), word);
1354
 
                }
1355
 
 
1356
 
                if (data == 0) {
1357
 
                        /* This may not be an issue if things have been removed over time,
1358
 
                         * though it is a problem if its a fresh index */
1359
 
                        printf ("Word '%s' has no data associated with it\n", word);
1360
 
                } else {
1361
 
                        if ((oldword = g_hash_table_lookup (keys, GUINT_TO_POINTER (data))) != NULL) {
1362
 
                                printf ("Warning, word '%s' duplicates data (%x) with name '%s'\n", word, data, oldword);
1363
 
                        } else {
1364
 
                                g_hash_table_insert (keys, GUINT_TO_POINTER (data), word);
1365
 
                        }
1366
 
                }
1367
 
 
1368
 
                if (g_hash_table_lookup (word_word, word) != NULL) {
1369
 
                        printf ("Warning, word '%s' occurs more than once\n", word);
1370
 
                } else {
1371
 
                        g_hash_table_insert (word_word, word, word);
1372
 
                }
1373
 
 
1374
 
                used = g_hash_table_new (g_str_hash, g_str_equal);
1375
 
 
1376
 
                idc = camel_index_find ((CamelIndex *) idx, word);
1377
 
                while ((name = camel_index_cursor_next (idc))) {
1378
 
                        if (g_hash_table_lookup (name_word, name) == NULL) {
1379
 
                                printf ("word '%s' references non-existant name '%s'\n", word, name);
1380
 
                        }
1381
 
                        if (g_hash_table_lookup (used, name) != NULL) {
1382
 
                                printf ("word '%s' uses word '%s' more than once\n", word, name);
1383
 
                        } else {
1384
 
                                g_hash_table_insert (used, g_strdup (name), (gpointer) 1);
1385
 
                        }
1386
 
                }
1387
 
                g_object_unref (idc);
1388
 
 
1389
 
                g_hash_table_foreach (used, (GHFunc) g_free, NULL);
1390
 
                g_hash_table_destroy (used);
1391
 
 
1392
 
                printf ("word '%s'\n", word);
1393
 
 
1394
 
                while (data) {
1395
 
                        printf (" data %x ", data);
1396
 
                        if (camel_key_file_read (p->links, &data, &count, &records) == -1) {
1397
 
                                printf ("Warning, read failed for word '%s', at data '%u'\n", word, data);
1398
 
                                data = 0;
1399
 
                        } else {
1400
 
                                printf ("(%d)\n", (gint)count);
1401
 
                                g_free (records);
1402
 
                        }
1403
 
                }
1404
 
        }
1405
 
 
1406
 
        g_hash_table_destroy (names);
1407
 
        g_hash_table_destroy (deleted);
1408
 
        g_hash_table_destroy (words);
1409
 
        g_hash_table_destroy (keys);
1410
 
 
1411
 
        g_hash_table_foreach (name_word, (GHFunc) g_free, NULL);
1412
 
        g_hash_table_destroy (name_word);
1413
 
 
1414
 
        g_hash_table_foreach (word_word, (GHFunc) g_free, NULL);
1415
 
        g_hash_table_destroy (word_word);
1416
 
}
1417
 
 
1418
 
/* ********************************************************************** */
1419
 
/* CamelTextIndexName */
1420
 
/* ********************************************************************** */
1421
 
 
1422
 
G_DEFINE_TYPE (CamelTextIndexName, camel_text_index_name, CAMEL_TYPE_INDEX_NAME)
1423
 
 
1424
 
static void
1425
 
text_index_name_finalize (GObject *object)
1426
 
{
1427
 
        CamelTextIndexNamePrivate *priv;
1428
 
 
1429
 
        priv = CAMEL_TEXT_INDEX_NAME (object)->priv;
1430
 
 
1431
 
        g_hash_table_destroy (CAMEL_TEXT_INDEX_NAME (object)->parent.words);
1432
 
 
1433
 
        g_string_free (priv->buffer, TRUE);
1434
 
        camel_mempool_destroy (priv->pool);
1435
 
 
1436
 
        /* Chain up to parent's finalize() method. */
1437
 
        G_OBJECT_CLASS (camel_text_index_name_parent_class)->finalize (object);
1438
 
}
1439
 
 
1440
 
static void
1441
 
text_index_name_add_word (CamelIndexName *idn,
1442
 
                          const gchar *word)
1443
 
{
1444
 
        CamelTextIndexNamePrivate *p = ((CamelTextIndexName *) idn)->priv;
1445
 
 
1446
 
        if (g_hash_table_lookup (idn->words, word) == NULL) {
1447
 
                gchar *w = camel_mempool_strdup (p->pool, word);
1448
 
 
1449
 
                g_hash_table_insert (idn->words, w, w);
1450
 
        }
1451
 
}
1452
 
 
1453
 
/* Why?
1454
 
 * Because it doesn't hang/loop forever on bad data
1455
 
 * Used to clean up utf8 before it gets further */
1456
 
 
1457
 
static inline guint32
1458
 
camel_utf8_next (const guchar **ptr,
1459
 
                 const guchar *ptrend)
1460
 
{
1461
 
        register guchar *p = (guchar *) * ptr;
1462
 
        register guint c;
1463
 
        register guint32 v;
1464
 
        gint l;
1465
 
 
1466
 
        if (p == ptrend)
1467
 
                return 0;
1468
 
 
1469
 
        while ((c = *p++)) {
1470
 
                if (c < 0x80) {
1471
 
                        *ptr = p;
1472
 
                        return c;
1473
 
                } else if ((c&0xe0) == 0xc0) {
1474
 
                        v = c & 0x1f;
1475
 
                        l = 1;
1476
 
                } else if ((c&0xf0) == 0xe0) {
1477
 
                        v = c & 0x0f;
1478
 
                        l = 2;
1479
 
                } else if ((c&0xf8) == 0xf0) {
1480
 
                        v = c & 0x07;
1481
 
                        l = 3;
1482
 
                } else if ((c&0xfc) == 0xf8) {
1483
 
                        v = c & 0x03;
1484
 
                        l = 4;
1485
 
                } else if ((c&0xfe) == 0xfc) {
1486
 
                        v = c & 0x01;
1487
 
                        l = 5;
1488
 
                } else
1489
 
                        /* Invalid, ignore and look for next start gchar if room */
1490
 
                        if (p == ptrend) {
1491
 
                                return 0;
1492
 
                        } else {
1493
 
                                continue;
1494
 
                        }
1495
 
 
1496
 
                /* bad data or truncated buffer */
1497
 
                if (p + l > ptrend)
1498
 
                        return 0;
1499
 
 
1500
 
                while (l && ((c = *p) & 0xc0) == 0x80) {
1501
 
                        p++;
1502
 
                        l--;
1503
 
                        v = (v << 6) | (c & 0x3f);
1504
 
                }
1505
 
 
1506
 
                /* valid gchar */
1507
 
                if (l == 0) {
1508
 
                        *ptr = p;
1509
 
                        return v;
1510
 
                }
1511
 
 
1512
 
                /* else look for a start gchar again */
1513
 
        }
1514
 
 
1515
 
        return 0;
1516
 
}
1517
 
 
1518
 
static gsize
1519
 
text_index_name_add_buffer (CamelIndexName *idn,
1520
 
                            const gchar *buffer,
1521
 
                            gsize len)
1522
 
{
1523
 
        CamelTextIndexNamePrivate *p = CAMEL_TEXT_INDEX_NAME (idn)->priv;
1524
 
        const guchar *ptr, *ptrend;
1525
 
        guint32 c;
1526
 
        gchar utf8[8];
1527
 
        gint utf8len;
1528
 
 
1529
 
        if (buffer == NULL) {
1530
 
                if (p->buffer->len) {
1531
 
                        camel_index_name_add_word (idn, p->buffer->str);
1532
 
                        g_string_truncate (p->buffer, 0);
1533
 
                }
1534
 
                return 0;
1535
 
        }
1536
 
 
1537
 
        ptr = (const guchar *) buffer;
1538
 
        ptrend = (const guchar *) buffer + len;
1539
 
        while ((c = camel_utf8_next (&ptr, ptrend))) {
1540
 
                if (g_unichar_isalnum (c)) {
1541
 
                        c = g_unichar_tolower (c);
1542
 
                        utf8len = g_unichar_to_utf8 (c, utf8);
1543
 
                        utf8[utf8len] = 0;
1544
 
                        g_string_append (p->buffer, utf8);
1545
 
                } else {
1546
 
                        if (p->buffer->len > 0 && p->buffer->len <= CAMEL_TEXT_INDEX_MAX_WORDLEN) {
1547
 
                                text_index_name_add_word (idn, p->buffer->str);
1548
 
                                /*camel_index_name_add_word (idn, p->buffer->str);*/
1549
 
                        }
1550
 
 
1551
 
                        g_string_truncate (p->buffer, 0);
1552
 
                }
1553
 
        }
1554
 
 
1555
 
        return 0;
1556
 
}
1557
 
 
1558
 
static void
1559
 
camel_text_index_name_class_init (CamelTextIndexNameClass *class)
1560
 
{
1561
 
        GObjectClass *object_class;
1562
 
        CamelIndexNameClass *index_name_class;
1563
 
 
1564
 
        g_type_class_add_private (class, sizeof (CamelTextIndexNamePrivate));
1565
 
 
1566
 
        object_class = G_OBJECT_CLASS (class);
1567
 
        object_class->finalize = text_index_name_finalize;
1568
 
 
1569
 
        index_name_class = CAMEL_INDEX_NAME_CLASS (class);
1570
 
        index_name_class->add_word = text_index_name_add_word;
1571
 
        index_name_class->add_buffer = text_index_name_add_buffer;
1572
 
}
1573
 
 
1574
 
static void
1575
 
camel_text_index_name_init (CamelTextIndexName *text_index_name)
1576
 
{
1577
 
        text_index_name->priv = G_TYPE_INSTANCE_GET_PRIVATE (text_index_name, CAMEL_TYPE_TEXT_INDEX_NAME, CamelTextIndexNamePrivate);
1578
 
 
1579
 
        text_index_name->parent.words = g_hash_table_new (
1580
 
                g_str_hash, g_str_equal);
1581
 
 
1582
 
        text_index_name->priv->buffer = g_string_new ("");
1583
 
        text_index_name->priv->pool =
1584
 
                camel_mempool_new (256, 128, CAMEL_MEMPOOL_ALIGN_BYTE);
1585
 
}
1586
 
 
1587
 
CamelTextIndexName *
1588
 
camel_text_index_name_new (CamelTextIndex *idx,
1589
 
                           const gchar *name,
1590
 
                           camel_key_t nameid)
1591
 
{
1592
 
        CamelTextIndexName *idn = g_object_new (CAMEL_TYPE_TEXT_INDEX_NAME, NULL);
1593
 
        CamelIndexName *cin = &idn->parent;
1594
 
        CamelTextIndexNamePrivate *p = idn->priv;
1595
 
 
1596
 
        cin->index = g_object_ref (idx);
1597
 
        cin->name = camel_mempool_strdup (p->pool, name);
1598
 
        p->nameid = nameid;
1599
 
 
1600
 
        return idn;
1601
 
}
1602
 
 
1603
 
/* ********************************************************************** */
1604
 
/* CamelTextIndexCursor */
1605
 
/* ********************************************************************** */
1606
 
 
1607
 
G_DEFINE_TYPE (CamelTextIndexCursor, camel_text_index_cursor, CAMEL_TYPE_INDEX_CURSOR)
1608
 
 
1609
 
static void
1610
 
text_index_cursor_finalize (GObject *object)
1611
 
{
1612
 
        CamelTextIndexCursorPrivate *priv;
1613
 
 
1614
 
        priv = CAMEL_TEXT_INDEX_CURSOR (object)->priv;
1615
 
 
1616
 
        g_free (priv->records);
1617
 
        g_free (priv->current);
1618
 
 
1619
 
        /* Chain up to parent's finalize() method. */
1620
 
        G_OBJECT_CLASS (camel_text_index_cursor_parent_class)->finalize (object);
1621
 
}
1622
 
 
1623
 
static const gchar *
1624
 
text_index_cursor_next (CamelIndexCursor *idc)
1625
 
{
1626
 
        CamelTextIndexCursorPrivate *p = CAMEL_TEXT_INDEX_CURSOR (idc)->priv;
1627
 
        CamelTextIndexPrivate *tip = CAMEL_TEXT_INDEX (idc->index)->priv;
1628
 
        guint flags;
1629
 
 
1630
 
        c (printf ("Going to next cursor for word with data '%08x' next %08x\n", p->first, p->next));
1631
 
 
1632
 
        do {
1633
 
                while (p->record_index >= p->record_count) {
1634
 
                        g_free (p->records);
1635
 
                        p->records = NULL;
1636
 
                        p->record_index = 0;
1637
 
                        p->record_count = 0;
1638
 
                        if (p->next == 0)
1639
 
                                return NULL;
1640
 
                        if (camel_key_file_read (tip->links, &p->next, &p->record_count, &p->records) == -1)
1641
 
                                return NULL;
1642
 
                }
1643
 
 
1644
 
                g_free (p->current);
1645
 
                camel_key_table_lookup (
1646
 
                        tip->name_index, p->records[p->record_index],
1647
 
                        &p->current, &flags);
1648
 
                if (flags & 1) {
1649
 
                        g_free (p->current);
1650
 
                        p->current = NULL;
1651
 
                }
1652
 
                p->record_index++;
1653
 
        } while (p->current == NULL);
1654
 
 
1655
 
        return p->current;
1656
 
}
1657
 
 
1658
 
static void
1659
 
text_index_cursor_reset (CamelIndexCursor *idc)
1660
 
{
1661
 
        CamelTextIndexCursorPrivate *p = CAMEL_TEXT_INDEX_CURSOR (idc)->priv;
1662
 
 
1663
 
        g_free (p->records);
1664
 
        p->records = NULL;
1665
 
        g_free (p->current);
1666
 
        p->current = NULL;
1667
 
        p->record_count = 0;
1668
 
        p->record_index = 0;
1669
 
        p->next = p->first;
1670
 
}
1671
 
 
1672
 
static void
1673
 
camel_text_index_cursor_class_init (CamelTextIndexCursorClass *class)
1674
 
{
1675
 
        GObjectClass *object_class;
1676
 
        CamelIndexCursorClass *index_cursor_class;
1677
 
 
1678
 
        g_type_class_add_private (class, sizeof (CamelTextIndexCursorPrivate));
1679
 
 
1680
 
        object_class = G_OBJECT_CLASS (class);
1681
 
        object_class->finalize = text_index_cursor_finalize;
1682
 
 
1683
 
        index_cursor_class = CAMEL_INDEX_CURSOR_CLASS (class);
1684
 
        index_cursor_class->next = text_index_cursor_next;
1685
 
        index_cursor_class->reset = text_index_cursor_reset;
1686
 
}
1687
 
 
1688
 
static void
1689
 
camel_text_index_cursor_init (CamelTextIndexCursor *text_index_cursor)
1690
 
{
1691
 
        text_index_cursor->priv = G_TYPE_INSTANCE_GET_PRIVATE (text_index_cursor, CAMEL_TYPE_TEXT_INDEX_CURSOR, CamelTextIndexCursorPrivate);
1692
 
}
1693
 
 
1694
 
CamelTextIndexCursor *
1695
 
camel_text_index_cursor_new (CamelTextIndex *idx,
1696
 
                             camel_block_t data)
1697
 
{
1698
 
        CamelTextIndexCursor *idc = g_object_new (CAMEL_TYPE_TEXT_INDEX_CURSOR, NULL);
1699
 
        CamelIndexCursor *cic = &idc->parent;
1700
 
        CamelTextIndexCursorPrivate *p = idc->priv;
1701
 
 
1702
 
        cic->index = g_object_ref (idx);
1703
 
        p->first = data;
1704
 
        p->next = data;
1705
 
        p->record_count = 0;
1706
 
        p->record_index = 0;
1707
 
 
1708
 
        return idc;
1709
 
}
1710
 
 
1711
 
/* ********************************************************************** */
1712
 
/* CamelTextIndexKeyCursor */
1713
 
/* ********************************************************************** */
1714
 
 
1715
 
G_DEFINE_TYPE (CamelTextIndexKeyCursor, camel_text_index_key_cursor, CAMEL_TYPE_INDEX_CURSOR)
1716
 
 
1717
 
static void
1718
 
text_index_key_cursor_dispose (GObject *object)
1719
 
{
1720
 
        CamelTextIndexKeyCursorPrivate *priv;
1721
 
 
1722
 
        priv = CAMEL_TEXT_INDEX_KEY_CURSOR (object)->priv;
1723
 
 
1724
 
        if (priv->table != NULL) {
1725
 
                g_object_unref (priv->table);
1726
 
                priv->table = NULL;
1727
 
        }
1728
 
 
1729
 
        /* Chain up parent's dispose() method. */
1730
 
        G_OBJECT_CLASS (camel_text_index_key_cursor_parent_class)->dispose (object);
1731
 
}
1732
 
 
1733
 
static void
1734
 
text_index_key_cursor_finalize (GObject *object)
1735
 
{
1736
 
        CamelTextIndexKeyCursorPrivate *priv;
1737
 
 
1738
 
        priv = CAMEL_TEXT_INDEX_KEY_CURSOR (object)->priv;
1739
 
 
1740
 
        g_free (priv->current);
1741
 
 
1742
 
        /* Chain up to parent's finalize() method. */
1743
 
        G_OBJECT_CLASS (camel_text_index_key_cursor_parent_class)->finalize (object);
1744
 
}
1745
 
 
1746
 
static const gchar *
1747
 
text_index_key_cursor_next (CamelIndexCursor *idc)
1748
 
{
1749
 
        CamelTextIndexKeyCursorPrivate *p = CAMEL_TEXT_INDEX_KEY_CURSOR (idc)->priv;
1750
 
 
1751
 
        c (printf ("Going to next cursor for keyid %08x\n", p->keyid));
1752
 
 
1753
 
        g_free (p->current);
1754
 
        p->current = NULL;
1755
 
 
1756
 
        while ((p->keyid = camel_key_table_next (p->table, p->keyid, &p->current, &p->flags, &p->data))) {
1757
 
                if ((p->flags & 1) == 0) {
1758
 
                        return p->current;
1759
 
                } else {
1760
 
                        g_free (p->current);
1761
 
                        p->current = NULL;
1762
 
                }
1763
 
        }
1764
 
 
1765
 
        return NULL;
1766
 
}
1767
 
 
1768
 
static void
1769
 
text_index_key_cursor_reset (CamelIndexCursor *idc)
1770
 
{
1771
 
        CamelTextIndexKeyCursorPrivate *p = CAMEL_TEXT_INDEX_KEY_CURSOR (idc)->priv;
1772
 
 
1773
 
        p->keyid = 0;
1774
 
        p->flags = 0;
1775
 
        p->data = 0;
1776
 
        g_free (p->current);
1777
 
        p->current = NULL;
1778
 
}
1779
 
 
1780
 
static void
1781
 
camel_text_index_key_cursor_class_init (CamelTextIndexKeyCursorClass *class)
1782
 
{
1783
 
        GObjectClass *object_class;
1784
 
        CamelIndexCursorClass *index_cursor_class;
1785
 
 
1786
 
        g_type_class_add_private (class, sizeof (CamelTextIndexKeyCursorPrivate));
1787
 
 
1788
 
        object_class = G_OBJECT_CLASS (class);
1789
 
        object_class->dispose = text_index_key_cursor_dispose;
1790
 
        object_class->finalize = text_index_key_cursor_finalize;
1791
 
 
1792
 
        index_cursor_class = CAMEL_INDEX_CURSOR_CLASS (class);
1793
 
        index_cursor_class->next = text_index_key_cursor_next;
1794
 
        index_cursor_class->reset = text_index_key_cursor_reset;
1795
 
}
1796
 
 
1797
 
static void
1798
 
camel_text_index_key_cursor_init (CamelTextIndexKeyCursor *text_index_key_cursor)
1799
 
{
1800
 
        text_index_key_cursor->priv = G_TYPE_INSTANCE_GET_PRIVATE (text_index_key_cursor, CAMEL_TYPE_TEXT_INDEX_KEY_CURSOR, CamelTextIndexKeyCursorPrivate);
1801
 
        text_index_key_cursor->priv->keyid = 0;
1802
 
        text_index_key_cursor->priv->flags = 0;
1803
 
        text_index_key_cursor->priv->data = 0;
1804
 
        text_index_key_cursor->priv->current = NULL;
1805
 
}
1806
 
 
1807
 
CamelTextIndexKeyCursor *
1808
 
camel_text_index_key_cursor_new (CamelTextIndex *idx,
1809
 
                                 CamelKeyTable *table)
1810
 
{
1811
 
        CamelTextIndexKeyCursor *idc = g_object_new (CAMEL_TYPE_TEXT_INDEX_KEY_CURSOR, NULL);
1812
 
        CamelIndexCursor *cic = &idc->parent;
1813
 
        CamelTextIndexKeyCursorPrivate *p = idc->priv;
1814
 
 
1815
 
        cic->index = g_object_ref (idx);
1816
 
        p->table = g_object_ref (table);
1817
 
 
1818
 
        return idc;
1819
 
}
1820
 
 
1821
 
/* ********************************************************************** */
1822
 
 
1823
 
#define m(x)
1824
 
 
1825
 
#if 0
1826
 
 
1827
 
struct _CamelIndexRoot {
1828
 
        struct _CamelBlockRoot root;
1829
 
 
1830
 
        camel_block_t word_root; /* a keyindex containing the keyid -> word mapping */
1831
 
        camel_block_t word_hash_root; /* a partitionindex containing word -> keyid mapping */
1832
 
 
1833
 
        camel_block_t name_root; /* same, for names */
1834
 
        camel_block_t name_hash_root;
1835
 
};
1836
 
 
1837
 
gchar wordbuffer[] = "This is a buffer of multiple words.  Some of the words are duplicates"
1838
 
" while other words are the same, some are in difFerenT Different different case cAsE casE,"
1839
 
" with,with:with;with-with'with\"'\"various punctuation as well.  So much for those Words. and 10"
1840
 
" numbers in a row too 1,2,3,4,5,6,7,8,9,10!  Yay!.";
1841
 
 
1842
 
gint
1843
 
main (gint argc,
1844
 
      gchar **argv)
1845
 
{
1846
 
#if 0
1847
 
        CamelBlockFile *bs;
1848
 
        CamelKeyTable *ki;
1849
 
        CamelPartitionTable *cpi;
1850
 
        CamelBlock *keyroot, *partroot;
1851
 
        struct _CamelIndexRoot *root;
1852
 
        FILE *fp;
1853
 
        gchar line[256], *key;
1854
 
        camel_key_t keyid;
1855
 
        gint index = 0, flags, data;
1856
 
#endif
1857
 
        CamelIndex *idx;
1858
 
        CamelIndexName *idn;
1859
 
        CamelIndexCursor *idc;
1860
 
        const gchar *word;
1861
 
        gint i;
1862
 
 
1863
 
        printf ("Camel text index tester!\n");
1864
 
 
1865
 
        g_thread_init (NULL);
1866
 
        camel_init (NULL, 0);
1867
 
 
1868
 
        idx = (CamelIndex *)camel_text_index_new ("textindex", O_CREAT|O_RDWR|O_TRUNC);
1869
 
 
1870
 
#if 1
1871
 
        camel_index_compress (idx);
1872
 
 
1873
 
        return 0;
1874
 
#endif
1875
 
 
1876
 
        for (i = 0; i < 100; i++) {
1877
 
                gchar name[16];
1878
 
 
1879
 
                sprintf (name, "%d", i);
1880
 
                printf ("Adding words to name '%s'\n", name);
1881
 
                idn = camel_index_add_name (idx, name);
1882
 
                camel_index_name_add_buffer (idn, wordbuffer, sizeof (wordbuffer) - 1);
1883
 
                camel_index_write_name (idx, idn);
1884
 
                g_object_unref (idn);
1885
 
        }
1886
 
 
1887
 
        printf ("Looking up which names contain word 'word'\n");
1888
 
        idc = camel_index_find (idx, "words");
1889
 
        while ((word = camel_index_cursor_next (idc)) != NULL) {
1890
 
                printf (" name is '%s'\n", word);
1891
 
        }
1892
 
        g_object_unref (idc);
1893
 
        printf ("done.\n");
1894
 
 
1895
 
        printf ("Looking up which names contain word 'truncate'\n");
1896
 
        idc = camel_index_find (idx, "truncate");
1897
 
        while ((word = camel_index_cursor_next (idc)) != NULL) {
1898
 
                printf (" name is '%s'\n", word);
1899
 
        }
1900
 
        g_object_unref (idc);
1901
 
        printf ("done.\n");
1902
 
 
1903
 
        camel_index_sync (idx);
1904
 
        g_object_unref (idx);
1905
 
 
1906
 
#if 0
1907
 
        bs = camel_block_file_new ("blocks", "TESTINDX", CAMEL_BLOCK_SIZE);
1908
 
 
1909
 
        root = (struct _CamelIndexRoot *) bs->root;
1910
 
        if (root->word_root == 0) {
1911
 
                keyroot = camel_block_file_new_block (bs);
1912
 
                root->word_root = keyroot->id;
1913
 
                camel_block_file_touch_block (bs, bs->root_block);
1914
 
        }
1915
 
        if (root->word_hash_root == 0) {
1916
 
                partroot = camel_block_file_new_block (bs);
1917
 
                root->word_hash_root = partroot->id;
1918
 
                camel_block_file_touch_block (bs, bs->root_block);
1919
 
        }
1920
 
 
1921
 
        ki = camel_key_table_new (bs, root->word_root);
1922
 
        cpi = camel_partition_table_new (bs, root->word_hash_root);
1923
 
 
1924
 
        fp = fopen ("/usr/dict/words", "r");
1925
 
        if (fp == NULL) {
1926
 
                perror ("fopen");
1927
 
                return 1;
1928
 
        }
1929
 
 
1930
 
        while (fgets (line, sizeof (line), fp) != NULL) {
1931
 
                line[strlen (line) - 1] = 0;
1932
 
 
1933
 
                /* see if its already there */
1934
 
                keyid = camel_partition_table_lookup (cpi, line);
1935
 
                if (keyid == 0) {
1936
 
                        m (printf ("Adding word '%s' %d\n", line, index));
1937
 
 
1938
 
                        keyid = camel_key_table_add (ki, line, index, 0);
1939
 
                        m (printf (" key = %08x\n", keyid));
1940
 
 
1941
 
                        camel_partition_table_add (cpi, line, keyid);
1942
 
 
1943
 
                        m (printf ("Lookup word '%s'\n", line));
1944
 
                        keyid = camel_partition_table_lookup (cpi, line);
1945
 
                        m (printf (" key = %08x\n", keyid));
1946
 
                }
1947
 
 
1948
 
                m (printf ("Lookup key %08x\n", keyid));
1949
 
 
1950
 
                camel_key_table_set_flags (ki, keyid, index, 1);
1951
 
 
1952
 
                data = camel_key_table_lookup (ki, keyid, &key, &flags);
1953
 
                m (printf (" word = '%s' %d %04x\n", key, data, flags));
1954
 
 
1955
 
                g_assert (data == index && strcmp (key, line) == 0);
1956
 
 
1957
 
                g_free (key);
1958
 
 
1959
 
                index++;
1960
 
        }
1961
 
 
1962
 
        printf ("Scanning again\n");
1963
 
        fseek (fp, SEEK_SET, 0);
1964
 
        index = 0;
1965
 
        while (fgets (line, sizeof (line), fp) != NULL) {
1966
 
                line[strlen (line) - 1] = 0;
1967
 
                m (printf ("Lookup word '%s' %d\n", line, index));
1968
 
                keyid = camel_partition_table_lookup (cpi, line);
1969
 
                m (printf (" key = %08d\n", keyid));
1970
 
 
1971
 
                m (printf ("Lookup key %08x\n", keyid));
1972
 
                data = camel_key_table_lookup (ki, keyid, &key, &flags);
1973
 
                m (printf (" word = '%s' %d\n", key, data));
1974
 
 
1975
 
                g_assert (data == index && strcmp (key, line) == 0);
1976
 
 
1977
 
                g_free (key);
1978
 
 
1979
 
                index++;
1980
 
        }
1981
 
        fclose (fp);
1982
 
 
1983
 
        printf ("Freeing partition index\n");
1984
 
        camel_partition_table_free (cpi);
1985
 
 
1986
 
        printf ("Syncing block file\n");
1987
 
        camel_block_file_sync (bs);
1988
 
#endif
1989
 
        return 0;
1990
 
}
1991
 
 
1992
 
#endif