libcqdb 0.4.0

C binding to the Rust implementation of Constant Quark Database (CQDB)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
/*
 *      Constant Quark Database (CQDB).
 *
 * Copyright (c) 2007, Naoaki Okazaki
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Northwestern University, University of Tokyo,
 *       nor the names of its contributors may be used to endorse or promote
 *       products derived from this software without specific prior written
 *       permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* $Id$ */

#ifndef    __CQDB_H__
#define    __CQDB_H__

/** @file */



/**
 * \addtogroup cqdb_const CQDB Constants
 * @{
 *
 *    The CQDB Constants.
 */

/**
 * CQDB flags.
 */
enum {
    CQDB_NONE = 0,                        /**< No flag. */
    CQDB_ONEWAY = 0x00000001,            /**< A reverse lookup array is omitted. */
    CQDB_ERROR_OCCURRED = 0x00010000,    /**< An error has occurred. */
};

/**
 * CQDB status codes.
 */
enum {
    CQDB_SUCCESS = 0,                    /**< Success. */
    CQDB_ERROR = -1024,                    /**< Unspecified error. */
    CQDB_ERROR_NOTFOUND,                /**< String not found. */
    CQDB_ERROR_OUTOFMEMORY,                /**< Insufficient memory. */
    CQDB_ERROR_FILEWRITE,                /**< Error in fwrite() operations. */
    CQDB_ERROR_FILETELL,                /**< Error in ftell() operations. */
    CQDB_ERROR_FILESEEK,                /**< Error in fseek() operations. */
    CQDB_ERROR_INVALIDID,                /**< Invalid parameters. */
};

/** @} */



/**
 * \addtogroup cqdb_writer CQDB Writer API
 * @{
 *
 *    The CQDB Writer API constructs a CQDB chunk on a seekable stream. The
 *    seekable stream must be created by the fopen() function with writable and
 *    binary flags ("wb"). The CQDB Writer API can build a CQDB chunk at any
 *    position on the stream; one can thus write some data, append a CQDB chunk,
 *    and continue writing other data on the stream.
 *
 *    By default, the function cqdb_writer() constructs a database with forward
 *    (string to integer identifier) and backward (integer identifier to string)
 *    lookups. The data for reverse lookup is omitted with ::CQDB_ONEWAY flag
 *    specified.
 *
 *    It is recommended to keep the maximum number of identifiers as smallest as
 *    possible because reverse lookup is maintained by a array with the size of
 *    sizeof(int) * (maximum number of identifiers + 1). For example, putting a
 *    set of integer identifers (0, 1, 1000) creates a reverse lookup array with
 *    1001 elements only to waste the disk space for 998 (= 1001-3) elements in
 *    the array.
 */

struct tag_cqdb_writer;
typedef struct tag_cqdb_writer cqdb_writer_t;    /**< Typedef of a CQDB writer. */

/**
 * Create a new CQDB writer on a seekable stream.
 *
 *    This function initializes a database on the seekable stream and returns
 *    the pointer to a ::cqdb_writer_t instance to write the database.
 *    The stream must have the writable and binary flags. The database creation
 *    flag must be zero except when the reverse lookup array is unnecessary;
 *    specifying ::CQDB_ONEWAY flag will save the storage space for the reverse
 *    lookup array. Once calling this function, one should avoid accessing the
 *    seekable stream directly until calling cqdb_writer_close().
 *
 *    @param    fp                The pointer to the writable and seekable stream.
 *    @param    flag            Database creation flag.
 *    @retval    cqdb_writer_t*    The pointer to the new ::cqdb_writer_t instance if
 *                            successful; otherwise \c NULL.
 */
cqdb_writer_t* cqdb_writer(FILE *fp, int flag);

/**
 * Put a string/identifier association to the database.
 *
 *    This function append a string/identifier association into the database.
 *    Make sure that the string and/or identifier have never been inserted to
 *    the database and that the identifier is a non-negative value.
 *
 *    @param    dbw            The pointer to the ::cqdb_writer_t instance.
 *    @param    str            The pointer to the string.
 *    @param    id            The identifier.
 *    @retval    int            Zero if successful, or a status code otherwise.
 */
int cqdb_writer_put(cqdb_writer_t* dbw, const char *str, int id);

/**
 * Close a CQDB writer.
 *
 *    This function finalizes the database on the stream. If successful, the
 *    data remaining on the memory is flushed to the stream; the stream position
 *    is moved to the end of the chunk. If an unexpected error occurs, this
 *    function tries to rewind the stream position to the original position when
 *    the function cqdb_writer() was called.
 *
 *    @param    dbw            The pointer to the ::cqdb_writer_t instance.
 *    @retval    int            Zero if successful, or a status code otherwise.
 */
int cqdb_writer_close(cqdb_writer_t* dbw);

/** @} */



/**
 * \addtogroup cqdb_reader CQDB Reader API
 * @{
 *
 *    The CQDB reader API provides a read access to the database whose memory
 *    image is loaded on a memory block. The memory-passing interface has
 *    several advantages. Firstly, one can choose an efficient way for their
 *    application to load a database image to a memory block, e.g., to read
 *    the whole image from a file, to use the Memory Mapped File (mmap) API,
 *    etc.
 *    Secondaly, one can design the file format freely only if the memory
 *    block for a database is extracted from the file.
 *
 *    The most fundamental operation on the CQDB reader API is forward lookup
 *    through the use of cqdb_to_id() function, which retrieves integer
 *    identifiers from strings. Reverse lookup (retrieving strings from integer
 *    identifiers) with cqdb_to_string() function is not supported if the
 *    database has been created with ::CQDB_ONEWAY flag.
 */

struct tag_cqdb;
typedef struct tag_cqdb cqdb_t;        /**< Typedef of a CQDB reader. */

/**
 * Open a new CQDB reader on a memory block.
 *
 *    This function initializes a database on a memory block and returns the
 *    pointer to a ::cqdb_t instance to access the database.
 *
 *    @param    buffer        The pointer to the memory block.
 *    @param    size        The size of the memory block.
 *    @retval    cqdb_t*        The pointer to the ::cqdb_t instance.
 */
cqdb_t* cqdb_reader(const void *buffer, size_t size);

/**
 * Delete the CQDB reader.
 *
 *    This function frees the work area allocated by cqdb_reader() function.
 *
 *    @param    db            The pointer to the ::cqdb_t instance.
 */
void cqdb_delete(cqdb_t* db);

/**
 * Retrieve the identifier associated with a string.
 *
 *    This function returns the identifier associated with a string.
 *
 *    @param    db            The pointer to the ::cqdb_t instance.
 *    @param    str            The pointer to a string.
 *    @retval    int            The non-negative identifier if successful, negative
 *                        status code otherwise.
 */
int cqdb_to_id(cqdb_t* db, const char *str);

/**
 * Retrieve the string associated with an identifier.
 *
 *    This function returns the string associated with an identifier.
 *
 *    @param    db            The pointer to the cqdb_t instance.
 *    @param    id            The id.
 *    @retval    const char*    The pointer to the string associated with the
 *                        identifier if successful; otherwise \c NULL.
 */
const char* cqdb_to_string(cqdb_t* db, int id);

/**
 * Get the number of associations in the database.
 *
 *    This function returns the number of associations in the database.
 *
 *    @param    db            The pointer to the ::cqdb_t instance.
 *    @retval    int            The number of string/identifier associations.
 */
int cqdb_num(cqdb_t* db);

/** @} */



/**
@mainpage Constant Quark Database (CQDB)

@section intro Introduction

It is a common technique for speed and memory optimizations that an
application converts all string values into integer identifiers, does some
processing with integer values, and then restores the original string values
(if necessary). The data structure for two-way associations between strings
and integer identifiers is known as Quark:
- GQuark in GLib: http://www.gtk.org/
- quark (C++): http://www.chokkan.org/software/sample/quark.h

Constant Quark Database (CQDB) is a database library specialized for
serialization and retrieval of <i>static</i> associations between strings and
integer identifiers. The database provides several features:
- <b>Fast look-ups.</b> Retrieving an integer identifier for a string is
  usually done by accessing three memory blocks. Retrieving a string for an
  integer identifier is always done by accessing two memory blocks.
  See the @ref performance "performance" evaluation.
- <b>Low overhead.</b> A CQDB database consists of a chunk header (24 bytes),
  hash tables (2048 bytes and 16 bytes per record), a reverse lookup array
  (4 bytes per integer identifier), and records (8 bytes + string size per
  record).
  See the @ref performance "performance" evaluation.
- <b>Sophisticated hash function.</b> CQDB incorporates the fast and
  collision-resistant hash function for strings
  (<a href="http://www.burtleburtle.net/bob/c/lookup3.c">lookup3.c</a>)
  implemented by Bob Jenkins.
- <b>Chunk format.</b> The structure of CQDB is designed to store the data in
  a chunk of a file; CQDB can be embedded into a file with other arbitrary
  data.
- <b>Omissible reverse look-up array.</b> The reverse look-up array can be
  omitted if it is not necessary to retrieve strings from integer identifiers.
- <b>Cross platform.</b> The source code can be compiled on Microsoft Visual
  Studio 2005, GNU C Compiler (gcc), etc.
- <b>Simple API.</b> The CQDB API exposes only a few functions.

CQDB is suitable for implementing dictionaries in which fast look-ups of
strings and identifiers are essential while a dictionary update rarely occurs.
The data structure is a specialization (and extension) of the
<a href="http://cr.yp.to/cdb.html">Constant Database</a> proposed by
Daniel J. Bernstein.

CQDB does not support assigning a unique integer identifier for a given string,
modify associations, nor check collisions in strings and identifiers; thus,
it may be necessary to use an existing Quark implementation to manage proper
associations between strings and identifiers on memory.

This library is used by the
<a href="http://www.chokkan.org/software/crfsuite/">CRFsuite</a> project.

@section download Download

- <a href="http://www.chokkan.org/software/dist/cqdb-1.1.tar.gz">Source code</a>

CQDB is distributed under the term of the
<a href="http://www.opensource.org/licenses/bsd-license.php">modified BSD license</a>.

@section changelog History
- Version 1.1 (2007-12-01):
    - Fixed a bug when a CQDB chunk is embedded to a file.

- Version 1.0 (2007-09-20):
    - Initial release.

@section api Documentation

- @ref cqdb_const "CQDB Constants"
- @ref cqdb_reader "CQDB Reader API"
- @ref cqdb_writer "CQDB Writer API"

@section sample Sample programs
@subsection sample_writer A writer sample

This sample code constructs a database "test.cqdb" with 1,000,000
string/identifier associations,
"00000000"/0, "00000001"/1, ..., "01000000"/1000000.

@code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cqdb.h"

#define    DBNAME        "test.cqdb"
#define    NUMELEMS    1000000

int main(int argc, char *argv[])
{
    int i, ret;
    char str[10];
    FILE *fp = NULL;
    cqdb_writer_t* dbw = NULL;

    // Open a file for writing.
    fp = fopen(DBNAME, "wb");
    if (fp == NULL) {
        fprintf(stderr, "ERROR: failed to open the file.\n");
        return 1;
    }

    // Create a CQDB on the file stream.
    dbw = cqdb_writer(fp, 0);
    if (dbw == NULL) {
        fprintf(stderr, "ERROR: failed to create a CQDB on the file.\n");
        goto error_exit;
    }

    // Put string/integer associations, "00000001"/1, ..., "01000000"/1000000.
    for (i = 0;i < NUMELEMS;++i) {
        sprintf(str, "%08d", i);
        if (ret = cqdb_writer_put(dbw, str, i)) {
            fprintf(stderr, "ERROR: failed to put a pair '%s'/%d.\n", str, i);
            goto error_exit;
        }
    }

    // Close the CQDB.
    if (ret = cqdb_writer_close(dbw)) {
        fprintf(stderr, "ERROR: failed to close the CQDB.\n");
        goto error_exit;
    }

    // Close the file.
    fclose(fp);
    return 0;

error_exit:
    if (dbw != NULL) cqdb_writer_close(dbw);
    if (fp != NULL) fclose(fp);
    return 1;
}

@endcode

@subsection sample_reader A reader sample

This sample code issues string queries "00000000", ..., "01000000" to retrive
integer identifiers (forward lookups) and integer queries 0, ..., 1000000 to
retrieve the strings  "00000000", ..., "01000000".

@code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cqdb.h"

#define    DBNAME        "test.cqdb"
#define    NUMELEMS    1000000

int main(int argc, char *argv[])
{
    int i, j, ret;
    long size = 0;
    char str[10], *value = NULL, *buffer = NULL;
    FILE *fp = NULL;
    cqdb_t* db = NULL;

    // Open the database.
    fp = fopen(DBNAME, "rb");
    if (fp == NULL) {
        fprintf(stderr, "ERROR: failed to open the file\n");
        return 1;
    }

    // Obtain the file size.
    fseek(fp, 0, SEEK_END);
    size = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    // Read the content of the file at a time.
    buffer = (char *)malloc(size);
    if (buffer == NULL) {
        fprintf(stderr, "ERROR: out of memory.\n");
        goto error_exit;
    }
    fread(buffer, 1, size, fp);
    fclose(fp);
    fp = NULL;

    // Open the database on the memory.
    db = cqdb_reader(buffer, size);
    if (db == NULL) {
        fprintf(stderr, "ERROR: failed to open a CQDB on the file.\n");
        goto error_exit;
    }

    // Forward lookups: strings to integer identifiers.
    for (i = 0;i < NUMELEMS;++i) {
        sprintf(str, "%08d", i);
        j = cqdb_to_id(db, str);
        // Validity check.
        if (j < 0 || i != j) {
            fprintf(stderr, "ERROR: inconsistency error '%s'/%d.\n", str, i);
            goto error_exit;
        }
    }

    // Reverse lookups: integer identifiers to strings.
    for (i = 0;i < NUMELEMS;++i) {
        sprintf(str, "%08d", i);
        value = cqdb_to_string(db, i);
        // Validity check.
        if (value == NULL || strcmp(str, value) != 0) {
            fprintf(stderr, "ERROR: inconsistency error '%s'/%d.\n", str, i);
            goto error_exit;
        }
    }

    // Delete the instance of the CQDB.
    cqdb_delete(db);
    free(buffer);

    return 0;

error_exit:
    if (fp != NULL) fclose(fp);
    if (buffer != NULL) free(buffer);
    return 1;
}

@endcode

@section performance Performance

An experiment for performance comparision with
<a href="http://www.oracle.com/database/berkeley-db/">Berkeley DB (BDB) 4.5.20</a>
and <a href="http://qdbm.sourceforge.net/">Quick Database Manager (QDBM) 1.8.75</a>
was conducted.
Constructing a database with 1,000,000 string/identifier associations,
"00000000"/0, "00000001"/1, ..., "01000000"/1000000, this experiment issued
string queries "00000000", ..., "01000000" (forward lookups) and integer
queries 0, ..., 1000000 (reverse lookups) to the database. Since BDB and
QDBM do not support reverse lookups, reverse items (key: identifier,
value: string) were inserted to the database in addition to the forward items
(key: string, value: integer). Microsoft Windows Vista Business was running on
the test environment (Intel Core2Duo 6600 (2.40GHz) processor, Intel G965
chipset, 2GB main memory, and Seagate ST3320620 HDD).
The test codes were compiled with Microsoft Visual Studio 2005.

This table shows the elapsed time for constructing the database (write time),
the elapsed time for processing with the queries (read time), and the size of
the database generated by each database library.
The read/write access was extremely faster than those of other database
libraries. The database was smaller than half the size of those generated by
other libraries.
This results suggest that the CQDB has the substantial advantage over the
existing database libraries for implementing a static quark database.

<table>
<tr>
<th>Database</th><th>Parameters</th><th>Write time [sec]</th><th>Read time [sec]</th><th>Database size [MB]</th>
</tr>
<tr align="right">
<td align="left">Constant Quark Database (CQDB) 1.0</td>
<td align="left">Default (none)</td>
<td><b>1.48</b></td><td><b>0.65</b></td><td><b>35.2</b></td>
</tr>
<tr align="right">
<td align="left">Berkeley DB (BDB) 4.5.20</td>
<td align="left">Default</td>
<td>91.8</td><td>37.5</td><td>79.7</td>
</tr>
<tr align="right">
<td align="left">Berkeley DB (BDB) 4.5.20</td>
<td align="left">table_size=4000000; cache_size=200MB</td>
<td>57.8</td><td>37.5</td><td>79.7</td>
</tr>
<tr align="right">
<td align="left">Quick Database Manager (QDBM) 1.8.75</td>
<td align="left">Default</td>
<td>95.4</td><td>80.6</td><td>76.3</td>
</tr>
<tr align="right">
<td align="left">Quick Database Manager (QDBM) 1.8.75</td>
<td align="left">table_size=4000000</td>
<td>15.7</td><td>12.0</td><td>92.2</td>
</tr>
</table>

@section reference Reference
- <a href="http://cr.yp.to/cdb.html">cdb</a> by Daniel J. Bernstein.
- <a href="http://www.corpit.ru/mjt/tinycdb.html">TinyCDB</a> by Michael Tokarev.
- <a href="http://www.unixuser.org/~euske/doc/cdbinternals/index.html">Constant Database (cdb) Internals</a> by Yusuke Shinyama.
- <a href="http://www.burtleburtle.net/bob/hash/index.html">Hash Functions and Block Ciphers</a> by Bob Jenkins.

*/

#endif/*__CQDB_H__*/