From c1cf3214381896dbd298324e413d46d981e95079 Mon Sep 17 00:00:00 2001 From: adam Date: Sun, 28 Oct 2012 20:43:15 +0700 Subject: [PATCH] initial import --- .gitignore | 44 + t => .gitmodules | 0 .idea/.name | 1 + .idea/compiler.xml | 21 + .idea/copyright/profiles_settings.xml | 5 + .idea/encodings.xml | 5 + .idea/inspectionProfiles/Project_Default.xml | 210 + .idea/inspectionProfiles/profiles_settings.xml | 7 + .idea/misc.xml | 55 + .idea/modules.xml | 9 + .idea/scopes/scope_settings.xml | 5 + .idea/uiDesigner.xml | 125 + .idea/vcs.xml | 7 + README.md | 210 + tcejdb/COPYING | 504 ++ tcejdb/ChangeLog | 3 + tcejdb/Makefile.in | 839 ++ tcejdb/README | 37 + tcejdb/bros/Makefile | 133 + tcejdb/bros/bdbtest.c | 438 + tcejdb/bros/cdbtest.c | 219 + tcejdb/bros/cmpsqltctest.c | 186 + tcejdb/bros/gdbmtest.c | 216 + tcejdb/bros/mapreporter | 72 + tcejdb/bros/maptest.cc | 679 ++ tcejdb/bros/ndbmtest.c | 204 + tcejdb/bros/qdbmtest.c | 375 + tcejdb/bros/reporter | 141 + tcejdb/bros/result.xls | Bin 0 -> 35840 bytes tcejdb/bros/sdbmtest.c | 204 + tcejdb/bros/sqltest.c | 404 + tcejdb/bros/tctest.c | 748 ++ tcejdb/bros/tdbtest.c | 205 + tcejdb/bson.c | 1359 +++ tcejdb/bson.h | 1089 +++ tcejdb/configure | 5493 ++++++++++++ tcejdb/configure.in | 365 + tcejdb/doc/benchmark.pdf | Bin 0 -> 33444 bytes tcejdb/doc/common.css | 211 + tcejdb/doc/icon16.png | Bin 0 -> 383 bytes tcejdb/doc/index.html | 159 + tcejdb/doc/index.ja.html | 200 + tcejdb/doc/logo-ja.png | Bin 0 -> 2094 bytes tcejdb/doc/logo.png | Bin 0 -> 2283 bytes tcejdb/doc/spex-en.html | 7145 +++++++++++++++ tcejdb/doc/spex-ja.html | 7476 ++++++++++++++++ tcejdb/doc/tokyoproducts.pdf | Bin 0 -> 299895 bytes tcejdb/doc/tokyoproducts.ppt | Bin 0 -> 214528 bytes tcejdb/ejdb.c | 2816 ++++++ tcejdb/ejdb.h | 311 + tcejdb/ejdb_private.h | 104 + tcejdb/ejdbutl.h | 76 + tcejdb/encoding.c | 165 + tcejdb/encoding.h | 54 + tcejdb/lab/calccomp | 118 + tcejdb/lab/datechange | 56 + tcejdb/lab/diffcheck | 45 + tcejdb/lab/htmltotsv | 102 + tcejdb/lab/magic | 19 + tcejdb/lab/printenv.cgi | 27 + tcejdb/lab/stepcount | 26 + tcejdb/lab/stopwatch | 61 + tcejdb/lab/tabcheck | 43 + tcejdb/lab/wgettsv | 239 + tcejdb/lab/widthcheck | 57 + tcejdb/man/htmltoman | 104 + tcejdb/man/tcadb.3 | 676 ++ tcejdb/man/tcamgr.1 | 97 + tcejdb/man/tcamttest.1 | 35 + tcejdb/man/tcatest.1 | 55 + tcejdb/man/tcbdb.3 | 1355 +++ tcejdb/man/tcbmgr.1 | 125 + tcejdb/man/tcbmttest.1 | 81 + tcejdb/man/tcbtest.1 | 107 + tcejdb/man/tcfdb.3 | 975 +++ tcejdb/man/tcfmgr.1 | 98 + tcejdb/man/tcfmttest.1 | 62 + tcejdb/man/tcftest.1 | 73 + tcejdb/man/tchdb.3 | 898 ++ tcejdb/man/tchmgr.1 | 110 + tcejdb/man/tchmttest.1 | 85 + tcejdb/man/tchtest.1 | 95 + tcejdb/man/tclist.3 | 1 + tcejdb/man/tcmap.3 | 1 + tcejdb/man/tcmdb.3 | 1 + tcejdb/man/tcmpool.3 | 1 + tcejdb/man/tctdb.3 | 1110 +++ tcejdb/man/tctmgr.1 | 140 + tcejdb/man/tctmttest.1 | 92 + tcejdb/man/tctree.3 | 1 + tcejdb/man/tcttest.1 | 105 + tcejdb/man/tcucodec.1 | 162 + tcejdb/man/tcumttest.1 | 41 + tcejdb/man/tcutest.1 | 81 + tcejdb/man/tcutil.3 | 4518 ++++++++++ tcejdb/man/tcxstr.3 | 1 + tcejdb/man/tokyocabinet.3 | 132 + tcejdb/md5.c | 381 + tcejdb/md5.h | 101 + tcejdb/myconf.c | 493 ++ tcejdb/myconf.h | 575 ++ tcejdb/nbproject/Package-Default.bash | 75 + tcejdb/nbproject/configurations.xml | 305 + tcejdb/nbproject/project.xml | 23 + tcejdb/numbers.c | 128 + tcejdb/samples/sample1/Makefile | 13 + tcejdb/samples/sample1/sample1.c | 72 + tcejdb/tcadb.c | 4338 ++++++++++ tcejdb/tcadb.h | 548 ++ tcejdb/tcamgr.c | 1027 +++ tcejdb/tcamttest.c | 542 ++ tcejdb/tcatest.c | 1845 ++++ tcejdb/tcawmgr.c | 482 ++ tcejdb/tcbdb.c | 4182 +++++++++ tcejdb/tcbdb.h | 1101 +++ tcejdb/tcbmgr.c | 1020 +++ tcejdb/tcbmttest.c | 1810 ++++ tcejdb/tcbtest.c | 2586 ++++++ tcejdb/tcejdb.iml | 13 + tcejdb/tcejdb.pc.in | 14 + tcejdb/tcfdb.c | 2746 ++++++ tcejdb/tcfdb.h | 858 ++ tcejdb/tcfmgr.c | 794 ++ tcejdb/tcfmttest.c | 1220 +++ tcejdb/tcftest.c | 1695 ++++ tcejdb/tchdb.c | 5145 +++++++++++ tcejdb/tchdb.h | 870 ++ tcejdb/tchmgr.c | 850 ++ tcejdb/tchmttest.c | 1757 ++++ tcejdb/tchtest.c | 2129 +++++ tcejdb/tctdb.c | 6566 ++++++++++++++ tcejdb/tctdb.h | 1174 +++ tcejdb/tctmgr.c | 1241 +++ tcejdb/tctmttest.c | 1563 ++++ tcejdb/tcttest.c | 2062 +++++ tcejdb/tcucodec.c | 1357 +++ tcejdb/tcumttest.c | 578 ++ tcejdb/tcutest.c | 1875 ++++ tcejdb/tcutil.c | 10542 +++++++++++++++++++++++ tcejdb/tcutil.h | 4188 +++++++++ tcejdb/testejdb/Makefile | 46 + tcejdb/testejdb/t1.c | 294 + tcejdb/testejdb/t2.c | 2311 +++++ tcejdb/testejdb/t3.c | 159 + tcejdb/timsort-impl.h | 798 ++ tcejdb/timsort.c | 402 + tcejdb/tokyocabinet.idl | 336 + 147 files changed, 120775 insertions(+) create mode 100644 .gitignore rename t => .gitmodules (100%) create mode 100644 .idea/.name create mode 100644 .idea/compiler.xml create mode 100644 .idea/copyright/profiles_settings.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/scopes/scope_settings.xml create mode 100644 .idea/uiDesigner.xml create mode 100644 .idea/vcs.xml create mode 100644 README.md create mode 100644 tcejdb/COPYING create mode 100644 tcejdb/ChangeLog create mode 100644 tcejdb/Makefile.in create mode 100644 tcejdb/README create mode 100644 tcejdb/bros/Makefile create mode 100644 tcejdb/bros/bdbtest.c create mode 100644 tcejdb/bros/cdbtest.c create mode 100644 tcejdb/bros/cmpsqltctest.c create mode 100644 tcejdb/bros/gdbmtest.c create mode 100755 tcejdb/bros/mapreporter create mode 100644 tcejdb/bros/maptest.cc create mode 100644 tcejdb/bros/ndbmtest.c create mode 100644 tcejdb/bros/qdbmtest.c create mode 100755 tcejdb/bros/reporter create mode 100644 tcejdb/bros/result.xls create mode 100644 tcejdb/bros/sdbmtest.c create mode 100644 tcejdb/bros/sqltest.c create mode 100644 tcejdb/bros/tctest.c create mode 100644 tcejdb/bros/tdbtest.c create mode 100644 tcejdb/bson.c create mode 100644 tcejdb/bson.h create mode 100755 tcejdb/configure create mode 100644 tcejdb/configure.in create mode 100644 tcejdb/doc/benchmark.pdf create mode 100644 tcejdb/doc/common.css create mode 100644 tcejdb/doc/icon16.png create mode 100644 tcejdb/doc/index.html create mode 100644 tcejdb/doc/index.ja.html create mode 100644 tcejdb/doc/logo-ja.png create mode 100644 tcejdb/doc/logo.png create mode 100644 tcejdb/doc/spex-en.html create mode 100644 tcejdb/doc/spex-ja.html create mode 100644 tcejdb/doc/tokyoproducts.pdf create mode 100644 tcejdb/doc/tokyoproducts.ppt create mode 100644 tcejdb/ejdb.c create mode 100644 tcejdb/ejdb.h create mode 100644 tcejdb/ejdb_private.h create mode 100644 tcejdb/ejdbutl.h create mode 100644 tcejdb/encoding.c create mode 100644 tcejdb/encoding.h create mode 100755 tcejdb/lab/calccomp create mode 100755 tcejdb/lab/datechange create mode 100755 tcejdb/lab/diffcheck create mode 100755 tcejdb/lab/htmltotsv create mode 100644 tcejdb/lab/magic create mode 100755 tcejdb/lab/printenv.cgi create mode 100755 tcejdb/lab/stepcount create mode 100755 tcejdb/lab/stopwatch create mode 100755 tcejdb/lab/tabcheck create mode 100755 tcejdb/lab/wgettsv create mode 100755 tcejdb/lab/widthcheck create mode 100755 tcejdb/man/htmltoman create mode 100644 tcejdb/man/tcadb.3 create mode 100644 tcejdb/man/tcamgr.1 create mode 100644 tcejdb/man/tcamttest.1 create mode 100644 tcejdb/man/tcatest.1 create mode 100644 tcejdb/man/tcbdb.3 create mode 100644 tcejdb/man/tcbmgr.1 create mode 100644 tcejdb/man/tcbmttest.1 create mode 100644 tcejdb/man/tcbtest.1 create mode 100644 tcejdb/man/tcfdb.3 create mode 100644 tcejdb/man/tcfmgr.1 create mode 100644 tcejdb/man/tcfmttest.1 create mode 100644 tcejdb/man/tcftest.1 create mode 100644 tcejdb/man/tchdb.3 create mode 100644 tcejdb/man/tchmgr.1 create mode 100644 tcejdb/man/tchmttest.1 create mode 100644 tcejdb/man/tchtest.1 create mode 100644 tcejdb/man/tclist.3 create mode 100644 tcejdb/man/tcmap.3 create mode 100644 tcejdb/man/tcmdb.3 create mode 100644 tcejdb/man/tcmpool.3 create mode 100644 tcejdb/man/tctdb.3 create mode 100644 tcejdb/man/tctmgr.1 create mode 100644 tcejdb/man/tctmttest.1 create mode 100644 tcejdb/man/tctree.3 create mode 100644 tcejdb/man/tcttest.1 create mode 100644 tcejdb/man/tcucodec.1 create mode 100644 tcejdb/man/tcumttest.1 create mode 100644 tcejdb/man/tcutest.1 create mode 100644 tcejdb/man/tcutil.3 create mode 100644 tcejdb/man/tcxstr.3 create mode 100644 tcejdb/man/tokyocabinet.3 create mode 100644 tcejdb/md5.c create mode 100644 tcejdb/md5.h create mode 100644 tcejdb/myconf.c create mode 100644 tcejdb/myconf.h create mode 100644 tcejdb/nbproject/Package-Default.bash create mode 100644 tcejdb/nbproject/configurations.xml create mode 100644 tcejdb/nbproject/project.xml create mode 100644 tcejdb/numbers.c create mode 100644 tcejdb/samples/sample1/Makefile create mode 100644 tcejdb/samples/sample1/sample1.c create mode 100644 tcejdb/tcadb.c create mode 100644 tcejdb/tcadb.h create mode 100644 tcejdb/tcamgr.c create mode 100644 tcejdb/tcamttest.c create mode 100644 tcejdb/tcatest.c create mode 100644 tcejdb/tcawmgr.c create mode 100644 tcejdb/tcbdb.c create mode 100644 tcejdb/tcbdb.h create mode 100644 tcejdb/tcbmgr.c create mode 100644 tcejdb/tcbmttest.c create mode 100644 tcejdb/tcbtest.c create mode 100644 tcejdb/tcejdb.iml create mode 100644 tcejdb/tcejdb.pc.in create mode 100644 tcejdb/tcfdb.c create mode 100644 tcejdb/tcfdb.h create mode 100644 tcejdb/tcfmgr.c create mode 100644 tcejdb/tcfmttest.c create mode 100644 tcejdb/tcftest.c create mode 100644 tcejdb/tchdb.c create mode 100644 tcejdb/tchdb.h create mode 100644 tcejdb/tchmgr.c create mode 100644 tcejdb/tchmttest.c create mode 100644 tcejdb/tchtest.c create mode 100644 tcejdb/tctdb.c create mode 100644 tcejdb/tctdb.h create mode 100644 tcejdb/tctmgr.c create mode 100644 tcejdb/tctmttest.c create mode 100644 tcejdb/tcttest.c create mode 100644 tcejdb/tcucodec.c create mode 100644 tcejdb/tcumttest.c create mode 100644 tcejdb/tcutest.c create mode 100644 tcejdb/tcutil.c create mode 100644 tcejdb/tcutil.h create mode 100644 tcejdb/testejdb/Makefile create mode 100644 tcejdb/testejdb/t1.c create mode 100644 tcejdb/testejdb/t2.c create mode 100644 tcejdb/testejdb/t3.c create mode 100644 tcejdb/timsort-impl.h create mode 100644 tcejdb/timsort.c create mode 100644 tcejdb/tokyocabinet.idl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d3fa50c --- /dev/null +++ b/.gitignore @@ -0,0 +1,44 @@ +*.o +*.vlog + +/.idea/workspace.xml + +/tcejdb/Makefile +/tcejdb/autom4te.cache +/tcejdb/config.log +/tcejdb/config.status +/tcejdb/libtcejdb.a +/tcejdb/libtcejdb.so +/tcejdb/libtcejdb.so.9 +/tcejdb/libtcejdb.so.9.11.0 +/tcejdb/tcamgr +/tcejdb/tcamttest +/tcejdb/tcatest +/tcejdb/tcawmgr.cgi +/tcejdb/tcbmgr +/tcejdb/tcbmttest +/tcejdb/tcbtest +/tcejdb/tcfmgr +/tcejdb/tcfmttest +/tcejdb/tcftest +/tcejdb/tchmgr +/tcejdb/tchmttest +/tcejdb/tchtest +/tcejdb/tctmgr +/tcejdb/tctmttest +/tcejdb/tcttest +/tcejdb/tcucodec +/tcejdb/tcumttest +/tcejdb/tcutest +/tcejdb/tcejdb.pc +/tcejdb/nbproject/private +/tcejdb/check.in +/tcejdb/check.out +/tcejdb/casket* +/tcejdb/samples/sample1/sample1 +/tcejdb/samples/sample1/addressbook* + +/tcejdb/testejdb/dbt* +/tcejdb/testejdb/t1 +/tcejdb/testejdb/t2 +/tcejdb/testejdb/t3 diff --git a/t b/.gitmodules similarity index 100% rename from t rename to .gitmodules diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..2a106dd --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +ejdb \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..a1b41c5 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..3572571 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..e206d70 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..42f45c3 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,210 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..3b31283 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..1e5cd7e --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..1f4c732 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml new file mode 100644 index 0000000..922003b --- /dev/null +++ b/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..3b00020 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..275077f --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..8035c41 --- /dev/null +++ b/README.md @@ -0,0 +1,210 @@ + + +EJDB - Embedded JSON Database engine +==================================== + +The main goal of this project to create the fast [MongoDB](http://mongodb.org)-like database engine library **which can be embed +into C/C++ applications under terms of LGPL license.** + +EJDB is the C library based on modified version of [Tokio Cabinet](http://fallabs.com/tokyocabinet/). + +JSON representation of queries and data implemented with API based on [C BSON](https://github.com/mongodb/mongo-c-driver/tree/master/src/) + + +One snippet intro +----------------------------------- + +```C +#include + +static EJDB* jb; + +int main() { + jb = ejdbnew(); + if (!ejdbopen(jb, "addressbook", JDBOWRITER | JDBOCREAT | JDBOTRUNC)) { + return 1; + } + //Get or create collection 'contacts' + EJCOLL *coll = ejdbcreatecoll(jb, "contacts", NULL); + + bson bsrec; + bson_oid_t oid; + + //Insert one record: + //JSON: {'name' : 'John', 'phone' : '333-222-333', 'age' : 58} + bson_init(&bsrec); + bson_append_string(&bsrec, "name", "John"); + bson_append_string(&bsrec, "phone", "333-222-333"); + bson_append_int(&bsrec, "age", 58); + bson_finish(&bsrec); + //Save BSON + ejdbsavebson(coll, &bsrec, &oid); + fprintf(stderr, "\nSaved Travolta"); + bson_destroy(&bsrec); + + //Now execute query + //QUERY: {'name' : {'$begin' : 'Bru'}} //Name starts with 'Bru' string + bson bq1; + bson_init_as_query(&bq1); + bson_append_start_object(&bq1, "name"); + bson_append_string(&bq1, "$begin", "Bru"); + bson_append_finish_object(&bq1); + bson_finish(&bq1); + + EJQ* q1 = ejdbcreatequery(jb, &bq1, NULL, 0, NULL); + + uint32_t count; + TCLIST *res = ejdbqrysearch(coll, q1, &count, 0, NULL); + fprintf(stderr, "\n\nRecords found: %d\n", count); + + //Now print the result set records + for (int i = 0; i < TCLISTNUM(res); ++i) { + void *bsdata = TCLISTVALPTR(res, i); + bson_print_raw(stderr, bsdata, 0); + } + fprintf(stderr, "\n"); + + //Dispose result set + tclistdel(res); + + //Dispose query + ejdbquerydel(q1); + bson_destroy(&bq1); + + //Close database + ejdbclose(jb); + ejdbdel(jb); + return 0; +} +``` + +Features +================================ +* LGPL license allows you to embed this library into proprietary software. +* MongoDB like queries and overall philosophy. +* Collection level write locking. + + +Building & Installation +================================ + +Prerequisites +-------------------------------- +**Libraries:** + +* cunit +* zlib +* bzip2 + +On Debian/Ubuntu linux you can install it as following: + +```sh + sudo apt-get install libcunit1 libcunit1-dev libbz2-1.0 libbz2-dev +``` + +Building +-------------------------------- +```sh + ./configure --prefix= && make && make check + make install +``` +* library name: **tcejdb** (with pkgconfig) +* main include header: **** + +Usage +=============================== + +Basic EJDB architecture +------------------------------- +**EJDB database files structure** + +``` +. +├── +├── _ +├── ... +├── _ +└── __. +``` + +Where + +* `````` - name of database. It is metadata DB. +* `````` - name of collection. Collection database. +* `````` - JSON field path used in index +* `````` - Collection index extension: + * ```.lex``` String index + * ```.dec``` Number index + * ```.tok``` Array index + +API +--------------------------------- +EJDB API presented in **ejdb.h** C header file. + +JSON processing API: **bson.h** + +Queries +--------------------------------- + +```C +** + * Create query object. + * Sucessfully created queries must be destroyed with ejdbquerydel(). + * + * EJDB queries inspired by MongoDB (mongodb.org) and follows same philosophy. + * + * - Supported queries: + * - Simple matching of String OR Number OR Array value: + * - {'bson.field.path' : 'val', ...} + * - $not Negate operation. + * - {'json.field.path' : {'$not' : val}} //Field not equal to val + * - {'json.field.path' : {'$not' : {'$begin' : prefix}}} //Field not begins with val + * - $begin String starts with prefix + * - {'json.field.path' : {'$begin' : prefix}} + * - $gt, $gte (>, >=) and $lt, $lte for number types: + * - {'json.field.path' : {'$gt' : number}, ...} + * - $bt Between for number types: + * - {'json.field.path' : {'$bt' : [num1, num2]}} + * - $in String OR Number OR Array val matches to value in specified array: + * - {'json.field.path' : {'$in' : [val1, val2, val3]}} + * - $nin - Not IN + * - $strand String tokens OR String array val matches all tokens in specified array: + * - {'json.field.path' : {'$strand' : [val1, val2, val3]}} + * - $stror String tokens OR String array val matches any token in specified array: + * - {'json.field.path' : {'$stror' : [val1, val2, val3]}} + * + * NOTE: Negate operations: $not and $nin not using indexes + * so they can be slow in comparison to other matching operations. + * + * NOTE: Only one index can be used in search query operation. + * + * QUERY HINTS (specified by `hints` argument): + * - $max Maximum number in the result set + * - $skip Number of skipped results in the result set + * - $orderby Sorting order of query fields. + * Eg: ORDER BY field1 ASC, field2 DESC + * hints: { + * "$orderby" : { + * "field1" : 1, + * "field2" : -1 + * } + * } + * + * Many query examples can be found in `testejdb/t2.c` test case. + * + * @param EJDB database handle. + * @param qobj Main BSON query object. + * @param orqobjs Array of additional OR query objects (joined with OR predicate). + * @param orqobjsnum Number of OR query objects. + * @param hints BSON object with query hints. + * @return On success return query handle. On error returns NULL. + */ +EJDB_EXPORT EJQ* ejdbcreatequery(EJDB *jb, bson *qobj, bson *orqobjs, int orqobjsnum, bson *hints); +``` + +Examples +------------------------------------ +You can find some code samples in: + +* /samples +* /testejdb test cases diff --git a/tcejdb/COPYING b/tcejdb/COPYING new file mode 100644 index 0000000..b1e3f5a --- /dev/null +++ b/tcejdb/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/tcejdb/ChangeLog b/tcejdb/ChangeLog new file mode 100644 index 0000000..ae20b39 --- /dev/null +++ b/tcejdb/ChangeLog @@ -0,0 +1,3 @@ +2012-10-27 Softmotions. + - Initial release based on Tokyo Cabinet v1.4.48 + - Release 1.0.0 diff --git a/tcejdb/Makefile.in b/tcejdb/Makefile.in new file mode 100644 index 0000000..33f9782 --- /dev/null +++ b/tcejdb/Makefile.in @@ -0,0 +1,839 @@ +# Makefile for Tokyo Cabinet + + + +#================================================================ +# Setting Variables +#================================================================ + + +# Generic settings +SHELL = @SHELL@ + +# Package information +PACKAGE = @PACKAGE_NAME@ +VERSION = @PACKAGE_VERSION@ +PACKAGEDIR = $(PACKAGE) +PACKAGETGZ = $(PACKAGE)-$(VERSION).tar.gz +LIBVER = @MYLIBVER@ +LIBREV = @MYLIBREV@ +FORMATVER = @MYFORMATVER@ + +# Targets +HEADERFILES = @MYHEADERFILES@ +LIBRARYFILES = @MYLIBRARYFILES@ +LIBOBJFILES = @MYLIBOBJFILES@ +COMMANDFILES = @MYCOMMANDFILES@ +CGIFILES = @MYCGIFILES@ +MAN1FILES = @MYMAN1FILES@ +MAN3FILES = @MYMAN3FILES@ +DOCUMENTFILES = @MYDOCUMENTFILES@ +PCFILES = @MYPCFILES@ + +# Install destinations +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datarootdir = @datarootdir@ +INCLUDEDIR = @includedir@ +LIBDIR = @libdir@ +BINDIR = @bindir@ +LIBEXECDIR = @libexecdir@ +DATADIR = @datadir@/$(PACKAGE) +MAN1DIR = @mandir@/man1 +MAN3DIR = @mandir@/man3 +PCDIR = @libdir@/pkgconfig +DESTDIR = + +# Building configuration +CC = @CC@ +CPPFLAGS = @MYCPPFLAGS@ \ + -D_TC_PREFIX="\"$(prefix)\"" -D_TC_INCLUDEDIR="\"$(INCLUDEDIR)\"" \ + -D_TC_LIBDIR="\"$(LIBDIR)\"" -D_TC_BINDIR="\"$(BINDIR)\"" -D_TC_LIBEXECDIR="\"$(LIBEXECDIR)\"" \ + -D_TC_APPINC="\"-I$(INCLUDEDIR)\"" -D_TC_APPLIBS="\"-L$(LIBDIR) -ltcejdb @LIBS@\"" +CFLAGS = @MYCFLAGS@ +LDFLAGS = @MYLDFLAGS@ +CMDLDFLAGS = @MYCMDLDFLAGS@ +LIBS = @LIBS@ +LDENV = LD_RUN_PATH=/lib:/usr/lib:$(LIBDIR):$(HOME)/lib:/usr/local/lib:@MYRUNPATH@:. +RUNENV = @MYLDLIBPATHENV@=.:/lib:/usr/lib:$(LIBDIR):$(HOME)/lib:/usr/local/lib:@MYRUNPATH@ +POSTCMD = @MYPOSTCMD@ + + + +#================================================================ +# Suffix rules +#================================================================ + + +.SUFFIXES : +.SUFFIXES : .c .o + +.c.o : + $(CC) -c $(CPPFLAGS) $(CFLAGS) $< + + + +#================================================================ +# Actions +#================================================================ + + +all : $(LIBRARYFILES) $(COMMANDFILES) $(CGIFILES) + @$(POSTCMD) + @printf '\n' + @printf '#================================================================\n' + @printf '# Ready to install.\n' + @printf '#================================================================\n' + + +clean : + rm -rf $(LIBRARYFILES) $(LIBOBJFILES) $(COMMANDFILES) $(CGIFILES) \ + *.o a.out tokyocabinet_all.c check.in check.out gmon.out *.vlog words.tsv \ + casket casket-* casket.* *.tch *.tcb *.tcf *.tct *.idx.* *.wal *~ hoge moge tako ika + make -C ./testejdb clean + + +version : + vernum=`expr $(LIBVER)00 + $(LIBREV)` ; \ + sed -e 's/_TC_VERSION.*/_TC_VERSION "$(VERSION)"/' \ + -e "s/_TC_LIBVER.*/_TC_LIBVER $$vernum/" \ + -e 's/_TC_FORMATVER.*/_TC_FORMATVER "$(FORMATVER)"/' tcutil.h > tcutil.h~ + [ -f tcutil.h~ ] && mv -f tcutil.h~ tcutil.h + + +untabify : + ls *.c *.h *.idl | while read name ; \ + do \ + sed -e 's/\t/ /g' -e 's/ *$$//' $$name > $$name~; \ + [ -f $$name~ ] && mv -f $$name~ $$name ; \ + done + + +install : + mkdir -p $(DESTDIR)$(INCLUDEDIR)/$(PACKAGE) + cp -Rf $(HEADERFILES) $(DESTDIR)$(INCLUDEDIR)/$(PACKAGE) + mkdir -p $(DESTDIR)$(LIBDIR) + cp -Rf $(LIBRARYFILES) $(DESTDIR)$(LIBDIR) + ## mkdir -p $(DESTDIR)$(BINDIR) + ## cp -Rf $(COMMANDFILES) $(DESTDIR)$(BINDIR) + ## mkdir -p $(DESTDIR)$(LIBEXECDIR) + ## cp -Rf $(CGIFILES) $(DESTDIR)$(LIBEXECDIR) + mkdir -p $(DESTDIR)$(DATADIR) + cp -Rf $(DOCUMENTFILES) $(DESTDIR)$(DATADIR) + ## mkdir -p $(DESTDIR)$(MAN1DIR) + ## cd man && cp -Rf $(MAN1FILES) $(DESTDIR)$(MAN1DIR) + ## mkdir -p $(DESTDIR)$(MAN3DIR) + ## cd man && cp -Rf $(MAN3FILES) $(DESTDIR)$(MAN3DIR) + mkdir -p $(DESTDIR)$(PCDIR) + cp -Rf $(PCFILES) $(DESTDIR)$(PCDIR) + -[ "$$UID" = 0 ] && PATH=/sbin:/usr/sbin:$(PATH) ldconfig 2>/dev/null || true + @printf '\n' + @printf '#================================================================\n' + @printf '# Thanks for using Tokyo Cabinet EJDB edition.\n' + @printf '#================================================================\n' + + +install-strip : + make DESTDIR=$(DESTDIR) install + cd $(DESTDIR)$(BINDIR) && strip $(COMMANDFILES) + + +uninstall : + - cd $(DESTDIR)$(INCLUDEDIR)/$(PACKAGE) && rm -f $(HEADERFILES) + - cd $(DESTDIR)$(LIBDIR) && rm -f $(LIBRARYFILES) + ## cd $(DESTDIR)$(BINDIR) && rm -f $(COMMANDFILES) + ## cd $(DESTDIR)$(LIBEXECDIR) && rm -f $(CGIFILES) + ## cd $(DESTDIR)$(MAN1DIR) && rm -f $(MAN1FILES) + ## cd $(DESTDIR)$(MAN3DIR) && rm -f $(MAN3FILES) + - rm -rf $(DESTDIR)$(DATADIR) + - cd $(DESTDIR)$(PCDIR) && rm -f $(PCFILES) + [ "$$UID" = 0 ] && PATH=/sbin:/usr/sbin:$(PATH) ldconfig 2>/dev/null || true + + +dist : + make version + make untabify + make distclean + cd .. && tar cvf - $(PACKAGEDIR) | gzip -c > $(PACKAGETGZ) + sync ; sync + + +distclean : clean + ## cd example && make clean + cd bros && make clean + rm -rf Makefile tcejdb.pc config.cache config.log config.status autom4te.cache + + +check : + make check-util + make check-hdb + make check-bdb + make check-fdb + make check-tdb + make check-adb + make check-ejdb + rm -rf casket* + @printf '\n' + @printf '#================================================================\n' + @printf '# Checking completed.\n' + @printf '#================================================================\n' + + +check-util : + rm -rf casket* + $(RUNENV) $(RUNCMD) ./tcamgr version + $(RUNENV) $(RUNCMD) ./tcutest xstr 50000 + $(RUNENV) $(RUNCMD) ./tcutest list -rd 50000 + $(RUNENV) $(RUNCMD) ./tcutest map -rd -tr 50000 + $(RUNENV) $(RUNCMD) ./tcutest map -rd -tr -rnd -dc 50000 + $(RUNENV) $(RUNCMD) ./tcutest tree -rd -tr 50000 + $(RUNENV) $(RUNCMD) ./tcutest tree -rd -tr -rnd -dc 50000 + $(RUNENV) $(RUNCMD) ./tcutest mdb -rd -tr 50000 + $(RUNENV) $(RUNCMD) ./tcutest mdb -rd -tr -rnd -dc 50000 + $(RUNENV) $(RUNCMD) ./tcutest mdb -rd -tr -rnd -dpr 50000 + $(RUNENV) $(RUNCMD) ./tcutest ndb -rd -tr 50000 + $(RUNENV) $(RUNCMD) ./tcutest ndb -rd -tr -rnd -dc 50000 + $(RUNENV) $(RUNCMD) ./tcutest ndb -rd -tr -rnd -dpr 50000 + $(RUNENV) $(RUNCMD) ./tcutest misc 500 + $(RUNENV) $(RUNCMD) ./tcutest wicked 50000 + $(RUNENV) $(RUNCMD) ./tcumttest combo 5 50000 500 + $(RUNENV) $(RUNCMD) ./tcumttest combo -rnd 5 50000 500 + $(RUNENV) $(RUNCMD) ./tcumttest typical 5 50000 5000 + $(RUNENV) $(RUNCMD) ./tcumttest typical -rr 1000 5 50000 5000 + $(RUNENV) $(RUNCMD) ./tcumttest typical -nc 5 50000 5000 + $(RUNENV) $(RUNCMD) ./tcumttest combo -tr 5 50000 500 + $(RUNENV) $(RUNCMD) ./tcumttest combo -tr -rnd 5 50000 500 + $(RUNENV) $(RUNCMD) ./tcumttest typical -tr 5 50000 5000 + $(RUNENV) $(RUNCMD) ./tcumttest typical -tr -rr 1000 5 50000 5000 + $(RUNENV) $(RUNCMD) ./tcumttest typical -tr -nc 5 50000 5000 + $(RUNENV) $(RUNCMD) ./tcucodec url Makefile > check.in + $(RUNENV) $(RUNCMD) ./tcucodec url -d check.in > check.out + $(RUNENV) $(RUNCMD) ./tcucodec base Makefile > check.in + $(RUNENV) $(RUNCMD) ./tcucodec base -d check.in > check.out + $(RUNENV) $(RUNCMD) ./tcucodec quote Makefile > check.in + $(RUNENV) $(RUNCMD) ./tcucodec quote -d check.in > check.out + $(RUNENV) $(RUNCMD) ./tcucodec mime Makefile > check.in + $(RUNENV) $(RUNCMD) ./tcucodec mime -d check.in > check.out + $(RUNENV) $(RUNCMD) ./tcucodec pack -bwt Makefile > check.in + $(RUNENV) $(RUNCMD) ./tcucodec pack -d -bwt check.in > check.out + $(RUNENV) $(RUNCMD) ./tcucodec tcbs Makefile > check.in + $(RUNENV) $(RUNCMD) ./tcucodec tcbs -d check.in > check.out + $(RUNENV) $(RUNCMD) ./tcucodec zlib Makefile > check.in + $(RUNENV) $(RUNCMD) ./tcucodec zlib -d check.in > check.out + $(RUNENV) $(RUNCMD) ./tcucodec xml Makefile > check.in + $(RUNENV) $(RUNCMD) ./tcucodec xml -d check.in > check.out + $(RUNENV) $(RUNCMD) ./tcucodec cstr Makefile > check.in + $(RUNENV) $(RUNCMD) ./tcucodec cstr -d check.in > check.out + $(RUNENV) $(RUNCMD) ./tcucodec ucs Makefile > check.in + $(RUNENV) $(RUNCMD) ./tcucodec ucs -d check.in > check.out + $(RUNENV) $(RUNCMD) ./tcucodec date -ds '1978-02-11T18:05:30+09:00' -rf > check.out + $(RUNENV) $(RUNCMD) ./tcucodec cipher -key "mikio" Makefile > check.in + $(RUNENV) $(RUNCMD) ./tcucodec cipher -key "mikio" check.in > check.out + $(RUNENV) $(RUNCMD) ./tcucodec tmpl -var name mikio -var nick micky \ + '@name=[%name%][%IF nick%] nick=[%nick%][%END%][%IF hoge%][%ELSE%].[%END%]' > check.out + $(RUNENV) $(RUNCMD) ./tcucodec conf > check.out + rm -rf casket* + + +check-hdb : + rm -rf casket* + $(RUNENV) $(RUNCMD) ./tchtest write casket 50000 5000 5 5 + $(RUNENV) $(RUNCMD) ./tchtest read casket + $(RUNENV) $(RUNCMD) ./tchtest remove casket + $(RUNENV) $(RUNCMD) ./tchtest write -mt -tl -td -rc 50 -xm 500000 casket 50000 5000 5 5 + $(RUNENV) $(RUNCMD) ./tchtest read -mt -nb -rc 50 -xm 500000 casket + $(RUNENV) $(RUNCMD) ./tchtest remove -mt -rc 50 -xm 500000 casket + $(RUNENV) $(RUNCMD) ./tchtest write -as -tb -rc 50 -xm 500000 casket 50000 50000 5 5 + $(RUNENV) $(RUNCMD) ./tchtest read -nl -rc 50 -xm 500000 casket + $(RUNENV) $(RUNCMD) ./tchtest remove -rc 50 -xm 500000 -df 5 casket + $(RUNENV) $(RUNCMD) ./tchtest rcat -pn 500 -xm 50000 -df 5 casket 50000 5000 5 5 + $(RUNENV) $(RUNCMD) ./tchtest rcat -tl -td -pn 5000 casket 50000 500 5 15 + $(RUNENV) $(RUNCMD) ./tchtest rcat -nl -pn 500 -rl casket 5000 500 5 5 + $(RUNENV) $(RUNCMD) ./tchtest rcat -tb -pn 500 casket 5000 500 5 5 + $(RUNENV) $(RUNCMD) ./tchtest rcat -ru -pn 500 casket 5000 500 1 1 + $(RUNENV) $(RUNCMD) ./tchtest rcat -tl -td -ru -pn 500 casket 5000 500 1 1 + $(RUNENV) $(RUNCMD) ./tchmgr list -pv casket > check.out + $(RUNENV) $(RUNCMD) ./tchmgr list -pv -fm 1 -px casket > check.out + $(RUNENV) $(RUNCMD) ./tchtest misc casket 5000 + $(RUNENV) $(RUNCMD) ./tchtest misc -tl -td casket 5000 + $(RUNENV) $(RUNCMD) ./tchtest misc -mt -tb casket 500 + $(RUNENV) $(RUNCMD) ./tchtest wicked casket 50000 + $(RUNENV) $(RUNCMD) ./tchtest wicked -tl -td casket 50000 + $(RUNENV) $(RUNCMD) ./tchtest wicked -mt -tb casket 5000 + $(RUNENV) $(RUNCMD) ./tchtest wicked -tt casket 5000 + $(RUNENV) $(RUNCMD) ./tchtest wicked -tx casket 5000 + $(RUNENV) $(RUNCMD) ./tchmttest write -xm 500000 -df 5 -tl casket 5 5000 500 5 + $(RUNENV) $(RUNCMD) ./tchmttest read -xm 500000 -df 5 casket 5 + $(RUNENV) $(RUNCMD) ./tchmttest read -xm 500000 -rnd casket 5 + $(RUNENV) $(RUNCMD) ./tchmttest remove -xm 500000 casket 5 + $(RUNENV) $(RUNCMD) ./tchmttest wicked -nc casket 5 5000 + $(RUNENV) $(RUNCMD) ./tchmttest wicked -tl -td casket 5 5000 + $(RUNENV) $(RUNCMD) ./tchmttest wicked -tb casket 5 5000 + $(RUNENV) $(RUNCMD) ./tchmttest typical -df 5 casket 5 50000 5000 + $(RUNENV) $(RUNCMD) ./tchmttest typical -rr 1000 casket 5 50000 5000 + $(RUNENV) $(RUNCMD) ./tchmttest typical -tl -rc 50000 -nc casket 5 50000 5000 + $(RUNENV) $(RUNCMD) ./tchmttest race -df 5 casket 5 10000 + $(RUNENV) $(RUNCMD) ./tchmgr create casket 3 1 1 + $(RUNENV) $(RUNCMD) ./tchmgr inform casket + $(RUNENV) $(RUNCMD) ./tchmgr put casket one first + $(RUNENV) $(RUNCMD) ./tchmgr put casket two second + $(RUNENV) $(RUNCMD) ./tchmgr put -dk casket three third + $(RUNENV) $(RUNCMD) ./tchmgr put -dc casket three third + $(RUNENV) $(RUNCMD) ./tchmgr put -dc casket three third + $(RUNENV) $(RUNCMD) ./tchmgr put -dc casket three third + $(RUNENV) $(RUNCMD) ./tchmgr put casket four fourth + $(RUNENV) $(RUNCMD) ./tchmgr put -dk casket five fifth + $(RUNENV) $(RUNCMD) ./tchmgr out casket one + $(RUNENV) $(RUNCMD) ./tchmgr out casket two + $(RUNENV) $(RUNCMD) ./tchmgr get casket three > check.out + $(RUNENV) $(RUNCMD) ./tchmgr get casket four > check.out + $(RUNENV) $(RUNCMD) ./tchmgr get casket five > check.out + $(RUNENV) $(RUNCMD) ./tchmgr list -pv casket > check.out + $(RUNENV) $(RUNCMD) ./tchmgr optimize casket + $(RUNENV) $(RUNCMD) ./tchmgr put -dc casket three third + $(RUNENV) $(RUNCMD) ./tchmgr get casket three > check.out + $(RUNENV) $(RUNCMD) ./tchmgr get casket four > check.out + $(RUNENV) $(RUNCMD) ./tchmgr get casket five > check.out + $(RUNENV) $(RUNCMD) ./tchmgr list -pv casket > check.out + rm -rf casket* + + +check-bdb : + rm -rf casket* + $(RUNENV) $(RUNCMD) ./tcbtest write casket 50000 5 5 5000 5 5 + $(RUNENV) $(RUNCMD) ./tcbtest read casket + $(RUNENV) $(RUNCMD) ./tcbtest remove casket + $(RUNENV) $(RUNCMD) ./tcbmgr list -rb 00001000 00002000 casket > check.out + $(RUNENV) $(RUNCMD) ./tcbmgr list -fm 000001 casket > check.out + $(RUNENV) $(RUNCMD) ./tcbtest write -mt -tl -td -ls 1024 casket 50000 5000 5000 5000 5 5 + $(RUNENV) $(RUNCMD) ./tcbtest read -mt -nb casket + $(RUNENV) $(RUNCMD) ./tcbtest remove -mt casket + $(RUNENV) $(RUNCMD) ./tcbtest write -tb -xm 50000 casket 50000 5 5 50000 5 5 + $(RUNENV) $(RUNCMD) ./tcbtest read -nl casket + $(RUNENV) $(RUNCMD) ./tcbtest remove -df 5 casket + $(RUNENV) $(RUNCMD) ./tcbtest rcat -lc 5 -nc 5 -df 5 -pn 500 casket 50000 5 5 5000 5 5 + $(RUNENV) $(RUNCMD) ./tcbtest rcat -tl -td -pn 5000 casket 50000 5 5 500 5 15 + $(RUNENV) $(RUNCMD) ./tcbtest rcat -nl -pn 5000 -rl casket 15000 5 5 500 5 5 + $(RUNENV) $(RUNCMD) ./tcbtest rcat -ca 1000 -tb -pn 5000 casket 15000 5 5 500 5 5 + $(RUNENV) $(RUNCMD) ./tcbtest rcat -ru -pn 500 casket 5000 5 5 500 1 1 + $(RUNENV) $(RUNCMD) ./tcbtest rcat -cd -tl -td -ru -pn 500 casket 5000 5 5 500 1 1 + $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out + $(RUNENV) $(RUNCMD) ./tcbtest queue casket 15000 5 5 + $(RUNENV) $(RUNCMD) ./tcbtest misc casket 5000 + $(RUNENV) $(RUNCMD) ./tcbtest misc -tl -td casket 5000 + $(RUNENV) $(RUNCMD) ./tcbtest misc -mt -tb casket 500 + $(RUNENV) $(RUNCMD) ./tcbtest wicked casket 50000 + $(RUNENV) $(RUNCMD) ./tcbtest wicked -tl -td casket 50000 + $(RUNENV) $(RUNCMD) ./tcbtest wicked -mt -tb casket 5000 + $(RUNENV) $(RUNCMD) ./tcbtest wicked -tt casket 5000 + $(RUNENV) $(RUNCMD) ./tcbtest wicked -tx casket 5000 + $(RUNENV) $(RUNCMD) ./tcbtest write -cd -lc 5 -nc 5 casket 5000 5 5 5 5 5 + $(RUNENV) $(RUNCMD) ./tcbtest read -cd -lc 5 -nc 5 casket + $(RUNENV) $(RUNCMD) ./tcbtest remove -cd -lc 5 -nc 5 casket + $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out + $(RUNENV) $(RUNCMD) ./tcbtest write -ci -td -lc 5 -nc 5 casket 5000 5 5 5 5 5 + $(RUNENV) $(RUNCMD) ./tcbtest read -ci -lc 5 -nc 5 casket + $(RUNENV) $(RUNCMD) ./tcbtest remove -ci -lc 5 -nc 5 casket + $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out + $(RUNENV) $(RUNCMD) ./tcbtest write -cj -tb -lc 5 -nc 5 casket 5000 5 5 5 5 5 + $(RUNENV) $(RUNCMD) ./tcbtest read -cj -lc 5 -nc 5 casket + $(RUNENV) $(RUNCMD) ./tcbtest remove -cj -lc 5 -nc 5 casket + $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out + $(RUNENV) $(RUNCMD) ./tcbmttest write -df 5 -tl casket 5 5000 5 5 500 5 + $(RUNENV) $(RUNCMD) ./tcbmttest read -df 5 casket 5 + $(RUNENV) $(RUNCMD) ./tcbmttest read -rnd casket 5 + $(RUNENV) $(RUNCMD) ./tcbmttest remove casket 5 + $(RUNENV) $(RUNCMD) ./tcbmttest wicked -nc casket 5 5000 + $(RUNENV) $(RUNCMD) ./tcbmttest wicked -tl -td casket 5 5000 + $(RUNENV) $(RUNCMD) ./tchmttest wicked -tb casket 5 5000 + $(RUNENV) $(RUNCMD) ./tcbmttest typical -df 5 casket 5 50000 5 5 + $(RUNENV) $(RUNCMD) ./tcbmttest typical -rr 1000 casket 5 50000 5 5 + $(RUNENV) $(RUNCMD) ./tcbmttest typical -tl -nc casket 5 50000 5 5 + $(RUNENV) $(RUNCMD) ./tcbmttest race -df 5 casket 5 10000 + $(RUNENV) $(RUNCMD) ./tcbmgr create casket 4 4 3 1 1 + $(RUNENV) $(RUNCMD) ./tcbmgr inform casket + $(RUNENV) $(RUNCMD) ./tcbmgr put casket one first + $(RUNENV) $(RUNCMD) ./tcbmgr put casket two second + $(RUNENV) $(RUNCMD) ./tcbmgr put -dk casket three third + $(RUNENV) $(RUNCMD) ./tcbmgr put -dc casket three third + $(RUNENV) $(RUNCMD) ./tcbmgr put -dc casket three third + $(RUNENV) $(RUNCMD) ./tcbmgr put -dd casket three third + $(RUNENV) $(RUNCMD) ./tcbmgr put -dd casket three third + $(RUNENV) $(RUNCMD) ./tcbmgr put casket four fourth + $(RUNENV) $(RUNCMD) ./tcbmgr put -dk casket five fifth + $(RUNENV) $(RUNCMD) ./tcbmgr out casket one + $(RUNENV) $(RUNCMD) ./tcbmgr out casket two + $(RUNENV) $(RUNCMD) ./tcbmgr get casket three > check.out + $(RUNENV) $(RUNCMD) ./tcbmgr get casket four > check.out + $(RUNENV) $(RUNCMD) ./tcbmgr get casket five > check.out + $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out + $(RUNENV) $(RUNCMD) ./tcbmgr list -j three -pv casket > check.out + $(RUNENV) $(RUNCMD) ./tcbmgr optimize casket + $(RUNENV) $(RUNCMD) ./tcbmgr put -dc casket three third + $(RUNENV) $(RUNCMD) ./tcbmgr get casket three > check.out + $(RUNENV) $(RUNCMD) ./tcbmgr get casket four > check.out + $(RUNENV) $(RUNCMD) ./tcbmgr get casket five > check.out + $(RUNENV) $(RUNCMD) ./tcbmgr list -pv casket > check.out + + +check-fdb : + rm -rf casket* + $(RUNENV) $(RUNCMD) ./tcftest write casket 50000 50 + $(RUNENV) $(RUNCMD) ./tcftest read casket + $(RUNENV) $(RUNCMD) ./tcftest remove casket + $(RUNENV) $(RUNCMD) ./tcftest write casket 50000 50 + $(RUNENV) $(RUNCMD) ./tcftest read -mt -nb casket + $(RUNENV) $(RUNCMD) ./tcftest remove -mt casket + $(RUNENV) $(RUNCMD) ./tcftest rcat -pn 500 casket 50000 50 + $(RUNENV) $(RUNCMD) ./tcftest rcat -nl -pn 500 -rl casket 5000 500 + $(RUNENV) $(RUNCMD) ./tcftest rcat -pn 500 -ru casket 5000 500 + $(RUNENV) $(RUNCMD) ./tcfmgr list -pv casket > check.out + $(RUNENV) $(RUNCMD) ./tcfmgr list -pv -ri "[100,200)" -px casket > check.out + $(RUNENV) $(RUNCMD) ./tcftest misc casket 5000 + $(RUNENV) $(RUNCMD) ./tcftest misc -mt -nl casket 500 + $(RUNENV) $(RUNCMD) ./tcftest wicked casket 50000 + $(RUNENV) $(RUNCMD) ./tcftest wicked -mt -nb casket 50000 + $(RUNENV) $(RUNCMD) ./tcfmttest write casket 5 5000 50 + $(RUNENV) $(RUNCMD) ./tcfmttest read casket 5 + $(RUNENV) $(RUNCMD) ./tcfmttest read -rnd casket 5 + $(RUNENV) $(RUNCMD) ./tcfmttest remove casket 5 + $(RUNENV) $(RUNCMD) ./tcfmttest wicked -nc casket 5 5000 + $(RUNENV) $(RUNCMD) ./tcfmttest wicked casket 5 5000 + $(RUNENV) $(RUNCMD) ./tcfmttest typical casket 5 50000 50 + $(RUNENV) $(RUNCMD) ./tcfmttest typical -rr 1000 casket 5 50000 50 + $(RUNENV) $(RUNCMD) ./tcfmttest typical -nc casket 5 50000 50 + $(RUNENV) $(RUNCMD) ./tcfmgr create casket 50 + $(RUNENV) $(RUNCMD) ./tcfmgr inform casket + $(RUNENV) $(RUNCMD) ./tcfmgr put casket 1 first + $(RUNENV) $(RUNCMD) ./tcfmgr put casket 2 second + $(RUNENV) $(RUNCMD) ./tcfmgr put -dk casket 3 third + $(RUNENV) $(RUNCMD) ./tcfmgr put -dc casket 3 third + $(RUNENV) $(RUNCMD) ./tcfmgr put -dc casket 3 third + $(RUNENV) $(RUNCMD) ./tcfmgr put -dc casket 3 third + $(RUNENV) $(RUNCMD) ./tcfmgr put casket 4 fourth + $(RUNENV) $(RUNCMD) ./tcfmgr put -dk casket 5 fifth + $(RUNENV) $(RUNCMD) ./tcfmgr out casket 1 + $(RUNENV) $(RUNCMD) ./tcfmgr out casket 2 + $(RUNENV) $(RUNCMD) ./tcfmgr get casket 3 > check.out + $(RUNENV) $(RUNCMD) ./tcfmgr get casket 4 > check.out + $(RUNENV) $(RUNCMD) ./tcfmgr get casket 5 > check.out + $(RUNENV) $(RUNCMD) ./tcfmgr list -pv casket > check.out + $(RUNENV) $(RUNCMD) ./tcfmgr optimize casket 5 + $(RUNENV) $(RUNCMD) ./tcfmgr put -dc casket 3 third + $(RUNENV) $(RUNCMD) ./tcfmgr get casket 3 > check.out + $(RUNENV) $(RUNCMD) ./tcfmgr get casket 4 > check.out + $(RUNENV) $(RUNCMD) ./tcfmgr get casket 5 > check.out + $(RUNENV) $(RUNCMD) ./tcfmgr list -pv casket > check.out + + +check-tdb : + rm -rf casket* + $(RUNENV) $(RUNCMD) ./tcttest write casket 50000 5000 5 5 + $(RUNENV) $(RUNCMD) ./tcttest read casket + $(RUNENV) $(RUNCMD) ./tcttest remove casket + $(RUNENV) $(RUNCMD) ./tcttest write -mt -tl -td -rc 50 -lc 5 -nc 5 -xm 500000 \ + -is -in -it -if -ix casket 5000 5000 5 5 + $(RUNENV) $(RUNCMD) ./tcttest read -mt -nb -rc 50 -lc 5 -nc 5 -xm 500000 casket + $(RUNENV) $(RUNCMD) ./tcttest remove -mt -rc 50 -lc 5 -nc 5 -xm 500000 -df 5 casket + $(RUNENV) $(RUNCMD) ./tcttest rcat -pn 500 -xm 50000 -df 5 -is casket 5000 5000 5 5 + $(RUNENV) $(RUNCMD) ./tcttest rcat -tl -td -pn 5000 -is -in casket 5000 500 5 15 + $(RUNENV) $(RUNCMD) ./tcttest rcat -nl -pn 500 -rl -is -in casket 5000 500 5 5 + $(RUNENV) $(RUNCMD) ./tcttest rcat -tb -pn 500 -is -in casket 5000 500 5 5 + $(RUNENV) $(RUNCMD) ./tcttest rcat -ru -pn 500 -is -in casket 5000 500 1 1 + $(RUNENV) $(RUNCMD) ./tcttest rcat -tl -td -ru -pn 500 -is -in casket 5000 500 1 1 + $(RUNENV) $(RUNCMD) ./tctmgr list -pv casket > check.out + $(RUNENV) $(RUNCMD) ./tctmgr list -pv -px casket > check.out + $(RUNENV) $(RUNCMD) ./tcttest misc casket 500 + $(RUNENV) $(RUNCMD) ./tcttest misc -tl -td casket 500 + $(RUNENV) $(RUNCMD) ./tcttest misc -mt -tb casket 500 + $(RUNENV) $(RUNCMD) ./tcttest wicked casket 5000 + $(RUNENV) $(RUNCMD) ./tcttest wicked -tl -td casket 5000 + $(RUNENV) $(RUNCMD) ./tcttest wicked -mt -tb casket 5000 + $(RUNENV) $(RUNCMD) ./tcttest wicked -tt casket 5000 + $(RUNENV) $(RUNCMD) ./tcttest wicked -tx casket 5000 + $(RUNENV) $(RUNCMD) ./tctmttest write -xm 500000 -df 5 -tl -is -in casket 5 5000 500 5 + $(RUNENV) $(RUNCMD) ./tctmttest read -xm 500000 -df 5 casket 5 + $(RUNENV) $(RUNCMD) ./tctmttest read -xm 500000 -rnd casket 5 + $(RUNENV) $(RUNCMD) ./tctmttest remove -xm 500000 casket 5 + $(RUNENV) $(RUNCMD) ./tctmttest wicked casket 5 5000 + $(RUNENV) $(RUNCMD) ./tctmttest wicked -tl -td casket 5 5000 + $(RUNENV) $(RUNCMD) ./tctmttest typical -df 5 casket 5 5000 500 + $(RUNENV) $(RUNCMD) ./tctmttest typical -rr 1000 casket 5 5000 500 + $(RUNENV) $(RUNCMD) ./tctmttest typical -tl -rc 50000 -lc 5 -nc 5 casket 5 5000 500 + $(RUNENV) $(RUNCMD) ./tctmgr create casket 3 1 1 + $(RUNENV) $(RUNCMD) ./tctmgr setindex casket name + $(RUNENV) $(RUNCMD) ./tctmgr inform casket + $(RUNENV) $(RUNCMD) ./tctmgr put casket "" name mikio birth 19780211 lang ja,en,c + $(RUNENV) $(RUNCMD) ./tctmgr put casket "" name fal birth 19771007 lang ja + $(RUNENV) $(RUNCMD) ./tctmgr put casket "" name banana price 100 + $(RUNENV) $(RUNCMD) ./tctmgr put -dc casket 3 color yellow + $(RUNENV) $(RUNCMD) ./tctmgr put -dk casket "" name melon price 1200 color green + $(RUNENV) $(RUNCMD) ./tctmgr put casket "" name void birth 20010101 lang en + $(RUNENV) $(RUNCMD) ./tctmgr out casket 5 + $(RUNENV) $(RUNCMD) ./tctmgr get casket 1 > check.out + $(RUNENV) $(RUNCMD) ./tctmgr get casket 2 > check.out + $(RUNENV) $(RUNCMD) ./tctmgr get casket 3 > check.out + $(RUNENV) $(RUNCMD) ./tctmgr search casket > check.out + $(RUNENV) $(RUNCMD) ./tctmgr search -m 10 -sk 1 -pv -ph casket > check.out + $(RUNENV) $(RUNCMD) ./tctmgr search -m 10 -ord name STRDESC -pv -ph casket > check.out + $(RUNENV) $(RUNCMD) ./tctmgr search -m 10 -ord name STRDESC -pv -ph casket \ + name STRBW mi birth NUMBT 19700101,19791231 lang STRAND ja,en > check.out + $(RUNENV) $(RUNCMD) ./tctmgr search -ord birth NUMDESC -pv -ms UNION casket \ + name STREQ mikio name STRINC fal name FTSEX "ba na na" + $(RUNENV) $(RUNCMD) ./tctmgr setindex casket name + $(RUNENV) $(RUNCMD) ./tctmgr setindex -it dec casket birth + $(RUNENV) $(RUNCMD) ./tctmgr setindex casket lang + $(RUNENV) $(RUNCMD) ./tctmgr list -pv casket > check.out + $(RUNENV) $(RUNCMD) ./tctmgr optimize casket + $(RUNENV) $(RUNCMD) ./tctmgr put casket "" name tokyo country japan lang ja + $(RUNENV) $(RUNCMD) ./tctmgr search -m 10 -sk 1 -pv -ph casket > check.out + $(RUNENV) $(RUNCMD) ./tctmgr search -m 10 -ord name STRDESC -pv -ph casket > check.out + $(RUNENV) $(RUNCMD) ./tctmgr search -m 10 -ord name STRDESC -pv -ph casket \ + name STRBW mi birth NUMBT 19700101,19791231 lang STRAND ja,en > check.out + $(RUNENV) $(RUNCMD) ./tctmgr search -ord price NUMDESC -ph -rm casket name STRINC a + $(RUNENV) $(RUNCMD) ./tctmgr list -pv casket > check.out + + +check-adb : + rm -rf casket* + $(RUNENV) $(RUNCMD) ./tcatest write 'casket.tch#mode=wct#bnum=5000' 50000 + $(RUNENV) $(RUNCMD) ./tcatest read 'casket.tch#mode=r' + $(RUNENV) $(RUNCMD) ./tcatest remove 'casket.tch#mode=w' + $(RUNENV) $(RUNCMD) ./tcatest misc 'casket.tch#mode=wct#bnum=500#opts=ld' 5000 + $(RUNENV) $(RUNCMD) ./tcatest wicked 'casket.tch#mode=wct' 5000 + $(RUNENV) $(RUNCMD) ./tcatest write '@casket.tcb#mode=wct#lmemb=5#nmemb=5' 50000 + $(RUNENV) $(RUNCMD) ./tcatest read '@casket.tcb#mode=r' + $(RUNENV) $(RUNCMD) ./tcatest remove '@casket.tcb#mode=w' + $(RUNENV) $(RUNCMD) ./tcatest misc '@casket.tcb#mode=wct#lmemb=5#nmemb=5#opts=ld' 5000 + $(RUNENV) $(RUNCMD) ./tcatest wicked '@casket.tcb#mode=wct' 5000 + $(RUNENV) $(RUNCMD) ./tcatest write 'casket.tcf#mode=wct#width=10' 50000 + $(RUNENV) $(RUNCMD) ./tcatest read 'casket.tcf#mode=r' + $(RUNENV) $(RUNCMD) ./tcatest remove 'casket.tcf#mode=w' + $(RUNENV) $(RUNCMD) ./tcatest write '*#bnum=5000#cap=100' 50000 + $(RUNENV) $(RUNCMD) ./tcatest misc '*' 5000 + $(RUNENV) $(RUNCMD) ./tcatest wicked '*' 5000 + $(RUNENV) $(RUNCMD) ./tcatest write '%casket-mul.tch#mode=wct#bnum=500' 50000 + $(RUNENV) $(RUNCMD) ./tcatest read '%casket-mul.tch#mode=r' + $(RUNENV) $(RUNCMD) ./tcatest remove '%casket-mul.tch#mode=w' + $(RUNENV) $(RUNCMD) ./tcatest misc '%casket-mul.tch#mode=wct#bnum=500#opts=ld' 5000 + $(RUNENV) $(RUNCMD) ./tcatest wicked '%casket-mul.tch#mode=wct' 5000 + $(RUNENV) $(RUNCMD) ./tcatest compare casket 50 500 + $(RUNENV) $(RUNCMD) ./tcatest compare casket 5 5000 + $(RUNENV) $(RUNCMD) ./tcamttest write 'casket.tch#mode=wct#bnum=5000' 5 5000 + $(RUNENV) $(RUNCMD) ./tcamttest read 'casket.tch#mode=r' 5 + $(RUNENV) $(RUNCMD) ./tcamttest remove 'casket.tch#mode=w' 5 + $(RUNENV) $(RUNCMD) ./tcamttest write '%casket-mul.tcb#mode=wct#bnum=5000' 5 5000 + $(RUNENV) $(RUNCMD) ./tcamttest read '%casket-mul.tcb#mode=r' 5 + $(RUNENV) $(RUNCMD) ./tcamttest remove '%casket-mul.tcb#mode=w' 5 + $(RUNENV) $(RUNCMD) ./tcamgr create 'casket.tch#mode=wct#bnum=3' + $(RUNENV) $(RUNCMD) ./tcamgr inform 'casket.tch' + $(RUNENV) $(RUNCMD) ./tcamgr put casket.tch one first + $(RUNENV) $(RUNCMD) ./tcamgr put casket.tch two second + $(RUNENV) $(RUNCMD) ./tcamgr put -dk casket.tch three third + $(RUNENV) $(RUNCMD) ./tcamgr put -dc casket.tch three third + $(RUNENV) $(RUNCMD) ./tcamgr put -dc casket.tch three third + $(RUNENV) $(RUNCMD) ./tcamgr put -dc casket.tch three third + $(RUNENV) $(RUNCMD) ./tcamgr put casket.tch four fourth + $(RUNENV) $(RUNCMD) ./tcamgr put -dk casket.tch five fifth + $(RUNENV) $(RUNCMD) ./tcamgr out casket.tch one + $(RUNENV) $(RUNCMD) ./tcamgr out casket.tch two + $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch three > check.out + $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch four > check.out + $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch five > check.out + $(RUNENV) $(RUNCMD) ./tcamgr list -pv -fm f casket.tch > check.out + $(RUNENV) $(RUNCMD) ./tcamgr optimize casket.tch + $(RUNENV) $(RUNCMD) ./tcamgr put -dc casket.tch three third + $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch three > check.out + $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch four > check.out + $(RUNENV) $(RUNCMD) ./tcamgr get casket.tch five > check.out + $(RUNENV) $(RUNCMD) ./tcamgr misc casket.tch putlist six sixth seven seventh + $(RUNENV) $(RUNCMD) ./tcamgr misc casket.tch outlist six + $(RUNENV) $(RUNCMD) ./tcamgr misc casket.tch getlist three four five six > check.out + $(RUNENV) $(RUNCMD) ./tcamgr list -pv casket.tch > check.out + $(RUNENV) $(RUNCMD) ./tcamgr create 'casket.tct#mode=wct#idx=name:lex#idx=age:dec' + $(RUNENV) $(RUNCMD) ./tcamgr put -sep '|' casket.tct 1 "name|mikio|age|30" + $(RUNENV) $(RUNCMD) ./tcamgr put -sep '|' casket.tct 2 "name|fal|age|31" + $(RUNENV) $(RUNCMD) ./tcamgr put -sep '|' casket.tct 3 "name|lupin|age|29" + $(RUNENV) $(RUNCMD) ./tcamgr get -sep '\t' casket.tct 1 > check.out + $(RUNENV) $(RUNCMD) ./tcamgr list -sep '\t' -pv casket.tct > check.out + $(RUNENV) $(RUNCMD) ./tcamgr misc -sep '|' casket.tct search \ + "addcond|name|STRINC|i" "setorder|age|NUMASC" "setmax|1" "get" > check.out + $(RUNENV) $(RUNCMD) ./tcamgr misc -sep '|' casket.tct search "get" "out" > check.out + $(RUNENV) $(RUNCMD) ./tcamgr create '%casket-mul.tcb#mode=wct' + $(RUNENV) $(RUNCMD) ./tcamgr put '%casket-mul.tcb' one first + $(RUNENV) $(RUNCMD) ./tcamgr put '%casket-mul.tcb' two second + $(RUNENV) $(RUNCMD) ./tcamgr put -dk '%casket-mul.tcb' three third + $(RUNENV) $(RUNCMD) ./tcamgr put -dc '%casket-mul.tcb' three third + $(RUNENV) $(RUNCMD) ./tcamgr put -dc '%casket-mul.tcb' three third + $(RUNENV) $(RUNCMD) ./tcamgr put -dc '%casket-mul.tcb' three third + $(RUNENV) $(RUNCMD) ./tcamgr put '%casket-mul.tcb' four fourth + $(RUNENV) $(RUNCMD) ./tcamgr put -dk '%casket-mul.tcb' five fifth + $(RUNENV) $(RUNCMD) ./tcamgr out '%casket-mul.tcb' one + $(RUNENV) $(RUNCMD) ./tcamgr out '%casket-mul.tcb' two + $(RUNENV) $(RUNCMD) ./tcamgr get '%casket-mul.tcb' three > check.out + $(RUNENV) $(RUNCMD) ./tcamgr get '%casket-mul.tcb' four > check.out + $(RUNENV) $(RUNCMD) ./tcamgr get '%casket-mul.tcb' five > check.out + $(RUNENV) $(RUNCMD) ./tcamgr list -pv -fm f '%casket-mul.tcb' > check.out + $(RUNENV) $(RUNCMD) ./tcamgr optimize '%casket-mul.tcb' + $(RUNENV) $(RUNCMD) ./tcamgr put -dc '%casket-mul.tcb' three third + $(RUNENV) $(RUNCMD) ./tcamgr get '%casket-mul.tcb' three > check.out + $(RUNENV) $(RUNCMD) ./tcamgr get '%casket-mul.tcb' four > check.out + $(RUNENV) $(RUNCMD) ./tcamgr get '%casket-mul.tcb' five > check.out + $(RUNENV) $(RUNCMD) ./tcamgr misc '%casket-mul.tcb' '%putlist' six sixth seven seventh + $(RUNENV) $(RUNCMD) ./tcamgr misc '%casket-mul.tcb' '@outlist' six + $(RUNENV) $(RUNCMD) ./tcamgr misc '%casket-mul.tcb' '@getlist' \ + three four five six > check.out + $(RUNENV) $(RUNCMD) ./tcamgr list -pv '%casket-mul.tcb' > check.out + +check-ejdb : + make -C ./testejdb check + +check-valgrind : + make RUNCMD="valgrind --tool=memcheck --log-file=%p.vlog" check + grep ERROR *.vlog | grep -v ' 0 errors' ; true + grep 'at exit' *.vlog | grep -v ' 0 bytes' ; true + +check-valgrind-ejdb : + make RUNCMD="valgrind --tool=memcheck --leak-check=full --log-file=%p.vlog" -C ./testejdb check-valgrind + grep ERROR *.vlog | grep -v ' 0 errors' ; true + grep 'at exit' *.vlog | grep -v ' 0 bytes' ; true + +check-large : + rm -rf casket* + $(RUNENV) $(RUNCMD) ./tchmttest typical casket 3 1000000 5000000 13 8 + $(RUNENV) $(RUNCMD) ./tchmttest typical -nc casket 3 1000000 5000000 13 8 + $(RUNENV) $(RUNCMD) ./tcbmttest typical casket 3 500000 8 8 500000 16 8 + $(RUNENV) $(RUNCMD) ./tcbmttest typical -nc casket 3 500000 8 8 500000 16 8 + $(RUNENV) $(RUNCMD) ./tcfmttest typical casket 3 500000 2048 4g + $(RUNENV) $(RUNCMD) ./tcfmttest typical -nc casket 3 500000 2048 4g + rm -rf casket* + + +check-compare : + $(RUNENV) $(RUNCMD) ./tcatest compare casket 5 10000 + $(RUNENV) $(RUNCMD) ./tcatest compare casket 10 5000 + $(RUNENV) $(RUNCMD) ./tcatest compare casket 50 1000 + $(RUNENV) $(RUNCMD) ./tcatest compare casket 100 500 + + +check-thread : + rm -rf casket* + $(RUNENV) $(RUNCMD) ./tcumttest typical 5 500000 500000 + $(RUNENV) $(RUNCMD) ./tcumttest typical -nc -rr 1000 5 500000 500000 + $(RUNENV) $(RUNCMD) ./tchmttest typical casket 5 500000 500000 + $(RUNENV) $(RUNCMD) ./tchmttest typical -rc 500000 -nc -rr 1000 casket 5 500000 500000 + $(RUNENV) $(RUNCMD) ./tcbmttest typical casket 5 100000 5 5 + $(RUNENV) $(RUNCMD) ./tcbmttest typical -nc -rr 1000 casket 5 100000 5 5 + $(RUNENV) $(RUNCMD) ./tcfmttest typical casket 5 500000 10 + $(RUNENV) $(RUNCMD) ./tcfmttest typical -nc -rr 1000 casket 5 500000 10 + rm -rf casket* + + +check-race : + $(RUNENV) $(RUNCMD) ./tchmttest race casket 5 10000 + $(RUNENV) $(RUNCMD) ./tcbmttest race casket 5 10000 + + +check-forever : + while true ; \ + do \ + make check || break ; \ + make check || break ; \ + make check-thread || break ; \ + make check-race || break ; \ + make check-race || break ; \ + make check-compare || break ; \ + make check-compare || break ; \ + done + + +words : + rm -f casket-* words.tsv + cat /usr/share/dict/words | \ + tr '\t\r' ' ' | grep -v '^ *$$' | cat -n | sort | \ + LC_ALL=C sed -e 's/^ *//' -e 's/\(^[0-9]*\)\t\(.*\)/\2\t\1/' > words.tsv + ./tchmgr create casket-hash -1 0 ; ./tchmgr importtsv casket-hash words.tsv + ./tcbmgr create casket-btree 8192 ; ./tcbmgr importtsv casket-btree words.tsv + ./tcbmgr create -td casket-btree-td 8192 ; ./tcbmgr importtsv casket-btree-td words.tsv + ./tcbmgr create -tb casket-btree-tb 8192 ; ./tcbmgr importtsv casket-btree-tb words.tsv + ./tcbmgr create -tt casket-btree-tt 8192 ; ./tcbmgr importtsv casket-btree-tt words.tsv + ./tcbmgr create -tx casket-btree-tx 8192 ; ./tcbmgr importtsv casket-btree-tx words.tsv + wc -c words.tsv casket-hash casket-btree \ + casket-btree-td casket-btree-tb casket-btree-tt casket-btree-tx + + +wordtable : + rm -rf casket* words.tsv + cat /usr/share/dict/words | \ + tr '\t\r' ' ' | grep -v '^ *$$' | cat -n | sort | \ + LC_ALL=C sed -e 's/^ *//' -e 's/\(^[0-9]*\)\t\(.*\)/\1\tword\t\2\tnum\t\1/' \ + -e 's/$$/\txxx\tabc\tyyy\t123/' > words.tsv + ./tctmgr create casket + ./tctmgr setindex casket word + ./tctmgr setindex -it dec casket num + ./tctmgr importtsv casket words.tsv + + +.PHONY : all clean install check + + + +#================================================================ +# Building binaries +#================================================================ + + +libtcejdb.a : $(LIBOBJFILES) + $(AR) $(ARFLAGS) $@ $(LIBOBJFILES) + + +libtcejdb.so.$(LIBVER).$(LIBREV).0 : $(LIBOBJFILES) + if uname -a | egrep -i 'SunOS' > /dev/null ; \ + then \ + $(CC) $(CFLAGS) -shared -Wl,-G,-h,libtcejdb.so.$(LIBVER) -o $@ \ + $(LIBOBJFILES) $(LDFLAGS) $(LIBS) ; \ + else \ + $(CC) $(CFLAGS) -shared -Wl,-soname,libtcejdb.so.$(LIBVER) -o $@ \ + $(LIBOBJFILES) $(LDFLAGS) $(LIBS) ; \ + fi + + +libtcejdb.so.$(LIBVER) : libtcejdb.so.$(LIBVER).$(LIBREV).0 + ln -f -s libtcejdb.so.$(LIBVER).$(LIBREV).0 $@ + + +libtcejdb.so : libtcejdb.so.$(LIBVER).$(LIBREV).0 + ln -f -s libtcejdb.so.$(LIBVER).$(LIBREV).0 $@ + + +libtcejdb.$(LIBVER).$(LIBREV).0.dylib : $(LIBOBJFILES) + $(CC) $(CFLAGS) -dynamiclib -o $@ \ + -install_name $(LIBDIR)/libtcejdb.$(LIBVER).dylib \ + -current_version $(LIBVER).$(LIBREV).0 -compatibility_version $(LIBVER) \ + $(LIBOBJFILES) $(LDFLAGS) $(LIBS) + + +libtcejdb.$(LIBVER).dylib : libtcejdb.$(LIBVER).$(LIBREV).0.dylib + ln -f -s libtcejdb.$(LIBVER).$(LIBREV).0.dylib $@ + + +libtcejdb.dylib : libtcejdb.$(LIBVER).$(LIBREV).0.dylib + ln -f -s libtcejdb.$(LIBVER).$(LIBREV).0.dylib $@ + + +tcutest : tcutest.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tcumttest : tcumttest.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tcucodec : tcucodec.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tchtest : tchtest.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tchmttest : tchmttest.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tchmgr : tchmgr.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tcbtest : tcbtest.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tcbmttest : tcbmttest.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tcbmgr : tcbmgr.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tcftest : tcftest.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tcfmttest : tcfmttest.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tcfmgr : tcfmgr.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tcttest : tcttest.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tctmttest : tctmttest.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tctmgr : tctmgr.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tcatest : tcatest.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tcamttest : tcamttest.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tcamgr : tcamgr.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +tcawmgr.cgi : tcawmgr.o $(LIBRARYFILES) + $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltcejdb $(LIBS) + + +myconf.o : myconf.h + +tcutil.o : myconf.h tcutil.h md5.h + +tchdb.o : myconf.h tcutil.h tchdb.h + +tcbdb.o : myconf.h tcutil.h tchdb.h tcbdb.h + +tcfdb.o : myconf.h tcutil.h tcfdb.h + +tctdb.o : myconf.h tcutil.h tchdb.h tctdb.h + +tcadb.o : myconf.h tcutil.h tchdb.h tcbdb.h tcfdb.h tctdb.h tcadb.h + +tcutest.o tcucodec.o : myconf.h tcutil.h + +tchtest.o tchmttest.o tchmgr.o : myconf.h tcutil.h tchdb.h + +tcbtest.o tcbmttest.o tcbmgr.o : myconf.h tcutil.h tchdb.h tcbdb.h + +tcftest.o tcfmttest.o tcfmgr.o : myconf.h tcutil.h tcfdb.h + +tcttest.o tctmttest.o tctmgr.o : myconf.h tcutil.h tchdb.h tcbdb.h tctdb.h + +tcatest.o tcamttest.o tcamgr.o tcawmgr.o : \ + myconf.h tcutil.h tchdb.h tcbdb.h tcfdb.h tctdb.h tcadb.h + +ejdb.o : myconf.h ejdb.h ejdb_private.h ejdbutl.h + +bson.o : myconf.h bson.h + + +tokyocabinet_all.c : myconf.c tcutil.c md5.c tchdb.c tcbdb.c tcfdb.c tctdb.c tcadb.c + cat myconf.c tcutil.c md5.c tchdb.c tcbdb.c tcfdb.c tctdb.c tcadb.c > $@ + +tokyocabinet_all.o : myconf.h tcutil.h tchdb.h tcbdb.h tcfdb.h tctdb.h tcadb.h + + + +# END OF FILE diff --git a/tcejdb/README b/tcejdb/README new file mode 100644 index 0000000..f810f4c --- /dev/null +++ b/tcejdb/README @@ -0,0 +1,37 @@ +================================================================ + Tokyo Cabinet: a modern implementation of DBM + Copyright (C) 2006-2011 Fal Labs +================================================================ + + +Please read the following documents with a WWW browser. +How to install Tokyo Cabinet is explained in the specification. + + README - this file + COPYING - license + ChangeLog - history of enhancement + doc/index.html - index of documents + + +Contents of the directory tree is below. + + ./ - sources of Tokyo Cabinet + ./doc/ - manuals and specifications + ./man/ - manuals for nroff + ./example/ - sample code of tutorial + ./lab/ - for test and experiment + ./bros/ - for comparison with other database managers + + +Tokyo Cabinet is released under the terms of the GNU Lesser General +Public License. See the file `COPYING' for details. + +Tokyo Cabinet was written by FAL Labs. You can contact the author +by e-mail to `hirarin@gmail.com'. + + +Thanks. + + + +== END OF FILE == diff --git a/tcejdb/bros/Makefile b/tcejdb/bros/Makefile new file mode 100644 index 0000000..619462e --- /dev/null +++ b/tcejdb/bros/Makefile @@ -0,0 +1,133 @@ +# Makefile for writing tests of DBM brothers + + + +#================================================================ +# Setting variables +#================================================================ + + +# Generic settings +SHELL = /bin/sh + +# Targets +MYBINS = tctest qdbmtest ndbmtest sdbmtest gdbmtest tdbtest cdbtest bdbtest \ + maptest sqltest cmpsqltctest + + + +#================================================================ +# Suffix rules +#================================================================ + + +.SUFFIXES : + + + +#================================================================ +# Actions +#================================================================ + + +mesg : + @echo "Here are writing tests of DBM brothers." 1>&2 + @echo "select a target of {all clean $(MYBINS)}." 1>&2 + + +all : $(MYBINS) + + +clean : + rm -rf $(MYBINS) *.o *.exe a.out casket* *~ + + +distclean : clean + + + +#================================================================ +# Building binaries +#================================================================ + + +tctest : tctest.c + LD_LIBRARY_PATH="$(LD_LIBRARY_PATH):.:.." ; PATH="$(PATH):.:.." ; \ + export LD_LIBRARY_PATH PATH ; ldflags=`tcucodec conf -l` ; \ + [ -z "$$ldflags" ] && \ + ldflags="-L/usr/local/lib -ltokyocabinet -lz -lpthread -lm -lc" ; \ + gcc -I.. -I/usr/local/include -D_GNU_SOURCE=1 \ + -Wall -ansi -pedantic -O3 -o $@ tctest.c \ + -static -L. -L.. $$ldflags + + +qdbmtest : qdbmtest.c + gcc -I.. -I/usr/local/include -D_GNU_SOURCE=1 \ + -Wall -ansi -pedantic -O3 -o $@ qdbmtest.c \ + -static -L.. -L/usr/local/lib -lqdbm -lz -lc + + +ndbmtest : ndbmtest.c + gcc -I/usr/local/include -D_GNU_SOURCE=1 \ + -Wall -ansi -pedantic -O3 -o $@ ndbmtest.c \ + -static -L/usr/local/lib -lndbm -lc + + +sdbmtest : sdbmtest.c + gcc -I/usr/local/include -D_GNU_SOURCE=1 \ + -Wall -ansi -pedantic -O3 -o $@ sdbmtest.c \ + -static -L/usr/local/lib -lsdbm -lc + + +gdbmtest : gdbmtest.c + gcc -I/usr/local/include -D_GNU_SOURCE=1 \ + -Wall -ansi -pedantic -O3 -o $@ gdbmtest.c \ + -static -L/usr/local/lib -lgdbm -lc + + +tdbtest : tdbtest.c + gcc -I/usr/local/include -D_GNU_SOURCE=1 \ + -Wall -ansi -pedantic -O3 -o $@ tdbtest.c \ + -static -L/usr/local/lib -ltdb -lc + + +cdbtest : cdbtest.c + gcc -I/usr/local/include -D_GNU_SOURCE=1 \ + -Wall -ansi -pedantic -O3 -o $@ cdbtest.c \ + -static -L/usr/local/lib -lcdb -lc + + +bdbtest : bdbtest.c + gcc -I/usr/local/bdb/include -D_GNU_SOURCE=1 \ + -Wall -O3 -o $@ bdbtest.c \ + -static -L/usr/local/bdb/lib -ldb -lpthread -lc + + +maptest : maptest.cc + LD_LIBRARY_PATH="$(LD_LIBRARY_PATH):.:.." ; PATH="$(PATH):.:.." ; \ + export LD_LIBRARY_PATH PATH ; ldflags=`tcucodec conf -l` ; \ + [ -z "$$ldflags" ] && \ + ldflags="-L/usr/local/lib -ltokyocabinet -lz -lpthread -lm -lc" ; \ + g++ -I.. -I/usr/local/include -D_GNU_SOURCE=1 \ + -Wall -ansi -pedantic -O3 -o $@ maptest.cc \ + -static -L. -L.. -lstdc++ $$ldflags + + +sqltest : sqltest.c + gcc -I/usr/local/include -D_GNU_SOURCE=1 \ + -Wall -O3 -o $@ sqltest.c \ + -static -L/usr/local/lib -lsqlite3 -lpthread -ldl -lc + + +cmpsqltctest : cmpsqltctest.c + LD_LIBRARY_PATH="$(LD_LIBRARY_PATH):.:.." ; PATH="$(PATH):.:.." ; \ + export LD_LIBRARY_PATH PATH ; ldflags=`tcucodec conf -l` ; \ + [ -z "$$ldflags" ] && \ + ldflags="-L/usr/local/lib -ltokyocabinet -lz -lpthread -lm -lc" ; \ + gcc -std=c99 -I.. -I/usr/local/include -D_GNU_SOURCE=1 \ + -Wall -pedantic -O3 -o $@ cmpsqltctest.c \ + -static -L. -L.. -lsqlite3 -lpthread -ldl $$ldflags + + + +# END OF FILE diff --git a/tcejdb/bros/bdbtest.c b/tcejdb/bros/bdbtest.c new file mode 100644 index 0000000..55afb53 --- /dev/null +++ b/tcejdb/bros/bdbtest.c @@ -0,0 +1,438 @@ +/************************************************************************************************* + * Writing test of Berkeley DB + *************************************************************************************************/ + + +#include +#include +#include +#include + +#undef TRUE +#define TRUE 1 /* boolean true */ +#undef FALSE +#define FALSE 0 /* boolean false */ + +#define RECBUFSIZ 32 /* buffer for records */ +#define SMALL_PAGESIZE 512 /* small page size */ +#define BIG_PAGESIZE 65536 /* maximum page size */ +#define BIG_CACHESIZE (rnum * 2 * 15) /* maximum cache size < avail mem. */ +#define SMALL_CACHESIZE (5 * 1048576) /* should be Btree fanout */ + + +/* global variables */ +const char *progname; /* program name */ +int showprgr; /* whether to show progression */ + + +/* function prototypes */ +int main(int argc, char **argv); +void usage(void); +int runwrite(int argc, char **argv); +int runread(int argc, char **argv); +int runbtwrite(int argc, char **argv); +int runbtread(int argc, char **argv); +int myrand(void); +int dowrite(char *name, int rnum); +int doread(char *name, int rnum); +int dobtwrite(char *name, int rnum, int rnd); +int dobtread(char *name, int rnum, int rnd); + + +/* main routine */ +int main(int argc, char **argv){ + int rv; + progname = argv[0]; + showprgr = TRUE; + if(getenv("HIDEPRGR")) showprgr = FALSE; + srand48(1978); + if(argc < 2) usage(); + rv = 0; + if(!strcmp(argv[1], "write")){ + rv = runwrite(argc, argv); + } else if(!strcmp(argv[1], "read")){ + rv = runread(argc, argv); + } else if(!strcmp(argv[1], "btwrite")){ + rv = runbtwrite(argc, argv); + } else if(!strcmp(argv[1], "btread")){ + rv = runbtread(argc, argv); + } else { + usage(); + } + return rv; +} + + +/* print the usage and exit */ +void usage(void){ + fprintf(stderr, "%s: test cases for Berkeley DB\n", progname); + fprintf(stderr, "\n"); + fprintf(stderr, "usage:\n"); + fprintf(stderr, " %s write name rnum\n", progname); + fprintf(stderr, " %s read name rnum\n", progname); + fprintf(stderr, " %s btwrite [-rnd] name rnum\n", progname); + fprintf(stderr, " %s btread [-rnd] name rnum\n", progname); + fprintf(stderr, "\n"); + exit(1); +} + + +/* parse arguments of write command */ +int runwrite(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dowrite(name, rnum); + return rv; +} + + +/* parse arguments of read command */ +int runread(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = doread(name, rnum); + return rv; +} + + +/* parse arguments of btwrite command */ +int runbtwrite(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rnd, rv; + name = NULL; + rstr = NULL; + rnum = 0; + rnd = FALSE; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + if(!name && !strcmp(argv[i], "-rnd")){ + rnd = TRUE; + } else { + usage(); + } + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dobtwrite(name, rnum, rnd); + return rv; +} + + +/* parse arguments of btread command */ +int runbtread(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rnd, rv; + name = NULL; + rstr = NULL; + rnum = 0; + rnd = FALSE; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + if(!name && !strcmp(argv[i], "-rnd")){ + rnd = TRUE; + } else { + usage(); + } + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dobtread(name, rnum, rnd); + return rv; +} + + +/* pseudo random number generator */ +int myrand(void){ + static int cnt = 0; + return (lrand48() + cnt++) & 0x7FFFFFFF; +} + + +/* perform write command */ +int dowrite(char *name, int rnum){ + DB *dbp; + DBT key, data; + int i, err, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(db_create(&dbp, NULL, 0) != 0){ + fprintf(stderr, "db_create failed\n"); + return 1; + } + if(dbp->set_pagesize(dbp, SMALL_PAGESIZE) != 0){ + fprintf(stderr, "DB->set_pagesize failed\n"); + dbp->close(dbp, 0); + return 1; + } + if(dbp->set_cachesize(dbp, 0, BIG_CACHESIZE, 0) != 0){ + fprintf(stderr, "DB->set_cachesize failed\n"); + dbp->close(dbp, 0); + return 1; + } + if(dbp->open(dbp, NULL, name, NULL, DB_HASH, DB_CREATE | DB_TRUNCATE, 00644) != 0){ + fprintf(stderr, "DB->open failed\n"); + dbp->close(dbp, 0); + return 1; + } + err = FALSE; + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + len = sprintf(buf, "%08d", i); + key.data = buf; + key.size = len; + data.data = buf; + data.size = len; + if(dbp->put(dbp, NULL, &key, &data, 0) != 0){ + fprintf(stderr, "DB->put failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(dbp->close(dbp, 0) != 0){ + fprintf(stderr, "DB->close failed\n"); + err = TRUE; + } + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform read command */ +int doread(char *name, int rnum){ + DB *dbp; + DBT key, data; + int i, err, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(db_create(&dbp, NULL, 0) != 0){ + fprintf(stderr, "db_create failed\n"); + return 1; + } + if(dbp->set_cachesize(dbp, 0, BIG_CACHESIZE, 0) != 0){ + fprintf(stderr, "DB->set_cachesize failed\n"); + dbp->close(dbp, 0); + return 1; + } + if(dbp->open(dbp, NULL, name, NULL, DB_HASH, DB_RDONLY, 00644) != 0){ + fprintf(stderr, "DB->open failed\n"); + dbp->close(dbp, 0); + return 1; + } + err = FALSE; + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + len = sprintf(buf, "%08d", i); + key.data = buf; + key.size = len; + if(dbp->get(dbp, NULL, &key, &data, 0) != 0){ + fprintf(stderr, "DB->get failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(dbp->close(dbp, 0) != 0){ + fprintf(stderr, "DB->close failed\n"); + err = TRUE; + } + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform btwrite command */ +int dobtwrite(char *name, int rnum, int rnd){ + DB *dbp; + DBT key, data; + int i, err, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(db_create(&dbp, NULL, 0) != 0){ + fprintf(stderr, "db_create failed\n"); + return 1; + } + if(dbp->set_pagesize(dbp, BIG_PAGESIZE) != 0){ + fprintf(stderr, "DB->set_pagesize failed\n"); + dbp->close(dbp, 0); + return 1; + } + if(dbp->set_cachesize(dbp, 0, rnd ? BIG_CACHESIZE : SMALL_CACHESIZE, 0) != 0){ + fprintf(stderr, "DB->set_cachesize failed\n"); + dbp->close(dbp, 0); + return 1; + } + if(dbp->open(dbp, NULL, name, NULL, DB_BTREE, DB_CREATE | DB_TRUNCATE, 00644) != 0){ + fprintf(stderr, "DB->open failed\n"); + dbp->close(dbp, 0); + return 1; + } + err = FALSE; + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + len = sprintf(buf, "%08d", rnd ? myrand() % rnum + 1 : i); + key.data = data.data = buf; + key.size = data.size = len; + if(dbp->put(dbp, NULL, &key, &data, 0) != 0){ + fprintf(stderr, "DB->put failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(dbp->close(dbp, 0) != 0){ + fprintf(stderr, "DB->close failed\n"); + err = TRUE; + } + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform btread command */ +int dobtread(char *name, int rnum, int rnd){ + DB *dbp; + DBT key, data; + int i, err, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(db_create(&dbp, NULL, 0) != 0){ + fprintf(stderr, "db_create failed\n"); + return 1; + } + if(dbp->set_cachesize(dbp, 0, rnd ? BIG_CACHESIZE : SMALL_CACHESIZE, 0) != 0){ + fprintf(stderr, "DB->set_cachesize failed\n"); + dbp->close(dbp, 0); + return 1; + } + if(dbp->open(dbp, NULL, name, NULL, DB_BTREE, DB_RDONLY, 00644) != 0){ + fprintf(stderr, "DB->open failed\n"); + dbp->close(dbp, 0); + return 1; + } + err = FALSE; + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + len = sprintf(buf, "%08d", rnd ? myrand() % rnum + 1 : i); + key.data = buf; + key.size = len; + if(dbp->get(dbp, NULL, &key, &data, 0) != 0){ + fprintf(stderr, "DB->get failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(dbp->close(dbp, 0) != 0){ + fprintf(stderr, "DB->close failed\n"); + err = TRUE; + } + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + + +/* END OF FILE */ diff --git a/tcejdb/bros/cdbtest.c b/tcejdb/bros/cdbtest.c new file mode 100644 index 0000000..b58a0d9 --- /dev/null +++ b/tcejdb/bros/cdbtest.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef TRUE +#define TRUE 1 /* boolean true */ +#undef FALSE +#define FALSE 0 /* boolean false */ + +#define RECBUFSIZ 32 /* buffer for records */ + + +/* global variables */ +const char *progname; /* program name */ +int showprgr; /* whether to show progression */ + + +/* function prototypes */ +int main(int argc, char **argv); +void usage(void); +int runwrite(int argc, char **argv); +int runread(int argc, char **argv); +int dowrite(char *name, int rnum); +int doread(char *name, int rnum); + + +/* main routine */ +int main(int argc, char **argv){ + int rv; + progname = argv[0]; + showprgr = TRUE; + if(getenv("HIDEPRGR")) showprgr = FALSE; + if(argc < 2) usage(); + rv = 0; + if(!strcmp(argv[1], "write")){ + rv = runwrite(argc, argv); + } else if(!strcmp(argv[1], "read")){ + rv = runread(argc, argv); + } else { + usage(); + } + return rv; +} + + +/* print the usage and exit */ +void usage(void){ + fprintf(stderr, "%s: test cases for Constant Database\n", progname); + fprintf(stderr, "\n"); + fprintf(stderr, "usage:\n"); + fprintf(stderr, " %s write name rnum\n", progname); + fprintf(stderr, " %s read name rnum\n", progname); + fprintf(stderr, "\n"); + exit(1); +} + + +/* parse arguments of write command */ +int runwrite(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dowrite(name, rnum); + return rv; +} + + +/* parse arguments of read command */ +int runread(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = doread(name, rnum); + return rv; +} + + +/* perform write command */ +int dowrite(char *name, int rnum){ + struct cdb_make cdb; + int i, fd, err, len; + char buf[32]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if((fd = open(name, O_RDWR | O_CREAT | O_TRUNC, 0644)) == -1){ + perror("open"); + return 1; + } + if(cdb_make_start(&cdb, fd) == -1){ + perror("cdb_make_start"); + close(fd); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + len = sprintf(buf, "%08d", i); + if(cdb_make_add(&cdb, buf, len, buf, len) == -1){ + perror("cdb_add"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(cdb_make_finish(&cdb) == -1){ + perror("cdb_make_finish"); + close(fd); + return 1; + } + if(close(fd) == -1){ + perror("close"); + return 1; + } + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform read command */ +int doread(char *name, int rnum){ + struct cdb cdb; + int i, fd, err, len; + char buf[RECBUFSIZ], *val; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if((fd = open(name, O_RDONLY, 0644)) == -1){ + perror("open"); + return 1; + } + cdb_init(&cdb, fd); + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* retrieve a record */ + len = sprintf(buf, "%08d", i); + if(cdb_find(&cdb, buf, len) == 0){ + perror("cdb_find"); + err = TRUE; + break; + } + len = cdb_datalen(&cdb); + if(!(val = malloc(len + 1))){ + perror("malloc"); + err = TRUE; + break; + } + cdb_read(&cdb, val, len, cdb_datapos(&cdb)); + free(val); + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + cdb_free(&cdb); + if(close(fd) == -1){ + perror("close"); + return 1; + } + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + + +/* END OF FILE */ diff --git a/tcejdb/bros/cmpsqltctest.c b/tcejdb/bros/cmpsqltctest.c new file mode 100644 index 0000000..168f932 --- /dev/null +++ b/tcejdb/bros/cmpsqltctest.c @@ -0,0 +1,186 @@ +/************************************************************************************************* + * Comparison test of SQLite and Tokyo Cabinet + *************************************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SQLFILE "casket.sql" +#define TCTFILE "casket.tct" +#define SQLBUFSIZ 1024 +#define SAMPLENUM 10 +#define RECORDNUM 1000000 + + +/* function prototypes */ +int main(int argc, char **argv); +static void test_sqlite(void); +static void test_tctdb(void); +static int myrand(void); +static int callback(void *opq, int argc, char **argv, char **colname); + + +/* main routine */ +int main(int argc, char **argv){ + test_sqlite(); + test_tctdb(); + return 0; +} + + +/* perform SQLite test */ +static void test_sqlite(void){ + double sum = 0.0; + for(int i = 1; i <= SAMPLENUM; i++){ + unlink(SQLFILE); + sync(); sync(); + printf("SQLite write sample:%d ... ", i); + fflush(stdout); + double stime = tctime(); + sqlite3 *db; + if(sqlite3_open(SQLFILE, &db) != 0) assert(!__LINE__); + char sql[SQLBUFSIZ], *errmsg; + sprintf(sql, "CREATE TABLE tbl ( k TEXT PRIMARY KEY, a TEXT, b INTEGER," + " c TEXT, d INTEGER );"); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK) assert(!__LINE__); + sprintf(sql, "CREATE INDEX tbl_s ON tbl ( a );"); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK) assert(!__LINE__); + sprintf(sql, "CREATE INDEX tbl_n ON tbl ( b );"); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK) assert(!__LINE__); + sprintf(sql, "PRAGMA cache_size = %d;", RECORDNUM); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK) assert(!__LINE__); + sprintf(sql, "BEGIN TRANSACTION;"); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK) assert(!__LINE__); + for(int j = 1; j <= RECORDNUM; j++){ + sprintf(sql, "INSERT INTO tbl VALUES ( '%08d', '%08d', %d, '%08d', %d );", + j, j, myrand() % RECORDNUM, myrand() % RECORDNUM, j); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK) assert(!__LINE__); + } + sprintf(sql, "END TRANSACTION;"); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK) assert(!__LINE__); + sqlite3_close(db); + double etime = tctime() - stime; + printf("%.6f\n", etime); + sum += etime; + } + printf("SQLite write average: %.6f\n", sum / SAMPLENUM); + sum = 0.0; + for(int i = 1; i <= SAMPLENUM; i++){ + sync(); sync(); + printf("SQLite read sample:%d ... ", i); + fflush(stdout); + double stime = tctime(); + sqlite3 *db; + if(sqlite3_open(SQLFILE, &db) != 0) assert(!__LINE__); + char sql[SQLBUFSIZ], *errmsg; + sprintf(sql, "PRAGMA cache_size = %d;", RECORDNUM); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK) assert(!__LINE__); + for(int j = 1; j <= RECORDNUM; j++){ + sprintf(sql, "SELECT * FROM tbl WHERE a = '%08d';", myrand() % RECORDNUM + 1); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK) assert(!__LINE__); + } + sqlite3_close(db); + double etime = tctime() - stime; + printf("%.6f\n", etime); + sum += etime; + } + printf("SQLite read average: %.6f\n", sum / SAMPLENUM); +} + + +/* perform TCHDB test */ +static void test_tctdb(void){ + double sum = 0.0; + for(int i = 1; i <= SAMPLENUM; i++){ + unlink(TCTFILE); + sync(); sync(); + printf("TCTDB write sample:%d ... ", i); + fflush(stdout); + double stime = tctime(); + TCTDB *tdb = tctdbnew(); + if(!tctdbtune(tdb, RECORDNUM * 2, -1, -1, 0)) assert(!__LINE__); + if(!tctdbsetcache(tdb, 0, RECORDNUM, RECORDNUM)) assert(!__LINE__); + if(!tctdbopen(tdb, TCTFILE, TDBOWRITER | TDBOCREAT | TDBOTRUNC)) assert(!__LINE__); + if(!tctdbsetindex(tdb, "a", TDBITLEXICAL)) assert(!__LINE__); + if(!tctdbsetindex(tdb, "b", TDBITDECIMAL)) assert(!__LINE__); + if(!tctdbtranbegin(tdb)) assert(!__LINE__); + char buf[SQLBUFSIZ]; + int size; + for(int j = 1; j <= RECORDNUM; j++){ + TCMAP *cols = tcmapnew2(7); + size = sprintf(buf, "%08d", j); + tcmapput(cols, "a", 1, buf, size); + size = sprintf(buf, "%d", myrand() % RECORDNUM); + tcmapput(cols, "b", 1, buf, size); + size = sprintf(buf, "%08d", myrand() % RECORDNUM); + tcmapput(cols, "c", 1, buf, size); + size = sprintf(buf, "%d", j); + tcmapput(cols, "d", 1, buf, size); + size = sprintf(buf, "%08d", j); + if(!tctdbput(tdb, buf, size, cols)) assert(!__LINE__); + tcmapdel(cols); + } + if(!tctdbtrancommit(tdb)) assert(!__LINE__); + if(!tctdbclose(tdb)) assert(!__LINE__); + tctdbdel(tdb); + double etime = tctime() - stime; + printf("%.6f\n", etime); + sum += etime; + } + printf("TCTDB write average: %.6f\n", sum / SAMPLENUM); + sum = 0.0; + for(int i = 1; i <= SAMPLENUM; i++){ + sync(); sync(); + printf("TCTDB read sample:%d ... ", i); + fflush(stdout); + double stime = tctime(); + TCTDB *tdb = tctdbnew(); + if(!tctdbsetcache(tdb, 0, RECORDNUM, RECORDNUM)) assert(!__LINE__); + if(!tctdbopen(tdb, TCTFILE, TDBOREADER)) assert(!__LINE__); + char buf[SQLBUFSIZ]; + for(int j = 1; j <= RECORDNUM; j++){ + TDBQRY *qry = tctdbqrynew(tdb); + sprintf(buf, "%08d", myrand() % RECORDNUM + 1); + tctdbqryaddcond(qry, "a", TDBQCSTREQ, buf); + TCLIST *res = tctdbqrysearch(qry); + for(int k = 0; k < tclistnum(res); k++){ + int ksiz; + const char *kbuf = tclistval(res, k, &ksiz); + TCMAP *cols = tctdbget(tdb, kbuf, ksiz); + if(!cols) assert(!__LINE__); + tcmapdel(cols); + } + tclistdel(res); + tctdbqrydel(qry); + } + if(!tctdbclose(tdb)) assert(!__LINE__); + double etime = tctime() - stime; + printf("%.6f\n", etime); + sum += etime; + } + printf("TCTDB read average: %.6f\n", sum / SAMPLENUM); +} + + +/* generate a random number */ +static int myrand(void){ + static int cnt = 0; + return (lrand48() + cnt++) & 0x7FFFFFFF; +} + + +/* iterator of SQLite */ +static int callback(void *opq, int argc, char **argv, char **colname){ + return 0; +} + + + +/* END OF FILE */ diff --git a/tcejdb/bros/gdbmtest.c b/tcejdb/bros/gdbmtest.c new file mode 100644 index 0000000..eff692d --- /dev/null +++ b/tcejdb/bros/gdbmtest.c @@ -0,0 +1,216 @@ +/************************************************************************************************* + * Writing test of GNU Database Manager + *************************************************************************************************/ + + +#include +#include +#include +#include + +#undef TRUE +#define TRUE 1 /* boolean true */ +#undef FALSE +#define FALSE 0 /* boolean false */ + +#define RECBUFSIZ 32 /* buffer for records */ +#define CACHESIZE 16 /* cache size */ + + +/* global variables */ +const char *progname; /* program name */ +int showprgr; /* whether to show progression */ + + +/* function prototypes */ +int main(int argc, char **argv); +void usage(void); +int runwrite(int argc, char **argv); +int runread(int argc, char **argv); +int dowrite(char *name, int rnum); +int doread(char *name, int rnum); + + +/* main routine */ +int main(int argc, char **argv){ + int rv; + progname = argv[0]; + showprgr = TRUE; + if(getenv("HIDEPRGR")) showprgr = FALSE; + if(argc < 2) usage(); + rv = 0; + if(!strcmp(argv[1], "write")){ + rv = runwrite(argc, argv); + } else if(!strcmp(argv[1], "read")){ + rv = runread(argc, argv); + } else { + usage(); + } + return rv; +} + + +/* print the usage and exit */ +void usage(void){ + fprintf(stderr, "%s: test cases for GNU Database Manager\n", progname); + fprintf(stderr, "\n"); + fprintf(stderr, "usage:\n"); + fprintf(stderr, " %s write name rnum\n", progname); + fprintf(stderr, " %s read name rnum\n", progname); + fprintf(stderr, "\n"); + exit(1); +} + + +/* parse arguments of write command */ +int runwrite(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dowrite(name, rnum); + return rv; +} + + +/* parse arguments of read command */ +int runread(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = doread(name, rnum); + return rv; +} + + +/* perform write command */ +int dowrite(char *name, int rnum){ + GDBM_FILE dbf; + datum key, content; + int i, err, optval, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(!(dbf = gdbm_open(name, 0, GDBM_NEWDB, 00644, NULL))){ + fprintf(stderr, "gdbm_open failed\n"); + return 1; + } + optval = CACHESIZE; + if(gdbm_setopt(dbf, GDBM_CACHESIZE, &optval, sizeof(int)) != 0){ + fprintf(stderr, "gdbm_setopt failed\n"); + gdbm_close(dbf); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + len = sprintf(buf, "%08d", i); + key.dptr = buf; + key.dsize = len; + content.dptr = buf; + content.dsize = len; + /* store a record */ + if(gdbm_store(dbf, key, content, GDBM_REPLACE) != 0){ + fprintf(stderr, "gdbm_store failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + gdbm_close(dbf); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform read command */ +int doread(char *name, int rnum){ + GDBM_FILE dbf; + datum key, content; + int i, err, optval, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(!(dbf = gdbm_open(name, 0, GDBM_READER, 00644, NULL))){ + fprintf(stderr, "gdbm_open failed\n"); + return 1; + } + optval = CACHESIZE; + if(gdbm_setopt(dbf, GDBM_CACHESIZE, &optval, sizeof(int)) != 0){ + fprintf(stderr, "gdbm_setopt failed\n"); + gdbm_close(dbf); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* retrieve a record */ + len = sprintf(buf, "%08d", i); + key.dptr = buf; + key.dsize = len; + content = gdbm_fetch(dbf, key); + if(!content.dptr){ + fprintf(stderr, "gdbm_fetch failed\n"); + err = TRUE; + break; + } + free(content.dptr); + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + gdbm_close(dbf); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + + +/* END OF FILE */ diff --git a/tcejdb/bros/mapreporter b/tcejdb/bros/mapreporter new file mode 100755 index 0000000..50f2131 --- /dev/null +++ b/tcejdb/bros/mapreporter @@ -0,0 +1,72 @@ +#! /usr/bin/perl + +#================================================================ +# mapreporter +# Measure elapsed time of map utilities +#================================================================ + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); + +use constant { + RECNUM => 1000000, + TESTCOUNT => 20, + REMOVETOP => 2, + REMOVEBOTTOM => 8, +}; + +my @commands = ( + "./maptest tcmap " . RECNUM, + "./maptest tctree " . RECNUM, + "./maptest stlmap " . RECNUM, + "./maptest stlmmap " . RECNUM, + "./maptest stlset " . RECNUM, + "./maptest gnuhash " . RECNUM, + "./maptest ggldh " . RECNUM, + "./maptest gglsh " . RECNUM, + "./maptest tcmap -rd " . RECNUM, + "./maptest tctree -rd " . RECNUM, + "./maptest stlmap -rd " . RECNUM, + "./maptest stlmmap -rd " . RECNUM, + "./maptest stlset -rd " . RECNUM, + "./maptest gnuhash -rd " . RECNUM, + "./maptest ggldh -rd " . RECNUM, + "./maptest gglsh -rd " . RECNUM, + ); + +my @table; +foreach my $command (@commands){ + system("sync ; sync"); + my @result; + for(my $i = 0; $i < TESTCOUNT; $i++){ + my $stime = gettimeofday(); + system("$command >/dev/null 2>&1"); + $stime = gettimeofday() - $stime; + printf("%s\t%d\t%0.5f\n", $command, $i + 1, $stime); + push(@result, $stime); + } + @result = sort { $a <=> $b } @result; + for(my $i = 0; $i < REMOVETOP; $i++){ + shift(@result); + } + for(my $i = 0; $i < REMOVEBOTTOM; $i++){ + pop(@result); + } + my $sum = 0; + foreach my $result (@result){ + $sum += $result; + } + my $avg = $sum / scalar(@result); + push(@table, [$command, $avg]); +} + +printf("\n\nRESULT\n"); +foreach my $row (@table){ + printf("%s\t%0.5f\n", $$row[0], $$row[1]); +} +printf("\n"); + + + +# END OF FILE diff --git a/tcejdb/bros/maptest.cc b/tcejdb/bros/maptest.cc new file mode 100644 index 0000000..a1e0848 --- /dev/null +++ b/tcejdb/bros/maptest.cc @@ -0,0 +1,679 @@ +/************************************************************************************************* + * Writing test of map utilities + *************************************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RECBUFSIZ 32 // buffer for records + +using namespace std; + +struct stringhash { // hash function for string + size_t operator()(const string& str) const { + const char *ptr = str.data(); + int size = str.size(); + size_t idx = 19780211; + while(size--){ + idx = idx * 37 + *(uint8_t *)ptr++; + } + return idx; + } +}; + + +// aliases of template instances +typedef map stlmap; +typedef multimap stlmmap; +typedef set stlset; +typedef tr1::unordered_map trhash; +typedef google::dense_hash_map ggldh; +typedef google::sparse_hash_map gglsh; + + +// global variables +const char *g_progname; // program name + + +// function prototypes +int main(int argc, char **argv); +static void usage(void); +static void iprintf(const char *format, ...); +static int runtcmap(int argc, char **argv); +static int runtctree(int argc, char **argv); +static int runstlmap(int argc, char **argv); +static int runstlmmap(int argc, char **argv); +static int runstlset(int argc, char **argv); +static int runtrhash(int argc, char **argv); +static int runggldh(int argc, char **argv); +static int rungglsh(int argc, char **argv); +static int proctcmap(int rnum, bool rd); +static int proctctree(int rnum, bool rd); +static int procstlmap(int rnum, bool rd); +static int procstlmmap(int rnum, bool rd); +static int procstlset(int rnum, bool rd); +static int proctrhash(int rnum, bool rd); +static int procggldh(int rnum, bool rd); +static int procgglsh(int rnum, bool rd); + + +// main routine +int main(int argc, char **argv){ + g_progname = argv[0]; + if(argc < 2) usage(); + int rv = 0; + if(!strcmp(argv[1], "tcmap")){ + rv = runtcmap(argc, argv); + } else if(!strcmp(argv[1], "tctree")){ + rv = runtctree(argc, argv); + } else if(!strcmp(argv[1], "stlmap")){ + rv = runstlmap(argc, argv); + } else if(!strcmp(argv[1], "stlmmap")){ + rv = runstlmmap(argc, argv); + } else if(!strcmp(argv[1], "stlset")){ + rv = runstlset(argc, argv); + } else if(!strcmp(argv[1], "trhash")){ + rv = runtrhash(argc, argv); + } else if(!strcmp(argv[1], "ggldh")){ + rv = runggldh(argc, argv); + } else if(!strcmp(argv[1], "gglsh")){ + rv = rungglsh(argc, argv); + } else { + usage(); + } + return rv; +} + + +// print the usage and exit +static void usage(void){ + fprintf(stderr, "%s: speed checker of map utilities\n", g_progname); + fprintf(stderr, "\n"); + fprintf(stderr, "usage:\n"); + fprintf(stderr, " %s tcmap [-rd] rnum\n", g_progname); + fprintf(stderr, " %s tctree [-rd] rnum\n", g_progname); + fprintf(stderr, " %s stlmap [-rd] rnum\n", g_progname); + fprintf(stderr, " %s stlmmap [-rd] rnum\n", g_progname); + fprintf(stderr, " %s stlset [-rd] rnum\n", g_progname); + fprintf(stderr, " %s trhash [-rd] rnum\n", g_progname); + fprintf(stderr, " %s ggldh [-rd] rnum\n", g_progname); + fprintf(stderr, " %s gglsh [-rd] rnum\n", g_progname); + fprintf(stderr, "\n"); + exit(1); +} + + +// print formatted information string and flush the buffer +static void iprintf(const char *format, ...){ + va_list ap; + va_start(ap, format); + vprintf(format, ap); + fflush(stdout); + va_end(ap); +} + + +// parse arguments of tcmap command +static int runtcmap(int argc, char **argv){ + char *rstr = NULL; + bool rd = false; + for(int i = 2; i < argc; i++){ + if(!rstr && argv[i][0] == '-'){ + if(!strcmp(argv[i], "-rd")){ + rd = true; + } else { + usage(); + } + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!rstr) usage(); + int rnum = tcatoi(rstr); + if(rnum < 1) usage(); + int rv = proctcmap(rnum, rd); + return rv; +} + + +// parse arguments of tctree command +static int runtctree(int argc, char **argv){ + char *rstr = NULL; + bool rd = false; + for(int i = 2; i < argc; i++){ + if(!rstr && argv[i][0] == '-'){ + if(!strcmp(argv[i], "-rd")){ + rd = true; + } else { + usage(); + } + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!rstr) usage(); + int rnum = tcatoi(rstr); + if(rnum < 1) usage(); + int rv = proctctree(rnum, rd); + return rv; +} + + +// parse arguments of stlmap command +static int runstlmap(int argc, char **argv){ + char *rstr = NULL; + bool rd = false; + for(int i = 2; i < argc; i++){ + if(!rstr && argv[i][0] == '-'){ + if(!strcmp(argv[i], "-rd")){ + rd = true; + } else { + usage(); + } + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!rstr) usage(); + int rnum = tcatoi(rstr); + if(rnum < 1) usage(); + int rv = procstlmap(rnum, rd); + return rv; +} + + +// parse arguments of stlmmap command +static int runstlmmap(int argc, char **argv){ + char *rstr = NULL; + bool rd = false; + for(int i = 2; i < argc; i++){ + if(!rstr && argv[i][0] == '-'){ + if(!strcmp(argv[i], "-rd")){ + rd = true; + } else { + usage(); + } + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!rstr) usage(); + int rnum = tcatoi(rstr); + if(rnum < 1) usage(); + int rv = procstlmmap(rnum, rd); + return rv; +} + + +// parse arguments of stlset command +static int runstlset(int argc, char **argv){ + char *rstr = NULL; + bool rd = false; + for(int i = 2; i < argc; i++){ + if(!rstr && argv[i][0] == '-'){ + if(!strcmp(argv[i], "-rd")){ + rd = true; + } else { + usage(); + } + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!rstr) usage(); + int rnum = tcatoi(rstr); + if(rnum < 1) usage(); + int rv = procstlset(rnum, rd); + return rv; +} + + +// parse arguments of trhash command +static int runtrhash(int argc, char **argv){ + char *rstr = NULL; + bool rd = false; + for(int i = 2; i < argc; i++){ + if(!rstr && argv[i][0] == '-'){ + if(!strcmp(argv[i], "-rd")){ + rd = true; + } else { + usage(); + } + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!rstr) usage(); + int rnum = tcatoi(rstr); + if(rnum < 1) usage(); + int rv = proctrhash(rnum, rd); + return rv; +} + + +// parse arguments of ggldh command +static int runggldh(int argc, char **argv){ + char *rstr = NULL; + bool rd = false; + for(int i = 2; i < argc; i++){ + if(!rstr && argv[i][0] == '-'){ + if(!strcmp(argv[i], "-rd")){ + rd = true; + } else { + usage(); + } + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!rstr) usage(); + int rnum = tcatoi(rstr); + if(rnum < 1) usage(); + int rv = procggldh(rnum, rd); + return rv; +} + + +// parse arguments of gglsh command +static int rungglsh(int argc, char **argv){ + char *rstr = NULL; + bool rd = false; + for(int i = 2; i < argc; i++){ + if(!rstr && argv[i][0] == '-'){ + if(!strcmp(argv[i], "-rd")){ + rd = true; + } else { + usage(); + } + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!rstr) usage(); + int rnum = tcatoi(rstr); + if(rnum < 1) usage(); + int rv = procgglsh(rnum, rd); + return rv; +} + + +// perform tcmap command +static int proctcmap(int rnum, bool rd){ + iprintf("\n rnum=%d rd=%d\n\n", rnum, rd); + double stime = tctime(); + { + TCMAP *mymap = tcmapnew2(rnum + 1); + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + int len = sprintf(buf, "%08d", i); + tcmapput(mymap, buf, len, buf, len); + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + if(rd){ + double itime = tctime(); + iprintf("time: %.3f\n", itime - stime); + stime = itime; + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + int len = sprintf(buf, "%08d", i); + int vsiz; + const void *vbuf = tcmapget(mymap, buf, len, &vsiz); + if(!vbuf){ + iprintf("not found\n"); + break; + } + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + } + iprintf("record number: %d\n", tcmaprnum(mymap)); + tcmapdel(mymap); + } + iprintf("time: %.3f\n", tctime() - stime); + iprintf("ok\n\n"); + return 0; +} + + +// perform tctree command +static int proctctree(int rnum, bool rd){ + iprintf("\n rnum=%d rd=%d\n\n", rnum, rd); + double stime = tctime(); + { + TCTREE *mytree = tctreenew(); + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + int len = sprintf(buf, "%08d", i); + tctreeput(mytree, buf, len, buf, len); + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + if(rd){ + double itime = tctime(); + iprintf("time: %.3f\n", itime - stime); + stime = itime; + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + int len = sprintf(buf, "%08d", i); + int vsiz; + const void *vbuf = tctreeget(mytree, buf, len, &vsiz); + if(!vbuf){ + iprintf("not found\n"); + break; + } + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + } + iprintf("record number: %d\n", tctreernum(mytree)); + tctreedel(mytree); + } + iprintf("time: %.3f\n", tctime() - stime); + iprintf("ok\n\n"); + return 0; +} + + +// perform stlmap command +static int procstlmap(int rnum, bool rd){ + iprintf("\n rnum=%d rd=%d\n\n", rnum, rd); + double stime = tctime(); + { + stlmap mymap; + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + sprintf(buf, "%08d", i); + mymap.insert(stlmap::value_type(buf, buf)); + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + if(rd){ + double itime = tctime(); + iprintf("time: %.3f\n", itime - stime); + stime = itime; + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + sprintf(buf, "%08d", i); + stlmap::const_iterator it = mymap.find(buf); + if(it == mymap.end()){ + iprintf("not found\n"); + break; + } + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + } + iprintf("record number: %d\n", mymap.size()); + } + iprintf("time: %.3f\n", tctime() - stime); + iprintf("ok\n\n"); + return 0; +} + + +// perform multimap command +static int procstlmmap(int rnum, bool rd){ + iprintf("\n rnum=%d rd=%d\n\n", rnum, rd); + double stime = tctime(); + { + stlmmap mymap; + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + sprintf(buf, "%08d", i); + mymap.insert(stlmmap::value_type(buf, buf)); + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + if(rd){ + double itime = tctime(); + iprintf("time: %.3f\n", itime - stime); + stime = itime; + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + sprintf(buf, "%08d", i); + stlmmap::const_iterator it = mymap.find(buf); + if(it == mymap.end()){ + iprintf("not found\n"); + break; + } + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + } + iprintf("record number: %d\n", mymap.size()); + } + iprintf("time: %.3f\n", tctime() - stime); + iprintf("ok\n\n"); + return 0; +} + + +// perform stlset command +static int procstlset(int rnum, bool rd){ + iprintf("\n rnum=%d rd=%d\n\n", rnum, rd); + double stime = tctime(); + { + stlset mymap; + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + sprintf(buf, "%08d", i); + mymap.insert(stlset::value_type(buf)); + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + if(rd){ + double itime = tctime(); + iprintf("time: %.3f\n", itime - stime); + stime = itime; + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + sprintf(buf, "%08d", i); + stlset::const_iterator it = mymap.find(buf); + if(it == mymap.end()){ + iprintf("not found\n"); + break; + } + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + } + iprintf("record number: %d\n", mymap.size()); + } + iprintf("time: %.3f\n", tctime() - stime); + iprintf("ok\n\n"); + return 0; +} + + +// perform trhash command +static int proctrhash(int rnum, bool rd){ + iprintf("\n rnum=%d rd=%d\n\n", rnum, rd); + double stime = tctime(); + { + trhash mymap; + mymap.rehash(rnum + 1); + mymap.max_load_factor(1.0); + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + sprintf(buf, "%08d", i); + mymap.insert(trhash::value_type(buf, buf)); + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + if(rd){ + double itime = tctime(); + iprintf("time: %.3f\n", itime - stime); + stime = itime; + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + sprintf(buf, "%08d", i); + trhash::const_iterator it = mymap.find(buf); + if(it == mymap.end()){ + iprintf("not found\n"); + break; + } + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + } + iprintf("record number: %d\n", mymap.size()); + } + iprintf("time: %.3f\n", tctime() - stime); + iprintf("ok\n\n"); + return 0; +} + + +// perform ggldh command +static int procggldh(int rnum, bool rd){ + iprintf("\n rnum=%d rd=%d\n\n", rnum, rd); + double stime = tctime(); + { + ggldh mymap; + mymap.set_empty_key(""); + mymap.resize(rnum + 1); + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + sprintf(buf, "%08d", i); + mymap.insert(ggldh::value_type(buf, buf)); + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + if(rd){ + double itime = tctime(); + iprintf("time: %.3f\n", itime - stime); + stime = itime; + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + sprintf(buf, "%08d", i); + ggldh::const_iterator it = mymap.find(buf); + if(it == mymap.end()){ + iprintf("not found\n"); + break; + } + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + } + iprintf("record number: %d\n", mymap.size()); + } + iprintf("time: %.3f\n", tctime() - stime); + iprintf("ok\n\n"); + return 0; +} + + +// perform gglsh command +static int procgglsh(int rnum, bool rd){ + iprintf("\n rnum=%d rd=%d\n\n", rnum, rd); + double stime = tctime(); + { + gglsh mymap; + mymap.resize(rnum + 1); + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + sprintf(buf, "%08d", i); + mymap.insert(gglsh::value_type(buf, buf)); + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + if(rd){ + double itime = tctime(); + iprintf("time: %.3f\n", itime - stime); + stime = itime; + for(int i = 1; i <= rnum; i++){ + char buf[RECBUFSIZ]; + sprintf(buf, "%08d", i); + gglsh::const_iterator it = mymap.find(buf); + if(it == mymap.end()){ + iprintf("not found\n"); + break; + } + if(rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); + } + } + } + iprintf("record number: %d\n", mymap.size()); + } + iprintf("time: %.3f\n", tctime() - stime); + iprintf("ok\n\n"); + return 0; +} + + + +// END OF FILE diff --git a/tcejdb/bros/ndbmtest.c b/tcejdb/bros/ndbmtest.c new file mode 100644 index 0000000..5fb2644 --- /dev/null +++ b/tcejdb/bros/ndbmtest.c @@ -0,0 +1,204 @@ +/************************************************************************************************* + * Writing test of New Database Manager + *************************************************************************************************/ + + +#include +#include +#include +#include +#include +#include + +#undef TRUE +#define TRUE 1 /* boolean true */ +#undef FALSE +#define FALSE 0 /* boolean false */ + +#define RECBUFSIZ 32 /* buffer for records */ + + +/* global variables */ +const char *progname; /* program name */ +int showprgr; /* whether to show progression */ + + +/* function prototypes */ +int main(int argc, char **argv); +void usage(void); +int runwrite(int argc, char **argv); +int runread(int argc, char **argv); +int dowrite(char *name, int rnum); +int doread(char *name, int rnum); + + +/* main routine */ +int main(int argc, char **argv){ + int rv; + progname = argv[0]; + showprgr = TRUE; + if(getenv("HIDEPRGR")) showprgr = FALSE; + if(argc < 2) usage(); + rv = 0; + if(!strcmp(argv[1], "write")){ + rv = runwrite(argc, argv); + } else if(!strcmp(argv[1], "read")){ + rv = runread(argc, argv); + } else { + usage(); + } + return rv; +} + + +/* print the usage and exit */ +void usage(void){ + fprintf(stderr, "%s: test cases for New Database Manager\n", progname); + fprintf(stderr, "\n"); + fprintf(stderr, "usage:\n"); + fprintf(stderr, " %s write name rnum\n", progname); + fprintf(stderr, " %s read name rnum\n", progname); + fprintf(stderr, "\n"); + exit(1); +} + + +/* parse arguments of write command */ +int runwrite(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dowrite(name, rnum); + return rv; +} + + +/* parse arguments of read command */ +int runread(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = doread(name, rnum); + return rv; +} + + +/* perform write command */ +int dowrite(char *name, int rnum){ + DBM *db; + datum key, content; + int i, err, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(!(db = dbm_open(name, O_RDWR | O_CREAT | O_TRUNC, 00644))){ + fprintf(stderr, "dbm_open failed\n"); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + len = sprintf(buf, "%08d", i); + key.dptr = buf; + key.dsize = len; + content.dptr = buf; + content.dsize = len; + /* store a record */ + if(dbm_store(db, key, content, DBM_REPLACE) < 0){ + fprintf(stderr, "dbm_store failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + dbm_close(db); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform read command */ +int doread(char *name, int rnum){ + DBM *db; + datum key, content; + int i, err, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(!(db = dbm_open(name, O_RDONLY, 00644))){ + fprintf(stderr, "dbm_open failed\n"); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* retrieve a record */ + len = sprintf(buf, "%08d", i); + key.dptr = buf; + key.dsize = len; + content = dbm_fetch(db, key); + if(!content.dptr){ + fprintf(stderr, "dbm_fetch failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + dbm_close(db); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + + +/* END OF FILE */ diff --git a/tcejdb/bros/qdbmtest.c b/tcejdb/bros/qdbmtest.c new file mode 100644 index 0000000..f89fc04 --- /dev/null +++ b/tcejdb/bros/qdbmtest.c @@ -0,0 +1,375 @@ +/************************************************************************************************* + * Writing test of Quick Database Manager + *************************************************************************************************/ + + +#include +#include +#include +#include +#include +#include + +#undef TRUE +#define TRUE 1 /* boolean true */ +#undef FALSE +#define FALSE 0 /* boolean false */ + +#define RECBUFSIZ 32 /* buffer for records */ + + +/* global variables */ +const char *progname; /* program name */ +int showprgr; /* whether to show progression */ + + +/* function prototypes */ +int main(int argc, char **argv); +void usage(void); +int runwrite(int argc, char **argv); +int runread(int argc, char **argv); +int runbtwrite(int argc, char **argv); +int runbtread(int argc, char **argv); +int myrand(void); +int dowrite(char *name, int rnum); +int doread(char *name, int rnum); +int dobtwrite(char *name, int rnum, int rnd); +int dobtread(char *name, int rnum, int rnd); + + +/* main routine */ +int main(int argc, char **argv){ + int rv; + progname = argv[0]; + showprgr = TRUE; + if(getenv("HIDEPRGR")) showprgr = FALSE; + srand48(1978); + if(argc < 2) usage(); + rv = 0; + if(!strcmp(argv[1], "write")){ + rv = runwrite(argc, argv); + } else if(!strcmp(argv[1], "read")){ + rv = runread(argc, argv); + } else if(!strcmp(argv[1], "btwrite")){ + rv = runbtwrite(argc, argv); + } else if(!strcmp(argv[1], "btread")){ + rv = runbtread(argc, argv); + } else { + usage(); + } + return rv; +} + + +/* print the usage and exit */ +void usage(void){ + fprintf(stderr, "%s: test cases for Quick Database Manager\n", progname); + fprintf(stderr, "\n"); + fprintf(stderr, "usage:\n"); + fprintf(stderr, " %s write name rnum\n", progname); + fprintf(stderr, " %s read name rnum\n", progname); + fprintf(stderr, " %s btwrite [-rnd] name rnum\n", progname); + fprintf(stderr, " %s btread [-rnd] name rnum\n", progname); + fprintf(stderr, "\n"); + exit(1); +} + + +/* parse arguments of write command */ +int runwrite(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dowrite(name, rnum); + return rv; +} + + +/* parse arguments of read command */ +int runread(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = doread(name, rnum); + return rv; +} + + +/* parse arguments of btwrite command */ +int runbtwrite(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rnd, rv; + name = NULL; + rstr = NULL; + rnum = 0; + rnd = FALSE; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + if(!name && !strcmp(argv[i], "-rnd")){ + rnd = TRUE; + } else { + usage(); + } + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dobtwrite(name, rnum, rnd); + return rv; +} + + +/* parse arguments of btread command */ +int runbtread(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rnd, rv; + name = NULL; + rstr = NULL; + rnum = 0; + rnd = FALSE; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + if(!name && !strcmp(argv[i], "-rnd")){ + rnd = TRUE; + } else { + usage(); + } + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dobtread(name, rnum, rnd); + return rv; +} + + +/* pseudo random number generator */ +int myrand(void){ + static int cnt = 0; + return (lrand48() + cnt++) & 0x7FFFFFFF; +} + + +/* perform write command */ +int dowrite(char *name, int rnum){ + DEPOT *depot; + int i, err, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(!(depot = dpopen(name, DP_OWRITER | DP_OCREAT | DP_OTRUNC, rnum * 3))){ + fprintf(stderr, "dpopen failed\n"); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + len = sprintf(buf, "%08d", i); + if(!dpput(depot, buf, len, buf, len, DP_DOVER)){ + fprintf(stderr, "dpput failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(!dpclose(depot)){ + fprintf(stderr, "dpclose failed\n"); + return 1; + } + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform read command */ +int doread(char *name, int rnum){ + DEPOT *depot; + int i, err, len; + char buf[RECBUFSIZ], vbuf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(!(depot = dpopen(name, DP_OREADER, -1))){ + fprintf(stderr, "dpopen failed\n"); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + len = sprintf(buf, "%08d", i); + if(dpgetwb(depot, buf, len, 0, RECBUFSIZ, vbuf) == -1){ + fprintf(stderr, "dpget failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(!dpclose(depot)){ + fprintf(stderr, "dpclose failed\n"); + return 1; + } + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform btwrite command */ +int dobtwrite(char *name, int rnum, int rnd){ + VILLA *villa; + int i, err, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(!(villa = vlopen(name, VL_OWRITER | VL_OCREAT | VL_OTRUNC, VL_CMPLEX))){ + fprintf(stderr, "vlopen failed\n"); + return 1; + } + if(rnd){ + vlsettuning(villa, 77, 256, rnum / 77, -1); + } else { + vlsettuning(villa, 101, 256, 16, 16); + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + len = sprintf(buf, "%08d", rnd ? myrand() % rnum + 1 : i); + if(!vlput(villa, buf, len, buf, len, VL_DOVER)){ + fprintf(stderr, "vlput failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(!vlclose(villa)){ + fprintf(stderr, "vlclose failed\n"); + return 1; + } + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform btread command */ +int dobtread(char *name, int rnum, int rnd){ + VILLA *villa; + int i, err, len; + const char *val; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(!(villa = vlopen(name, VL_OREADER, VL_CMPLEX))){ + fprintf(stderr, "vlopen failed\n"); + return 1; + } + if(rnd){ + vlsettuning(villa, 37, 200, rnum / 45, 512); + } else { + vlsettuning(villa, 101, 256, 16, 16); + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + len = sprintf(buf, "%08d", rnd ? myrand() % rnum + 1 : i); + if(!(val = vlgetcache(villa, buf, len, NULL))){ + fprintf(stderr, "vlget failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(!vlclose(villa)){ + fprintf(stderr, "vlclose failed\n"); + return 1; + } + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + + +/* END OF FILE */ diff --git a/tcejdb/bros/reporter b/tcejdb/bros/reporter new file mode 100755 index 0000000..04d3478 --- /dev/null +++ b/tcejdb/bros/reporter @@ -0,0 +1,141 @@ +#! /usr/bin/perl + +#================================================================ +# reporter +# Measure elapsed time and database size of DBM brothers +#================================================================ + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); + +use constant { + RECNUM => 1000000, + TESTCOUNT => 20, + REMOVETOP => 2, + REMOVEBOTTOM => 8, +}; + +my @commands = ( + './tctest write casket.tch ' . RECNUM, + './tctest read casket.tch ' . RECNUM, + './qdbmtest write casket.qdbh ' . RECNUM, + './qdbmtest read casket.qdbh ' . RECNUM, + './ndbmtest write casket.ndbh ' . RECNUM, + './ndbmtest read casket.ndbh ' . RECNUM, + './sdbmtest write casket.sdbh ' . RECNUM, + './sdbmtest read casket.sdbh ' . RECNUM, + './gdbmtest write casket.gdbh ' . RECNUM, + './gdbmtest read casket.gdbh ' . RECNUM, + './tdbtest write casket.tdbh ' . RECNUM, + './tdbtest read casket.tdbh ' . RECNUM, + './cdbtest write casket.cdbh ' . RECNUM, + './cdbtest read casket.cdbh ' . RECNUM, + './bdbtest write casket.bdbh ' . RECNUM, + './bdbtest read casket.bdbh ' . RECNUM, + './tctest btwrite casket.tcb ' . RECNUM, + './tctest btread casket.tcb ' . RECNUM, + './tctest btwrite -rnd casket.tcb_r ' . RECNUM, + './tctest btread -rnd casket.tcb_r ' . RECNUM, + './qdbmtest btwrite casket.qdbb ' . RECNUM, + './qdbmtest btread casket.qdbb ' . RECNUM, + './qdbmtest btwrite -rnd casket.qdbb_r ' . RECNUM, + './qdbmtest btread -rnd casket.qdbb_r ' . RECNUM, + './bdbtest btwrite casket.bdbb ' . RECNUM, + './bdbtest btread casket.bdbb ' . RECNUM, + './bdbtest btwrite -rnd casket.bdbb_r ' . RECNUM, + './bdbtest btread -rnd casket.bdbb_r ' . RECNUM, + './tctest flwrite casket.tcf ' . RECNUM, + './tctest flread casket.tcf ' . RECNUM, + ); + +my @names = ( + 'casket.tch', + 'casket.qdbh', + 'casket.ndbh', + 'casket.sdbh', + 'casket.gdbh', + 'casket.tdbh', + 'casket.cdbh', + 'casket.bdbh', + 'casket.tcb', + 'casket.tcb_r', + 'casket.qdbb', + 'casket.qdbb_r', + 'casket.bdbb', + 'casket.bdbb_r', + 'casket.tcf', + ); + +foreach my $name (@names){ + my @paths = glob("$name*"); + foreach my $path (@paths){ + unlink($path); + } +} + +my @table; +foreach my $command (@commands){ + system('sync ; sync'); + $ENV{'HIDEPRGR'} = 1; + my @result; + for(my $i = 0; $i < TESTCOUNT; $i++){ + my $stime = gettimeofday(); + system("$command >/dev/null 2>&1"); + $stime = gettimeofday() - $stime; + printf("%s\t%d\t%0.5f\n", $command, $i + 1, $stime); + push(@result, $stime); + } + @result = sort { $a <=> $b } @result; + for(my $i = 0; $i < REMOVETOP; $i++){ + shift(@result); + } + for(my $i = 0; $i < REMOVEBOTTOM; $i++){ + pop(@result); + } + my $sum = 0; + foreach my $result (@result){ + $sum += $result; + } + my $avg = $sum / scalar(@result); + push(@table, [$command, $avg]); +} + +printf("\n\nRESULT\n\n"); +foreach my $row (@table){ + printf("%s\t%0.5f\n", $$row[0], $$row[1]); +} +printf("\n"); + +my @sizes; +foreach my $name (@names){ + my @paths = glob("$name*"); + my $sum = 0; + foreach my $path (@paths){ + my @sbuf = stat($path); + $sum += $sbuf[7]; + } + printf("%s\t%s\n", $name, $sum); + push(@sizes, $sum); +} +printf("\n"); + +printf("%s,%.5f,%.5f,%d\n", "TC", $table[0][1], $table[1][1], $sizes[0]); +printf("%s,%.5f,%.5f,%d\n", "QDBM", $table[2][1], $table[3][1], $sizes[1]); +printf("%s,%.5f,%.5f,%d\n", "NDBM", $table[4][1], $table[5][1], $sizes[2]); +printf("%s,%.5f,%.5f,%d\n", "SDBM", $table[6][1], $table[7][1], $sizes[3]); +printf("%s,%.5f,%.5f,%d\n", "GDBM", $table[8][1], $table[9][1], $sizes[4]); +printf("%s,%.5f,%.5f,%d\n", "TDB", $table[10][1], $table[11][1], $sizes[5]); +printf("%s,%.5f,%.5f,%d\n", "CDB", $table[12][1], $table[13][1], $sizes[6]); +printf("%s,%.5f,%.5f,%d\n", "BDB", $table[14][1], $table[15][1], $sizes[7]); +printf("%s,%.5f,%.5f,%d\n", "TC-BT-ASC", $table[16][1], $table[17][1], $sizes[8]); +printf("%s,%.5f,%.5f,%d\n", "TC-BT-RND", $table[18][1], $table[19][1], $sizes[9]); +printf("%s,%.5f,%.5f,%d\n", "QDBM-BT-ASC", $table[20][1], $table[21][1], $sizes[10]); +printf("%s,%.5f,%.5f,%d\n", "QDBM-BT-RND", $table[22][1], $table[23][1], $sizes[11]); +printf("%s,%.5f,%.5f,%d\n", "BDB-BT-ASC", $table[24][1], $table[25][1], $sizes[12]); +printf("%s,%.5f,%.5f,%d\n", "BDB-BT-RND", $table[26][1], $table[27][1], $sizes[13]); +printf("%s,%.5f,%.5f,%d\n", "TC-FIXED", $table[28][1], $table[29][1], $sizes[14]); + + + +# END OF FILE diff --git a/tcejdb/bros/result.xls b/tcejdb/bros/result.xls new file mode 100644 index 0000000000000000000000000000000000000000..cbd2a4363c97b3983b1d3695654049792e68d1f9 GIT binary patch literal 35840 zcmeHQ3v^t?dH(P2m9%=;URky*ENgu&zhqkylI2%0SZ@;%OO|B;4wz$B(v`KbysNHO zU{Nr_3JrNRAruoQoR*X%O%5rE$%8iK1VNh~Qp_REY3h=Y#DRv>rfFkR@+?ICzPWq% zad-8y4FSBfntNyd`RAXRf9B5oGxz_mzWr?BnNOWu`eSjp9paYXPv%N?0$fA5*^YY! z>64R;+V>`e0m`_y5C?KS=*a9WS!O(w^Q{R4-6B60mwZWo0#}h2;P=arn07Unfyx4qcwkD z^TnDsBhHao)cJw89F9*)G!1?67`s-JK#tZdrUWG-BNCQTsTH3*6>rrP7|$c?^fVGU zla>a@gf0cbF`-Q}CUjXf1J1E{ZKtGj+bnSUE(ngry{)Rw>GRyYrpArZkRIe1WGnX79O^CRZdSBeK+n1tb=qR_U=df6y) zzyS62hARGmu{|1%)+Y`quPRrOh69FAua$NY966WD5?PGtc9qr0b=&t>M8o^n`Sj)B_tL~v#jGS0 z0~{}np|G3;8iKUW*-wW*6`WMl;lG{X8v8TmEPiH5uEmGn*a;RK zi@m%;q0PbnELoElKT9g6fM-tu_oRgvPEl?F9u8Vu2k)i_+B=%m6^@wY1cc^KiEtQ5ICN+(*+d3X}i!Ha>Pn--sInJ1;5R($}! z+~NZHaF0tD?^xC(?}cL?bzeA^t?or*ZgpQYmZR>A$8>d-%>yrTV?dMt zlIvuF$k{F?OZHhT27+WINMW8(*-4h+I>|rFDm;#&JaUJYE8>~Z8gOk8-^(16^AIk8 zUjqMY5$~(=g&fS>oV~e5G?~HPfy4grA*0(Iju@eSqot|cXbOiS17>&_vs9NkJTe&3 zMLHVWTXEZibjGaPUh8%xZm3RoOB1fm>e|%Ogtij9s;RrGac^@%tgE9X4@I{$MU&zY zq~wdvdbhOhZSHF8>~7oLp+QE-mdyZKZiR)hccf=H5{QgMOrymgfdD;h8twj||DYK* zYO8CiH#+kzwenrrado;3+p6oF8CF{vnl>1buxT2Noo!?g+hkPuhkMOnUm$qU2!;F1 z@H%JS<W%zAbQ4x3JLtJ2mIJu`CyebZ!}vV993)!P{uG>yH1TT&_(ZRgImU9HC6w(C;( z5N&6Nd2?!CZmzC%wzJqOw>uoTDc~QBXW=;6;zTZuA_sz_Msp}Q9PtMu@hmmfTQ@s% z9Jg{@9So>PC<0C!fnj6V>FF^8-3x=eqcp|H_ae3pz}f6xeq zB1Vsi;9&2_pg&^vA=c|3!SD`5hK)XZtdmSQbjS=EHxJ-)I21OG$N=)!LRhG&F~TT4 zjM9@UTOegtW%rB(dJn~$6@PNJRd3yzoJTO@s)w?>Ir_vp(^p#QjwhwNdFEl1L>Z62 zlXA4S=_t=M%9_mZA#>0i#gt}js@_t)F=bf1rdiDzATMJxA-X2QPKt!$eP9g5A0*Xb z6=4$tiNSRSo@Jw-BUyDXy3+2W>bz^ife0VI1e(JU_Ec>p`iMT(Plmza;6Uy-BfSHL ze{hfwGrQmJ)x0K5(F9_mHV&bYtOUb1`3FZ#CYXNYw9pMdN*o?Rd|MTVo-rH=qo85K zKWyB{!2(yiwlWspm=La8TU}k9*F6$s&4&D8|6wzNr6E=VN39}nM#!Uq(ZmHdZ`iFz&V5QNzue&bF$PQ{Jn?PRT^!&BM)MU#40Yo!uzm%r2A(^-E&!PhJJ26a8AAY;I> z-D-$iI0KS9kn)7itqiL*uJGQgu4{mO`oW7Tq%zb+x%E?0qMCr&K_a#%tLwa8ApxLMyJu4O+jgYLaQzXRtYx$=qpO`dn7l?!mX z#n$d~xp8fjO|zyrGomdQ@=jjbac9oXuC{BMYMjvXk6XR%UCSSO65etj2OsRP^Bzy| zO6MlGRNTe>oZ7W~_4f8GBkR8)GH(##-}q& zju0(|$LBLkwkOeJSx!6>%98mCZ$ewD%Ec-ISXmN`bGZ|SEoTahtBezmJ6|V^OPUjg z6)gouUODlwMNWZ{gHAjikT_x7GCN^7k4=GbjdJ2~&2qwu6>J&1CB}O!pukJ+H_p0Q z^6sSm;CM*vkJp6U6P#GmF-Ed0WEHO8z&jeQNGqa0U8S>^O8~X7*_UN15A$XNUWQzD znhPfT46tG6I-Ks?vo zvErfYzgvIQ#r(P~m%m)NHjnu^Zc5dGg%u+;96l&&9z`wZZ1FogFVAiuH4iX-a(qXm z@wTV`M^S@J25Ma&x@{iwbHrhfhN~4LHJslnYPpJ<|NS|C);_vOQOi}-ZoB*2$A<$2 zQ3$Eg{o;F1HIfE7UW#6@DCU7@%XkE%D3bp^x#iU#ezv~!5|uv>6b)Jb;*SpQ-`1(> zhiyA4zV}H*jU&AjoVTduD{35>Y+Jsfc2v4Px$WdeMJ->|PkUeIXZjc9HjsBX0Rgo! z_q~c5pOI44AFOGCqK2brMU66L2iys&S{qo)KRg7pr{#9 zYLq*DvU~fho8SH5OR8=Q6t&TPpWFP*_7_#%7AR`-fA-ZJ<|oHX@tjz_;8oPhqtv{L z+AWuSGyJX%IjU}6MeWP08rrK$U8-(gMeS=pebq&3oJC4;x2P2=YJ`z*l!krM{*C8e z`p3wxRNV>{wSO-E{%?=I#~Q(firT|heKD8RIRBLL&95543l%lONR4k_eRB8Ow$rqV~VbzY{v}zWJ(dI7R`Vf|pLXNsZ5QsalV$7)LK>BvRvBT%Vjc`>xj>`}{9e z-HH^oOCQ`93~c_j;u|(2oHtE15_KLQn2`{%N`(J34S7{5xWX^&Ybh*XLzfMaeQ_M zrfTe+&T>}ds?BW?M)TP6N6=8Ep$`cSXT z{>|zQ`^vFqxw-D&xNDyTM>8(Nf%DwI?J^@cKAg=SZg+5Sba&94J;$y68=rjsMD5?K z^QklS_Fcm(GZ$vv8@+jQY~_bbo(En|LX58vnRZvjXS_#1rAWoTUgaw!UCwx%w=^%W zE^mB>{*T9ZxxRh-kE9Nu2QWFa27O+S^m{aU#I4CjFDGV~O3(39rlXsL9$>rVfesYg z6omOY0NVsQZs76cX#sw04{7i5LP|p6wW#%Fcsq#jJTI4;JndR6Y;DpltzzKX1|qFM zFA~g&cxRd~c-@$T7I3#pFpjoBB?e~cdr)E2Rk>`0qjgdRA5cw>SDj1LZdLWMF}xZD zDl;y@^qa3RbG@AKEypcOJAQlx%Z9Zoj9E9)jV5yC&(*rnq(+?aP1`^>m^SMMS|f#= zow|V&6BDrp7SFMPE$DadO;(}FE?Mn0z;}zoFM(%&XLma=PJ%%XU|_H=yTLr-JKY*k z$x+>;xQ%2ok+j_xgtjMz_D;~o*{7G%Ddm$p;maUOeG~#>VofQ#?}hI|;7O#HsT=?# zIp#1hu*h`{vO`Z4iyX1IBJy)#D;KMlk%|qE6C#!6-t}>S3ax6e0B7ry?~rA*4p2O) z=oZNe)uQ-7ww^)qaWlO2oVr!ph{M5cxg|-@DPi_=x+r0&N0HTy^<3iRKJYkNQI1)~ zb_--N@~j3AQ5pF@n)niY4xUt-r zr&$~YYI+hA4Rfihrmk)ZrI3J+Dhc-{gl&mcy%a#=gOFG`LCi*CeOlwFm`QG}0!|kE z4*4vuPPx?;m0R_kjNA(LINaOgy#vNbMs9WV?8O`XIGTRW?;YNRa%*mq+?uCif4nZR zfO6|X6$?}a7Eo?oG`-w9`8~C6B+9MUdi*-dt$IwT+X-29my%ldB6MAX)cQzVJk=pn z&swOnhB%kPsfD`Z$B)N^x~&&fsQcS<5bAPMmgV$9-MP@tOQG!)>UKg1c^1OPGY;{S z%lT{YHzTn40tj`B<|x#?_w;>@R$r!G%QT9K3*RK-oGlDFMYP2JdKm(KCnTKdL^STe z@VqLw7NVmSw5cbHSxieWX6l1V1+@*jGKgN$-JK?H99x-YXr(v3QMO?UfEA+14u^2B*a7`)(!-i!%vU zUeWgZ^kO_?MQLrj_*S?img2C<^6AU)^j%^n!{jHyE(|fBz5<7nzQl9~1%} z(^W~Et~pqAT|tYk#DY-#ob0(a(Vi<-pq+bWYpxPnb4_8(RfhL&OXF$D_E)sh^3jqj ziohaM?YLIru*FEiNTz~ZB$P`lF58AH8^=p-wBD!Z(Sj>xzm-S(t(f%|1X4I!QB4>s zC};2@kdrB3ugnC4kd}g%4o1pOdT&*jCds^tT3`Jpkyqp@EU%LI7qz-d!i!p5Ibh1# z$^lcBR!K0v>UWkAwXj;2SeK}ERT3;}S(OInO$WQR*k~MU8*4B)Hlo&4NwBCT)va3- z@S;{!mrocCvAj_Wsw7y{dMXJ9!5?GLlT??e)fDiM2p6}QD#UB!`QyRy=JBCBt{?xv z_&wttOq`whR4LYzl6&|6>1RLu?6DslJC#i4V-SkAsnWQCQm;dz7m6JUg1{ItqL(q; z#~49?hx9zGJ#k`EPSo{dZ+A*g0ly!=Nx-Ea@lzx?rf88SkPzdKU|sjM` zasu(m6UYm?FTVJq)b&qF-KkT61DpN?d_TzdpAhaTfgp7!0I0(s{0Zd&t2+T-CwD?o zV}p{bvy_;{pb{0cErslSm;K(PT57TH8pFwNG4y>dv=X*$tac%lvGa5&V}}EWL!B)< zVNI^s$qRXH_a77#vhSZ+*?JS1J9Ynoc7oHQVct0-+VKPw6?}UfYG#zFo&9<-$It6f z^AD~}ZSOj<_jIKyR`Y45qBS?5RIIk+wqms%$4j0B#cIx|)$n}vlToU+l)1UaJC&As zK;(q+#Lo$%eC32u9&*C?ir5J&N(1Bi=EQrwm8x~u8k9=?1m6rM&$^4RVX1IEDcu*h zU0|xVx$dIIF0~u8ZNqREZL@i}s9P4|t|G*#bDp1Q!mZ;(lDaBoI8uxoH|wrKK&gbD zD=~{^3rJn`T#1?g>Pn@Ys-!OIZq{8?h^Crrk#wh;&`~dSZaV5ZT0!MSYLzPkch_pf zSwNXgYbVR1=BB{TqHG0rWkl~X5NA;(GLGJ`z;5yb#9gcESC#+LbqlTkJs@8J&MGA~ zpQ{A~XR%6Ew}tV##lG`Fi-_xxS^@8a6~PXS9*TpS6jO|F*cuQb($J`B4MLSKUGCqk zdG2>#%f(v$rbP2tM<h(?9WmVs={p$r{8Iz&XE7PI{m}T!1^zoh?VT zjzk@QLa7|G$BQ#xOc}M9u`jdntw#%csCizBrg`yQ)E;kHRL6g@INs1JeSa4bEN%a^ z2zasY(DlzrP*v7cwRy76s<({XEB64Hfz!_TQcF(d^ zmhuA2Pi}(2B2kb0d}N3VtAovz@AcHSPFywLIO!q%I-ICueR5ju zYH}r2)81Z(S3~ShG|o(<`|`AOXVBVK>p9nUO3&n6dIi}Xc*pTIbqK=omPT9qA)HYe zat3nvbk6P?63Mtf90z`njb4^|m6%)y4uUgx{>3kTyL-U<(A^%{u=dN()d2PiJYNhk zy%V0o-#&Q0ZyST>k=On3JjMGGJm0lE1cm|%Q!#{&({!c?{;lMNCr~lHk zg~t=L_Q0V)$arV`Uy=Yu8Z0M4SzW;>xwZ#-!=d3&f5d1#(rXS%17Nc2wU$d;zlBpf zxFY_8?Ze#vb1%=+AMXFR!Bcau0iOH(UGUs%cf)g^zaO6a z@p1UXeLl|$=$MDjzWb1wc%-kaj+Dln3ax;_PB3zy5&zCa3V=ou-0riO@sGg7t7H5c zTn|D;(W;xBc>J-uci$U1_Rw8Fz3;wQB5*P;!+{J3G91WoAj5$S2QnPUa3I5h3@eii!nPzG6sJO3*R_vXvt4fqxCN&iM}O-&pyq$7~4TJ2i4E2NT@2C_fBavz~LT2kb9s@x@xBec(68!5NCf}eCj`jUt^(zg<9v2mfvGX$DH|E&!wZ*% r|5FfxjUDS?su3`^`fNJ~{$Iv|qvbPrzRu#jbv<8N=aT=eT>k$9-m$_X literal 0 HcmV?d00001 diff --git a/tcejdb/bros/sdbmtest.c b/tcejdb/bros/sdbmtest.c new file mode 100644 index 0000000..a3bd4fe --- /dev/null +++ b/tcejdb/bros/sdbmtest.c @@ -0,0 +1,204 @@ +/************************************************************************************************* + * Writing test of Substitute Database Manager + *************************************************************************************************/ + + +#include +#include +#include +#include +#include +#include + +#undef TRUE +#define TRUE 1 /* boolean true */ +#undef FALSE +#define FALSE 0 /* boolean false */ + +#define RECBUFSIZ 32 /* buffer for records */ + + +/* global variables */ +const char *progname; /* program name */ +int showprgr; /* whether to show progression */ + + +/* function prototypes */ +int main(int argc, char **argv); +void usage(void); +int runwrite(int argc, char **argv); +int runread(int argc, char **argv); +int dowrite(char *name, int rnum); +int doread(char *name, int rnum); + + +/* main routine */ +int main(int argc, char **argv){ + int rv; + progname = argv[0]; + showprgr = TRUE; + if(getenv("HIDEPRGR")) showprgr = FALSE; + if(argc < 2) usage(); + rv = 0; + if(!strcmp(argv[1], "write")){ + rv = runwrite(argc, argv); + } else if(!strcmp(argv[1], "read")){ + rv = runread(argc, argv); + } else { + usage(); + } + return rv; +} + + +/* print the usage and exit */ +void usage(void){ + fprintf(stderr, "%s: test cases for Substitute Database Manager\n", progname); + fprintf(stderr, "\n"); + fprintf(stderr, "usage:\n"); + fprintf(stderr, " %s write name rnum\n", progname); + fprintf(stderr, " %s read name rnum\n", progname); + fprintf(stderr, "\n"); + exit(1); +} + + +/* parse arguments of write command */ +int runwrite(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dowrite(name, rnum); + return rv; +} + + +/* parse arguments of read command */ +int runread(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = doread(name, rnum); + return rv; +} + + +/* perform write command */ +int dowrite(char *name, int rnum){ + DBM *db; + datum key, val; + int i, err, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(!(db = dbm_open(name, O_RDWR | O_CREAT | O_TRUNC, 00644))){ + fprintf(stderr, "dbm_open failed\n"); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + len = sprintf(buf, "%08d", i); + key.dptr = buf; + key.dsize = len; + val.dptr = buf; + val.dsize = len; + /* store a record */ + if(dbm_store(db, key, val, DBM_REPLACE) < 0){ + fprintf(stderr, "dbm_store failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + dbm_close(db); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform read command */ +int doread(char *name, int rnum){ + DBM *db; + datum key, val; + int i, err, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(!(db = dbm_open(name, O_RDONLY, 00644))){ + fprintf(stderr, "dbm_open failed\n"); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* retrieve a record */ + len = sprintf(buf, "%08d", i); + key.dptr = buf; + key.dsize = len; + val = dbm_fetch(db, key); + if(!val.dptr){ + fprintf(stderr, "dbm_fetch failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + dbm_close(db); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + + +/* END OF FILE */ diff --git a/tcejdb/bros/sqltest.c b/tcejdb/bros/sqltest.c new file mode 100644 index 0000000..a6624d3 --- /dev/null +++ b/tcejdb/bros/sqltest.c @@ -0,0 +1,404 @@ +#include +#include +#include +#include +#include + +#undef TRUE +#define TRUE 1 /* boolean true */ +#undef FALSE +#define FALSE 0 /* boolean false */ + +#define RECBUFSIZ 256 /* buffer for records */ + + +/* global variables */ +const char *progname; /* program name */ +int showprgr; /* whether to show progression */ + + +/* function prototypes */ +int main(int argc, char **argv); +void usage(void); +int runwrite(int argc, char **argv); +int runread(int argc, char **argv); +int runtblwrite(int argc, char **argv); +int runtblread(int argc, char **argv); +int myrand(void); +int callback(void *opq, int argc, char **argv, char **colname); +int dowrite(char *name, int rnum); +int doread(char *name, int rnum); +int dotblwrite(char *name, int rnum); +int dotblread(char *name, int rnum); + + +/* main routine */ +int main(int argc, char **argv){ + int rv; + progname = argv[0]; + showprgr = TRUE; + if(getenv("HIDEPRGR")) showprgr = FALSE; + if(argc < 2) usage(); + rv = 0; + if(!strcmp(argv[1], "write")){ + rv = runwrite(argc, argv); + } else if(!strcmp(argv[1], "read")){ + rv = runread(argc, argv); + } else if(!strcmp(argv[1], "tblwrite")){ + rv = runtblwrite(argc, argv); + } else if(!strcmp(argv[1], "tblread")){ + rv = runtblread(argc, argv); + } else { + usage(); + } + return rv; +} + + +/* print the usage and exit */ +void usage(void){ + fprintf(stderr, "%s: test cases for SQLite\n", progname); + fprintf(stderr, "\n"); + fprintf(stderr, "usage:\n"); + fprintf(stderr, " %s write name rnum\n", progname); + fprintf(stderr, " %s read name rnum\n", progname); + fprintf(stderr, " %s tblwrite name rnum\n", progname); + fprintf(stderr, " %s tblread name rnum\n", progname); + fprintf(stderr, "\n"); + exit(1); +} + + +/* parse arguments of write command */ +int runwrite(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dowrite(name, rnum); + return rv; +} + + +/* parse arguments of read command */ +int runread(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = doread(name, rnum); + return rv; +} + + +/* parse arguments of tblwrite command */ +int runtblwrite(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dotblwrite(name, rnum); + return rv; +} + + +/* parse arguments of tblread command */ +int runtblread(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dotblread(name, rnum); + return rv; +} + + +/* pseudo random number generator */ +int myrand(void){ + static int cnt = 0; + return (lrand48() + cnt++) & 0x7FFFFFFF; +} + + +/* call back function for select statement */ +int callback(void *opq, int argc, char **argv, char **colname){ + return 0; +} + + +/* perform write command */ +int dowrite(char *name, int rnum){ + sqlite3 *db; + char sql[RECBUFSIZ], *errmsg; + int i, err; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + unlink(name); + if(sqlite3_open(name, &db) != 0){ + fprintf(stderr, "sqlite3_open failed\n"); + return 1; + } + sprintf(sql, "CREATE TABLE tbl ( key TEXT PRIMARY KEY, val TEXT );"); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + } + sprintf(sql, "PRAGMA cache_size = %d;", rnum); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + } + sprintf(sql, "BEGIN TRANSACTION;"); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + sprintf(sql, "INSERT INTO tbl VALUES ( '%08d', '%08d' );", i, i); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + err = TRUE; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + sprintf(sql, "END TRANSACTION;"); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + } + sqlite3_close(db); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform read command */ +int doread(char *name, int rnum){ + sqlite3 *db; + char sql[RECBUFSIZ], *errmsg; + int i, err; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(sqlite3_open(name, &db) != 0){ + fprintf(stderr, "sqlite3_open failed\n"); + return 1; + } + sprintf(sql, "PRAGMA cache_size = %d;", rnum); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + sprintf(sql, "SELECT * FROM tbl WHERE key = '%08d';", i); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + err = TRUE; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + sqlite3_close(db); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform tblwrite command */ +int dotblwrite(char *name, int rnum){ + sqlite3 *db; + char sql[RECBUFSIZ], *errmsg; + int i, err; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + unlink(name); + if(sqlite3_open(name, &db) != 0){ + fprintf(stderr, "sqlite3_open failed\n"); + return 1; + } + sprintf(sql, "CREATE TABLE tbl ( key INTEGER PRIMARY KEY, s TEXT, n INTEGER," + " t TEXT, f TEXT );"); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + } + sprintf(sql, "CREATE INDEX tbl_s ON tbl ( s );"); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + } + sprintf(sql, "CREATE INDEX tbl_n ON tbl ( n );"); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + } + sprintf(sql, "PRAGMA cache_size = %d;", rnum); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + } + sprintf(sql, "BEGIN TRANSACTION;"); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + sprintf(sql, "INSERT INTO tbl VALUES ( %d, '%08d', %d, '%08d', '%08d' );", + i, i, myrand() % i, i, myrand() % rnum); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + sprintf(sql, "END TRANSACTION;"); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + } + sqlite3_close(db); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform tblread command */ +int dotblread(char *name, int rnum){ + sqlite3 *db; + char sql[RECBUFSIZ], *errmsg; + int i, err; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(sqlite3_open(name, &db) != 0){ + fprintf(stderr, "sqlite3_open failed\n"); + return 1; + } + sprintf(sql, "PRAGMA cache_size = %d;", rnum); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + sprintf(sql, "SELECT * FROM tbl WHERE key = '%08d';", i); + if(sqlite3_exec(db, sql, callback, 0, &errmsg) != SQLITE_OK){ + fprintf(stderr, "sqlite3_exec failed: %s\n", errmsg); + sqlite3_free(errmsg); + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + sqlite3_close(db); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + + +/* END OF FILE */ diff --git a/tcejdb/bros/tctest.c b/tcejdb/bros/tctest.c new file mode 100644 index 0000000..b09e566 --- /dev/null +++ b/tcejdb/bros/tctest.c @@ -0,0 +1,748 @@ +/************************************************************************************************* + * Writing test of Tokyo Cabinet + *************************************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef TRUE +#define TRUE 1 /* boolean true */ +#undef FALSE +#define FALSE 0 /* boolean false */ + +#define RECBUFSIZ 32 /* buffer for records */ + + +/* global variables */ +const char *progname; /* program name */ +int showprgr; /* whether to show progression */ + + +/* function prototypes */ +int main(int argc, char **argv); +void usage(void); +int runwrite(int argc, char **argv); +int runread(int argc, char **argv); +int runbtwrite(int argc, char **argv); +int runbtread(int argc, char **argv); +int runflwrite(int argc, char **argv); +int runflread(int argc, char **argv); +int runtblwrite(int argc, char **argv); +int runtblread(int argc, char **argv); +int myrand(void); +int dowrite(char *name, int rnum); +int doread(char *name, int rnum); +int dobtwrite(char *name, int rnum, int rnd); +int dobtread(char *name, int rnum, int rnd); +int doflwrite(char *name, int rnum); +int doflread(char *name, int rnum); +int dotblwrite(char *name, int rnum); +int dotblread(char *name, int rnum); + + +/* main routine */ +int main(int argc, char **argv){ + int rv; + progname = argv[0]; + showprgr = TRUE; + if(getenv("HIDEPRGR")) showprgr = FALSE; + srand48(1978); + if(argc < 2) usage(); + rv = 0; + if(!strcmp(argv[1], "write")){ + rv = runwrite(argc, argv); + } else if(!strcmp(argv[1], "read")){ + rv = runread(argc, argv); + } else if(!strcmp(argv[1], "btwrite")){ + rv = runbtwrite(argc, argv); + } else if(!strcmp(argv[1], "btread")){ + rv = runbtread(argc, argv); + } else if(!strcmp(argv[1], "flwrite")){ + rv = runflwrite(argc, argv); + } else if(!strcmp(argv[1], "flread")){ + rv = runflread(argc, argv); + } else if(!strcmp(argv[1], "tblwrite")){ + rv = runtblwrite(argc, argv); + } else if(!strcmp(argv[1], "tblread")){ + rv = runtblread(argc, argv); + } else { + usage(); + } + return rv; +} + + +/* print the usage and exit */ +void usage(void){ + fprintf(stderr, "%s: test cases for Tokyo Cabinet\n", progname); + fprintf(stderr, "\n"); + fprintf(stderr, "usage:\n"); + fprintf(stderr, " %s write name rnum\n", progname); + fprintf(stderr, " %s read name rnum\n", progname); + fprintf(stderr, " %s btwrite [-rnd] name rnum\n", progname); + fprintf(stderr, " %s btread [-rnd] name rnum\n", progname); + fprintf(stderr, " %s flwrite name rnum\n", progname); + fprintf(stderr, " %s flread name rnum\n", progname); + fprintf(stderr, " %s tblwrite name rnum\n", progname); + fprintf(stderr, " %s tblread name rnum\n", progname); + fprintf(stderr, "\n"); + exit(1); +} + + +/* parse arguments of write command */ +int runwrite(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dowrite(name, rnum); + return rv; +} + + +/* parse arguments of read command */ +int runread(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = doread(name, rnum); + return rv; +} + + +/* parse arguments of btwrite command */ +int runbtwrite(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rnd, rv; + name = NULL; + rstr = NULL; + rnum = 0; + rnd = FALSE; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + if(!name && !strcmp(argv[i], "-rnd")){ + rnd = TRUE; + } else { + usage(); + } + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dobtwrite(name, rnum, rnd); + return rv; +} + + +/* parse arguments of btread command */ +int runbtread(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rnd, rv; + name = NULL; + rstr = NULL; + rnum = 0; + rnd = FALSE; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + if(!name && !strcmp(argv[i], "-rnd")){ + rnd = TRUE; + } else { + usage(); + } + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dobtread(name, rnum, rnd); + return rv; +} + + +/* parse arguments of flwrite command */ +int runflwrite(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = doflwrite(name, rnum); + return rv; +} + + +/* parse arguments of read command */ +int runflread(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = doflread(name, rnum); + return rv; +} + + +/* parse arguments of tblwrite command */ +int runtblwrite(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rnd, rv; + name = NULL; + rstr = NULL; + rnum = 0; + rnd = FALSE; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dotblwrite(name, rnum); + return rv; +} + + +/* parse arguments of tblread command */ +int runtblread(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rnd, rv; + name = NULL; + rstr = NULL; + rnum = 0; + rnd = FALSE; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dotblread(name, rnum); + return rv; +} + + +/* pseudo random number generator */ +int myrand(void){ + static int cnt = 0; + return (lrand48() + cnt++) & 0x7FFFFFFF; +} + + +/* perform write command */ +int dowrite(char *name, int rnum){ + TCHDB *hdb; + int i, err, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + hdb = tchdbnew(); + tchdbtune(hdb, rnum * 3, 0, 0, 0); + tchdbsetxmsiz(hdb, rnum * 48); + if(!tchdbopen(hdb, name, HDBOWRITER | HDBOCREAT | HDBOTRUNC)){ + fprintf(stderr, "tchdbopen failed\n"); + tchdbdel(hdb); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + len = sprintf(buf, "%08d", i); + if(!tchdbputasync(hdb, buf, len, buf, len)){ + fprintf(stderr, "tchdbputasync failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(!tchdbclose(hdb)){ + fprintf(stderr, "tchdbclose failed\n"); + tchdbdel(hdb); + return 1; + } + tchdbdel(hdb); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform read command */ +int doread(char *name, int rnum){ + TCHDB *hdb; + int i, err, len; + char buf[RECBUFSIZ], vbuf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + hdb = tchdbnew(); + tchdbsetxmsiz(hdb, rnum * 48); + if(!tchdbopen(hdb, name, HDBOREADER)){ + fprintf(stderr, "tchdbopen failed\n"); + tchdbdel(hdb); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + len = sprintf(buf, "%08d", i); + if(tchdbget3(hdb, buf, len, vbuf, RECBUFSIZ) == -1){ + fprintf(stderr, "tchdbget3 failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(!tchdbclose(hdb)){ + fprintf(stderr, "tchdbclose failed\n"); + tchdbdel(hdb); + return 1; + } + tchdbdel(hdb); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform btwrite command */ +int dobtwrite(char *name, int rnum, int rnd){ + TCBDB *bdb; + int i, err, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + bdb = tcbdbnew(); + if(rnd){ + tcbdbtune(bdb, 77, 256, -1, 0, 0, 0); + tcbdbsetcache(bdb, rnum / 77, -1); + } else { + tcbdbtune(bdb, 101, 256, -1, 0, 0, 0); + tcbdbsetcache(bdb, 256, 256); + } + tcbdbsetxmsiz(bdb, rnum * 32); + if(!tcbdbopen(bdb, name, BDBOWRITER | BDBOCREAT | BDBOTRUNC)){ + fprintf(stderr, "tcbdbopen failed\n"); + tcbdbdel(bdb); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + len = sprintf(buf, "%08d", rnd ? myrand() % rnum + 1 : i); + if(!tcbdbput(bdb, buf, len, buf, len)){ + fprintf(stderr, "tcbdbput failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(!tcbdbclose(bdb)){ + fprintf(stderr, "tcbdbclose failed\n"); + tcbdbdel(bdb); + return 1; + } + tcbdbdel(bdb); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform btread command */ +int dobtread(char *name, int rnum, int rnd){ + TCBDB *bdb; + int i, err, len, vlen; + const char *val; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + bdb = tcbdbnew(); + if(rnd){ + tcbdbsetcache(bdb, rnum / 77 + 1, -1); + } else { + tcbdbsetcache(bdb, 256, 256); + } + tcbdbsetxmsiz(bdb, rnum * 32); + if(!tcbdbopen(bdb, name, BDBOREADER)){ + fprintf(stderr, "tcbdbopen failed\n"); + tcbdbdel(bdb); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + len = sprintf(buf, "%08d", rnd ? myrand() % rnum + 1 : i); + if(!(val = tcbdbget3(bdb, buf, len, &vlen))){ + fprintf(stderr, "tdbdbget3 failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(!tcbdbclose(bdb)){ + fprintf(stderr, "tcbdbclose failed\n"); + tcbdbdel(bdb); + return 1; + } + tcbdbdel(bdb); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform flwrite command */ +int doflwrite(char *name, int rnum){ + TCFDB *fdb; + int i, err, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + fdb = tcfdbnew(); + tcfdbtune(fdb, 8, 1024 + rnum * 9); + if(!tcfdbopen(fdb, name, FDBOWRITER | FDBOCREAT | FDBOTRUNC)){ + fprintf(stderr, "tcfdbopen failed\n"); + tcfdbdel(fdb); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + len = sprintf(buf, "%08d", i); + if(!tcfdbput(fdb, i, buf, len)){ + fprintf(stderr, "tcfdbput failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(!tcfdbclose(fdb)){ + fprintf(stderr, "tcfdbclose failed\n"); + tcfdbdel(fdb); + return 1; + } + tcfdbdel(fdb); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform flread command */ +int doflread(char *name, int rnum){ + TCFDB *fdb; + int i, err; + char vbuf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + fdb = tcfdbnew(); + if(!tcfdbopen(fdb, name, FDBOREADER)){ + fprintf(stderr, "tcfdbopen failed\n"); + tcfdbdel(fdb); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + if(tcfdbget4(fdb, i, vbuf, RECBUFSIZ) == -1){ + fprintf(stderr, "tcfdbget4 failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(!tcfdbclose(fdb)){ + fprintf(stderr, "tcfdbclose failed\n"); + tcfdbdel(fdb); + return 1; + } + tcfdbdel(fdb); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform tblwrite command */ +int dotblwrite(char *name, int rnum){ + TCTDB *tdb; + int i, err, pksiz, vsiz; + char pkbuf[RECBUFSIZ], vbuf[RECBUFSIZ]; + TCMAP *cols; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + tdb = tctdbnew(); + tctdbtune(tdb, rnum * 3, 0, 0, 0); + tctdbsetxmsiz(tdb, rnum * 80); + tctdbsetcache(tdb, -1, rnum / 100, -1); + if(!tctdbopen(tdb, name, TDBOWRITER | TDBOCREAT | TDBOTRUNC)){ + fprintf(stderr, "tctdbopen failed\n"); + tctdbdel(tdb); + return 1; + } + if(!tctdbsetindex(tdb, "s", TDBITLEXICAL)){ + fprintf(stderr, "tctdbsetindex failed\n"); + tctdbdel(tdb); + return 1; + } + if(!tctdbsetindex(tdb, "n", TDBITDECIMAL)){ + fprintf(stderr, "tctdbsetindex failed\n"); + tctdbdel(tdb); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* store a record */ + pksiz = sprintf(pkbuf, "%d", i); + cols = tcmapnew2(7); + vsiz = sprintf(vbuf, "%08d", i); + tcmapput(cols, "s", 1, vbuf, vsiz); + vsiz = sprintf(vbuf, "%08d", myrand() % i); + tcmapput(cols, "n", 1, vbuf, vsiz); + vsiz = sprintf(vbuf, "%08d", i); + tcmapput(cols, "t", 1, vbuf, vsiz); + vsiz = sprintf(vbuf, "%08d", myrand() % rnum); + tcmapput(cols, "f", 1, vbuf, vsiz); + if(!tctdbput(tdb, pkbuf, pksiz, cols)){ + fprintf(stderr, "tctdbput failed\n"); + err = TRUE; + break; + } + tcmapdel(cols); + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(!tctdbclose(tdb)){ + fprintf(stderr, "tctdbclose failed\n"); + tctdbdel(tdb); + return 1; + } + tctdbdel(tdb); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform tblread command */ +int dotblread(char *name, int rnum){ + TCTDB *tdb; + int i, j, err, pksiz, rsiz; + char pkbuf[RECBUFSIZ]; + const char *rbuf; + TCMAP *cols; + TDBQRY *qry; + TCLIST *res; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + tdb = tctdbnew(); + tctdbsetxmsiz(tdb, rnum * 80); + tctdbsetcache(tdb, -1, rnum / 100, -1); + if(!tctdbopen(tdb, name, TDBOREADER)){ + fprintf(stderr, "tctdbopen failed\n"); + tctdbdel(tdb); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + /* search for a record */ + pksiz = sprintf(pkbuf, "%08d", i); + qry = tctdbqrynew(tdb); + tctdbqryaddcond(qry, "s", TDBQCSTREQ, pkbuf); + res = tctdbqrysearch(qry); + for(j = 0; j < tclistnum(res); j++){ + rbuf = tclistval(res, j, &rsiz); + cols = tctdbget(tdb, rbuf, rsiz); + if(cols){ + tcmapdel(cols); + } else { + fprintf(stderr, "tctdbget failed\n"); + err = TRUE; + break; + } + } + tclistdel(res); + tctdbqrydel(qry); + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + if(!tctdbclose(tdb)){ + fprintf(stderr, "tctdbclose failed\n"); + tctdbdel(tdb); + return 1; + } + tctdbdel(tdb); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + + +/* END OF FILE */ diff --git a/tcejdb/bros/tdbtest.c b/tcejdb/bros/tdbtest.c new file mode 100644 index 0000000..49d6ae4 --- /dev/null +++ b/tcejdb/bros/tdbtest.c @@ -0,0 +1,205 @@ +/************************************************************************************************* + * Writing test of Trivial Database + *************************************************************************************************/ + + +#include +#include +#include +#include +#include +#include + +#undef TRUE +#define TRUE 1 /* boolean true */ +#undef FALSE +#define FALSE 0 /* boolean false */ + +#define RECBUFSIZ 32 /* buffer for records */ + + +/* global variables */ +const char *progname; /* program name */ +int showprgr; /* whether to show progression */ + + +/* function prototypes */ +int main(int argc, char **argv); +void usage(void); +int runwrite(int argc, char **argv); +int runread(int argc, char **argv); +int dowrite(char *name, int rnum); +int doread(char *name, int rnum); + + +/* main routine */ +int main(int argc, char **argv){ + int rv; + progname = argv[0]; + showprgr = TRUE; + if(getenv("HIDEPRGR")) showprgr = FALSE; + if(argc < 2) usage(); + rv = 0; + if(!strcmp(argv[1], "write")){ + rv = runwrite(argc, argv); + } else if(!strcmp(argv[1], "read")){ + rv = runread(argc, argv); + } else { + usage(); + } + return rv; +} + + +/* print the usage and exit */ +void usage(void){ + fprintf(stderr, "%s: test cases for Trivial Database\n", progname); + fprintf(stderr, "\n"); + fprintf(stderr, "usage:\n"); + fprintf(stderr, " %s write name rnum\n", progname); + fprintf(stderr, " %s read name rnum\n", progname); + fprintf(stderr, "\n"); + exit(1); +} + + +/* parse arguments of write command */ +int runwrite(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = dowrite(name, rnum); + return rv; +} + + +/* parse arguments of read command */ +int runread(int argc, char **argv){ + char *name, *rstr; + int i, rnum, rv; + name = NULL; + rstr = NULL; + rnum = 0; + for(i = 2; i < argc; i++){ + if(!name && argv[i][0] == '-'){ + usage(); + } else if(!name){ + name = argv[i]; + } else if(!rstr){ + rstr = argv[i]; + } else { + usage(); + } + } + if(!name || !rstr) usage(); + rnum = atoi(rstr); + if(rnum < 1) usage(); + rv = doread(name, rnum); + return rv; +} + + +/* perform write command */ +int dowrite(char *name, int rnum){ + TDB_CONTEXT *tdb; + TDB_DATA key, record; + int i, err, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(!(tdb = tdb_open(name, rnum * 2, 0, O_RDWR | O_CREAT | O_TRUNC, 00644))){ + fprintf(stderr, "tdb_open failed\n"); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + len = sprintf(buf, "%08d", i); + key.dptr = (unsigned char *)buf; + key.dsize = len; + record.dptr = (unsigned char *)buf; + record.dsize = len; + /* store a record */ + if(tdb_store(tdb, key, record, TDB_REPLACE) != 0){ + fprintf(stderr, "tdb_store failed\n"); + err = TRUE; + break; + } + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + tdb_close(tdb); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + +/* perform read command */ +int doread(char *name, int rnum){ + TDB_CONTEXT *tdb; + TDB_DATA key, record; + int i, err, len; + char buf[RECBUFSIZ]; + if(showprgr) printf("\n name=%s rnum=%d\n\n", name, rnum); + /* open a database */ + if(!(tdb = tdb_open(name, rnum * 2, 0, O_RDONLY, 00644))){ + fprintf(stderr, "tdb_open failed\n"); + return 1; + } + err = FALSE; + /* loop for each record */ + for(i = 1; i <= rnum; i++){ + len = sprintf(buf, "%08d", i); + key.dptr = (unsigned char *)buf; + key.dsize = len; + /* retrieve a record */ + record = tdb_fetch(tdb, key); + if(!record.dptr){ + fprintf(stderr, "tdb_fetch failed\n"); + err = TRUE; + break; + } + free(record.dptr); + /* print progression */ + if(showprgr && rnum > 250 && i % (rnum / 250) == 0){ + putchar('.'); + fflush(stdout); + if(i == rnum || i % (rnum / 10) == 0){ + printf(" (%08d)\n", i); + fflush(stdout); + } + } + } + /* close the database */ + tdb_close(tdb); + if(showprgr && !err) printf("ok\n\n"); + return err ? 1 : 0; +} + + + +/* END OF FILE */ diff --git a/tcejdb/bson.c b/tcejdb/bson.c new file mode 100644 index 0000000..96343a4 --- /dev/null +++ b/tcejdb/bson.c @@ -0,0 +1,1359 @@ +/* bson.c */ + +/* Copyright 2009, 2010 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include "bson.h" +#include "encoding.h" +#include "myconf.h" + +const int initialBufferSize = 128; + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +/* only need one of these */ +static const int zero = 0; + +/* Custom standard function pointers. */ +void *(*bson_malloc_func)(size_t) = MYMALLOC; +void *(*bson_realloc_func)(void *, size_t) = MYREALLOC; +void ( *bson_free_func)(void *) = MYFREE; +#ifdef R_SAFETY_NET +bson_printf_func bson_printf; +#else +bson_printf_func bson_printf = printf; +#endif +bson_fprintf_func bson_fprintf = fprintf; +bson_sprintf_func bson_sprintf = sprintf; + +static int _bson_errprintf(const char *, ...); +bson_printf_func bson_errprintf = _bson_errprintf; + +/* ObjectId fuzz functions. */ +static int ( *oid_fuzz_func)(void) = NULL; +static int ( *oid_inc_func)(void) = NULL; + +/* ---------------------------- + READING + ------------------------------ */ + +EJDB_EXPORT bson* bson_create(void) { + return (bson*) bson_malloc(sizeof (bson)); +} + +EJDB_EXPORT void bson_dispose(bson* b) { + bson_free(b); +} + +EJDB_EXPORT bson *bson_empty(bson *obj) { + static char *data = "\005\0\0\0\0"; + bson_init_data(obj, data); + obj->finished = 1; + obj->err = 0; + obj->errstr = NULL; + obj->stackPos = 0; + obj->flags = 0; + return obj; +} + +EJDB_EXPORT int bson_copy(bson *out, const bson *in) { + if (!out || !in) return BSON_ERROR; + if (!in->finished) return BSON_ERROR; + bson_init_size(out, bson_size(in)); + memcpy(out->data, in->data, bson_size(in)); + out->finished = 1; + + return BSON_OK; +} + +int bson_init_data(bson *b, char *data) { + b->data = data; + return BSON_OK; +} + +int bson_init_finished_data(bson *b, char *data) { + bson_init_data(b, data); + b->finished = 1; + return BSON_OK; +} + +static void _bson_reset(bson *b) { + b->finished = 0; + b->stackPos = 0; + b->err = 0; + b->errstr = NULL; + b->flags = 0; +} + +EJDB_EXPORT int bson_size(const bson *b) { + int i; + if (!b || !b->data) + return 0; + bson_little_endian32(&i, b->data); + return i; +} + +EJDB_EXPORT int bson_buffer_size(const bson *b) { + return (b->cur - b->data + 1); +} + +EJDB_EXPORT const char *bson_data(const bson *b) { + return (const char *) b->data; +} + +static char hexbyte(char hex) { + if (hex >= '0' && hex <= '9') + return (hex - '0'); + else if (hex >= 'A' && hex <= 'F') + return (hex - 'A' + 10); + else if (hex >= 'a' && hex <= 'f') + return (hex - 'a' + 10); + else + return 0x0; +} + +EJDB_EXPORT void bson_oid_from_string(bson_oid_t *oid, const char *str) { + int i; + for (i = 0; i < 12; i++) { + oid->bytes[i] = (hexbyte(str[2 * i]) << 4) | hexbyte(str[2 * i + 1]); + } +} + +EJDB_EXPORT void bson_oid_to_string(const bson_oid_t *oid, char *str) { + static const char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + int i; + for (i = 0; i < 12; i++) { + str[2 * i] = hex[(oid->bytes[i] & 0xf0) >> 4]; + str[2 * i + 1] = hex[ oid->bytes[i] & 0x0f ]; + } + str[24] = '\0'; +} + +EJDB_EXPORT void bson_set_oid_fuzz(int ( *func)(void)) { + oid_fuzz_func = func; +} + +EJDB_EXPORT void bson_set_oid_inc(int ( *func)(void)) { + oid_inc_func = func; +} + +EJDB_EXPORT void bson_oid_gen(bson_oid_t *oid) { + static int incr = 0; + static int fuzz = 0; + int i; + int t = time(NULL); + + if (oid_inc_func) + i = oid_inc_func(); + else + i = incr++; + + if (!fuzz) { + if (oid_fuzz_func) + fuzz = oid_fuzz_func(); + else { + srand(t); + fuzz = rand(); + } + } + + bson_big_endian32(&oid->ints[0], &t); + oid->ints[1] = fuzz; + bson_big_endian32(&oid->ints[2], &i); +} + +EJDB_EXPORT time_t bson_oid_generated_time(bson_oid_t *oid) { + time_t out; + bson_big_endian32(&out, &oid->ints[0]); + + return out; +} + +EJDB_EXPORT void bson_print(FILE *f, const bson *b) { + bson_print_raw(f, b->data, 0); +} + +EJDB_EXPORT void bson_print_raw(FILE *f, const char *data, int depth) { + bson_iterator i; + const char *key; + int temp; + bson_timestamp_t ts; + char oidhex[25]; + bson scope; + bson_iterator_from_buffer(&i, data); + + while (bson_iterator_next(&i)) { + bson_type t = bson_iterator_type(&i); + if (t == 0) + break; + key = bson_iterator_key(&i); + + for (temp = 0; temp <= depth; temp++) + bson_fprintf(f, "\t"); + bson_fprintf(f, "%s : %d \t ", key, t); + switch (t) { + case BSON_DOUBLE: + bson_fprintf(f, "%f", bson_iterator_double(&i)); + break; + case BSON_STRING: + bson_fprintf(f, "%s", bson_iterator_string(&i)); + break; + case BSON_SYMBOL: + bson_fprintf(f, "SYMBOL: %s", bson_iterator_string(&i)); + break; + case BSON_OID: + bson_oid_to_string(bson_iterator_oid(&i), oidhex); + bson_fprintf(f, "%s", oidhex); + break; + case BSON_BOOL: + bson_fprintf(f, "%s", bson_iterator_bool(&i) ? "true" : "false"); + break; + case BSON_DATE: + bson_fprintf(f, "%ld", (long int) bson_iterator_date(&i)); + break; + case BSON_BINDATA: + bson_fprintf(f, "BSON_BINDATA"); + break; + case BSON_UNDEFINED: + bson_fprintf(f, "BSON_UNDEFINED"); + break; + case BSON_NULL: + bson_fprintf(f, "BSON_NULL"); + break; + case BSON_REGEX: + bson_fprintf(f, "BSON_REGEX: %s", bson_iterator_regex(&i)); + break; + case BSON_CODE: + bson_fprintf(f, "BSON_CODE: %s", bson_iterator_code(&i)); + break; + case BSON_CODEWSCOPE: + bson_fprintf(f, "BSON_CODE_W_SCOPE: %s", bson_iterator_code(&i)); + /* bson_init( &scope ); */ /* review - stepped on by bson_iterator_code_scope? */ + bson_iterator_code_scope(&i, &scope); + bson_fprintf(f, "\n\t SCOPE: "); + bson_print(f, &scope); + /* bson_destroy( &scope ); */ /* review - causes free error */ + break; + case BSON_INT: + bson_fprintf(f, "%d", bson_iterator_int(&i)); + break; + case BSON_LONG: + bson_fprintf(f, "%lld", (uint64_t) bson_iterator_long(&i)); + break; + case BSON_TIMESTAMP: + ts = bson_iterator_timestamp(&i); + bson_fprintf(f, "i: %d, t: %d", ts.i, ts.t); + break; + case BSON_OBJECT: + case BSON_ARRAY: + bson_fprintf(f, "\n"); + bson_print_raw(f, bson_iterator_value(&i), depth + 1); + break; + default: + bson_errprintf("can't print type : %d\n", t); + } + bson_fprintf(f, "\n"); + } +} + +/* ---------------------------- + ITERATOR + ------------------------------ */ + +EJDB_EXPORT bson_iterator* bson_iterator_create(void) { + return (bson_iterator*) malloc(sizeof ( bson_iterator)); +} + +EJDB_EXPORT void bson_iterator_dispose(bson_iterator* i) { + free(i); +} + +EJDB_EXPORT void bson_iterator_init(bson_iterator *i, const bson *b) { + i->cur = b->data + 4; + i->first = 1; +} + +EJDB_EXPORT void bson_iterator_from_buffer(bson_iterator *i, const char *buffer) { + i->cur = buffer + 4; + i->first = 1; +} + +EJDB_EXPORT bson_type bson_find(bson_iterator *it, const bson *obj, const char *name) { + bson_iterator_init(it, (bson *) obj); + while (bson_iterator_next(it)) { + if (strcmp(name, bson_iterator_key(it)) == 0) + break; + } + return bson_iterator_type(it); +} + +EJDB_EXPORT bson_type bson_find_from_buffer(bson_iterator *it, const char *buffer, const char *name) { + bson_iterator_from_buffer(it, buffer); + while (bson_iterator_next(it)) { + if (strcmp(name, bson_iterator_key(it)) == 0) + break; + } + return bson_iterator_type(it); +} + +static bson_type bson_find_fieldpath_value_impl(char* pstack, int curr, const char *fpath, int fplen, bson_iterator *it) { + int i; + int klen = 0; + bson_type t; + while ((t = bson_iterator_next(it)) != BSON_EOO) { + const char* key = bson_iterator_key(it); + klen = strlen(key); + if (curr + klen > fplen || curr + klen + 1 >= BSON_MAX_FPATH_LEN) { + continue; + } + //PUSH + if (curr > 0) { //add leading dot + memset(pstack + curr, '.', 1); + curr++; + } + memcpy(pstack + curr, key, klen); + curr += klen; + for (i = 0; i < curr && i < fplen && pstack[i] == fpath[i]; ++i); + if (i == curr && i == fplen) { //Position matched with field path + return t; + } + if (i == curr && i < fplen && (t == BSON_OBJECT || t == BSON_ARRAY)) { //Only prefix and we can go into nested objects + bson_iterator sit; + bson_iterator_subiterator(it, &sit); + bson_type st = bson_find_fieldpath_value_impl(pstack, curr, fpath, fplen, &sit); + if (st != BSON_EOO) { //Found in nested + *it = sit; + return st; + } + } + //POP + curr -= klen; + if (curr > 0) { + curr--; //remove leading dot + } + } + return BSON_EOO; +} + +EJDB_EXPORT bson_type bson_find_fieldpath_value(const char *fpath, bson_iterator *it) { + return bson_find_fieldpath_value2(fpath, strlen(fpath), it); +} + +EJDB_EXPORT bson_type bson_find_fieldpath_value2(const char *fpath, int fplen, bson_iterator *it) { + if (fplen >= BSON_MAX_FPATH_LEN) { + return BSON_EOO; //give up + } + char pstack[BSON_MAX_FPATH_LEN]; + return bson_find_fieldpath_value_impl(pstack, 0, fpath, fplen, it); +} + +EJDB_EXPORT bson_bool_t bson_iterator_more(const bson_iterator *i) { + return *(i->cur); +} + +EJDB_EXPORT bson_type bson_iterator_next(bson_iterator *i) { + int ds; + + if (i->first) { + i->first = 0; + return (bson_type) (*i->cur); + } + + switch (bson_iterator_type(i)) { + case BSON_EOO: + return BSON_EOO; /* don't advance */ + case BSON_UNDEFINED: + case BSON_NULL: + ds = 0; + break; + case BSON_BOOL: + ds = 1; + break; + case BSON_INT: + ds = 4; + break; + case BSON_LONG: + case BSON_DOUBLE: + case BSON_TIMESTAMP: + case BSON_DATE: + ds = 8; + break; + case BSON_OID: + ds = 12; + break; + case BSON_STRING: + case BSON_SYMBOL: + case BSON_CODE: + ds = 4 + bson_iterator_int_raw(i); + break; + case BSON_BINDATA: + ds = 5 + bson_iterator_int_raw(i); + break; + case BSON_OBJECT: + case BSON_ARRAY: + case BSON_CODEWSCOPE: + ds = bson_iterator_int_raw(i); + break; + case BSON_DBREF: + ds = 4 + 12 + bson_iterator_int_raw(i); + break; + case BSON_REGEX: + { + const char *s = bson_iterator_value(i); + const char *p = s; + p += strlen(p) + 1; + p += strlen(p) + 1; + ds = p - s; + break; + } + + default: + { + char msg[] = "unknown type: 000000000000"; + bson_numstr(msg + 14, (unsigned) (i->cur[0])); + bson_fatal_msg(0, msg); + return 0; + } + } + + i->cur += 1 + strlen(i->cur + 1) + 1 + ds; + + return (bson_type) (*i->cur); +} + +EJDB_EXPORT bson_type bson_iterator_type(const bson_iterator *i) { + return (bson_type) i->cur[0]; +} + +EJDB_EXPORT const char *bson_iterator_key(const bson_iterator *i) { + return i->cur + 1; +} + +EJDB_EXPORT const char *bson_iterator_value(const bson_iterator *i) { + const char *t = i->cur + 1; + t += strlen(t) + 1; + return t; +} + +/* types */ + +int bson_iterator_int_raw(const bson_iterator *i) { + int out; + bson_little_endian32(&out, bson_iterator_value(i)); + return out; +} + +double bson_iterator_double_raw(const bson_iterator *i) { + double out; + bson_little_endian64(&out, bson_iterator_value(i)); + return out; +} + +int64_t bson_iterator_long_raw(const bson_iterator *i) { + int64_t out; + bson_little_endian64(&out, bson_iterator_value(i)); + return out; +} + +bson_bool_t bson_iterator_bool_raw(const bson_iterator *i) { + return bson_iterator_value(i)[0]; +} + +EJDB_EXPORT bson_oid_t *bson_iterator_oid(const bson_iterator *i) { + return (bson_oid_t *) bson_iterator_value(i); +} + +EJDB_EXPORT int bson_iterator_int(const bson_iterator *i) { + switch (bson_iterator_type(i)) { + case BSON_INT: + return bson_iterator_int_raw(i); + case BSON_LONG: + return bson_iterator_long_raw(i); + case BSON_DOUBLE: + return bson_iterator_double_raw(i); + default: + return 0; + } +} + +EJDB_EXPORT double bson_iterator_double(const bson_iterator *i) { + switch (bson_iterator_type(i)) { + case BSON_INT: + return bson_iterator_int_raw(i); + case BSON_LONG: + return bson_iterator_long_raw(i); + case BSON_DOUBLE: + return bson_iterator_double_raw(i); + default: + return 0; + } +} + +EJDB_EXPORT int64_t bson_iterator_long(const bson_iterator *i) { + switch (bson_iterator_type(i)) { + case BSON_INT: + return bson_iterator_int_raw(i); + case BSON_LONG: + return bson_iterator_long_raw(i); + case BSON_DOUBLE: + return bson_iterator_double_raw(i); + default: + return 0; + } +} + +static int64_t bson_iterator_long_ext(const bson_iterator *i) { + switch (bson_iterator_type(i)) { + case BSON_INT: + return bson_iterator_int_raw(i); + case BSON_LONG: + case BSON_DATE: + case BSON_TIMESTAMP: + return bson_iterator_long_raw(i); + case BSON_DOUBLE: + return bson_iterator_double_raw(i); + default: + return 0; + } +} + +EJDB_EXPORT bson_timestamp_t bson_iterator_timestamp(const bson_iterator *i) { + bson_timestamp_t ts; + bson_little_endian32(&(ts.i), bson_iterator_value(i)); + bson_little_endian32(&(ts.t), bson_iterator_value(i) + 4); + return ts; +} + +EJDB_EXPORT int bson_iterator_timestamp_time(const bson_iterator *i) { + int time; + bson_little_endian32(&time, bson_iterator_value(i) + 4); + return time; +} + +EJDB_EXPORT int bson_iterator_timestamp_increment(const bson_iterator *i) { + int increment; + bson_little_endian32(&increment, bson_iterator_value(i)); + return increment; +} + +EJDB_EXPORT bson_bool_t bson_iterator_bool(const bson_iterator *i) { + switch (bson_iterator_type(i)) { + case BSON_BOOL: + return bson_iterator_bool_raw(i); + case BSON_INT: + return bson_iterator_int_raw(i) != 0; + case BSON_LONG: + return bson_iterator_long_raw(i) != 0; + case BSON_DOUBLE: + return bson_iterator_double_raw(i) != 0; + case BSON_EOO: + case BSON_NULL: + case BSON_UNDEFINED: + return 0; + default: + return 1; + } +} + +EJDB_EXPORT const char *bson_iterator_string(const bson_iterator *i) { + switch (bson_iterator_type(i)) { + case BSON_STRING: + case BSON_SYMBOL: + return bson_iterator_value(i) + 4; + default: + return ""; + } +} + +int bson_iterator_string_len(const bson_iterator *i) { + return bson_iterator_int_raw(i); +} + +EJDB_EXPORT const char *bson_iterator_code(const bson_iterator *i) { + switch (bson_iterator_type(i)) { + case BSON_STRING: + case BSON_CODE: + return bson_iterator_value(i) + 4; + case BSON_CODEWSCOPE: + return bson_iterator_value(i) + 8; + default: + return NULL; + } +} + +EJDB_EXPORT void bson_iterator_code_scope(const bson_iterator *i, bson *scope) { + if (bson_iterator_type(i) == BSON_CODEWSCOPE) { + int code_len; + bson_little_endian32(&code_len, bson_iterator_value(i) + 4); + bson_init_data(scope, (void *) (bson_iterator_value(i) + 8 + code_len)); + _bson_reset(scope); + scope->finished = 1; + } else { + bson_empty(scope); + } +} + +EJDB_EXPORT bson_date_t bson_iterator_date(const bson_iterator *i) { + return bson_iterator_long_raw(i); +} + +EJDB_EXPORT time_t bson_iterator_time_t(const bson_iterator *i) { + return bson_iterator_date(i) / 1000; +} + +EJDB_EXPORT int bson_iterator_bin_len(const bson_iterator *i) { + return ( bson_iterator_bin_type(i) == BSON_BIN_BINARY_OLD) + ? bson_iterator_int_raw(i) - 4 + : bson_iterator_int_raw(i); +} + +EJDB_EXPORT char bson_iterator_bin_type(const bson_iterator *i) { + return bson_iterator_value(i)[4]; +} + +EJDB_EXPORT const char *bson_iterator_bin_data(const bson_iterator *i) { + return ( bson_iterator_bin_type(i) == BSON_BIN_BINARY_OLD) + ? bson_iterator_value(i) + 9 + : bson_iterator_value(i) + 5; +} + +EJDB_EXPORT const char *bson_iterator_regex(const bson_iterator *i) { + return bson_iterator_value(i); +} + +EJDB_EXPORT const char *bson_iterator_regex_opts(const bson_iterator *i) { + const char *p = bson_iterator_value(i); + return p + strlen(p) + 1; + +} + +EJDB_EXPORT void bson_iterator_subobject(const bson_iterator *i, bson *sub) { + bson_init_data(sub, (char *) bson_iterator_value(i)); + _bson_reset(sub); + sub->finished = 1; +} + +EJDB_EXPORT void bson_iterator_subiterator(const bson_iterator *i, bson_iterator *sub) { + bson_iterator_from_buffer(sub, bson_iterator_value(i)); +} + +/* ---------------------------- + BUILDING + ------------------------------ */ + +static void _bson_init_size(bson *b, int size) { + if (size == 0) + b->data = NULL; + else + b->data = (char *) bson_malloc(size); + b->dataSize = size; + b->cur = b->data + 4; + _bson_reset(b); +} + +EJDB_EXPORT void bson_init(bson *b) { + _bson_init_size(b, initialBufferSize); +} + +EJDB_EXPORT void bson_init_as_query(bson *b) { + bson_init(b); + b->flags |= BSON_FLAG_QUERY_MODE; +} + +void bson_init_size(bson *b, int size) { + _bson_init_size(b, size); +} + +void bson_append_byte(bson *b, char c) { + b->cur[0] = c; + b->cur++; +} + +EJDB_EXPORT void bson_append(bson *b, const void *data, int len) { + memcpy(b->cur, data, len); + b->cur += len; +} + +void bson_append32(bson *b, const void *data) { + bson_little_endian32(b->cur, data); + b->cur += 4; +} + +void bson_append64(bson *b, const void *data) { + bson_little_endian64(b->cur, data); + b->cur += 8; +} + +int bson_ensure_space(bson *b, const int bytesNeeded) { + int pos = b->cur - b->data; + char *orig = b->data; + int new_size; + + if (pos + bytesNeeded <= b->dataSize) + return BSON_OK; + + new_size = 1.5 * (b->dataSize + bytesNeeded); + + if (new_size < b->dataSize) { + if ((b->dataSize + bytesNeeded) < INT_MAX) + new_size = INT_MAX; + else { + b->err = BSON_SIZE_OVERFLOW; + return BSON_ERROR; + } + } + + b->data = bson_realloc(b->data, new_size); + if (!b->data) + bson_fatal_msg(!!b->data, "realloc() failed"); + + b->dataSize = new_size; + b->cur += b->data - orig; + + return BSON_OK; +} + +EJDB_EXPORT int bson_finish(bson *b) { + int i; + + if (b->err & BSON_NOT_UTF8) + return BSON_ERROR; + + if (!b->finished) { + if (bson_ensure_space(b, 1) == BSON_ERROR) return BSON_ERROR; + bson_append_byte(b, 0); + i = b->cur - b->data; + bson_little_endian32(b->data, &i); + b->finished = 1; + } + + return BSON_OK; +} + +EJDB_EXPORT void bson_destroy(bson *b) { + if (b) { + bson_free(b->data); + b->err = 0; + b->data = 0; + b->cur = 0; + b->finished = 1; + } +} + +EJDB_EXPORT void bson_del(bson *b) { + if (b) { + bson_destroy(b); + bson_free(b); + } +} + +static int bson_append_estart(bson *b, int type, const char *name, const int dataSize) { + const int len = strlen(name) + 1; + + if (b->finished) { + b->err |= BSON_ALREADY_FINISHED; + return BSON_ERROR; + } + + if (bson_ensure_space(b, 1 + len + dataSize) == BSON_ERROR) { + return BSON_ERROR; + } + + if (bson_check_field_name(b, (const char *) name, len - 1, + !(b->flags & BSON_FLAG_QUERY_MODE), !(b->flags & BSON_FLAG_QUERY_MODE)) == BSON_ERROR) { + bson_builder_error(b); + return BSON_ERROR; + } + + bson_append_byte(b, (char) type); + bson_append(b, name, len); + return BSON_OK; +} + +/* ---------------------------- + BUILDING TYPES + ------------------------------ */ + +EJDB_EXPORT int bson_append_int(bson *b, const char *name, const int i) { + if (bson_append_estart(b, BSON_INT, name, 4) == BSON_ERROR) + return BSON_ERROR; + bson_append32(b, &i); + return BSON_OK; +} + +EJDB_EXPORT int bson_append_long(bson *b, const char *name, const int64_t i) { + if (bson_append_estart(b, BSON_LONG, name, 8) == BSON_ERROR) + return BSON_ERROR; + bson_append64(b, &i); + return BSON_OK; +} + +EJDB_EXPORT int bson_append_double(bson *b, const char *name, const double d) { + if (bson_append_estart(b, BSON_DOUBLE, name, 8) == BSON_ERROR) + return BSON_ERROR; + bson_append64(b, &d); + return BSON_OK; +} + +EJDB_EXPORT int bson_append_bool(bson *b, const char *name, const bson_bool_t i) { + if (bson_append_estart(b, BSON_BOOL, name, 1) == BSON_ERROR) + return BSON_ERROR; + bson_append_byte(b, i != 0); + return BSON_OK; +} + +EJDB_EXPORT int bson_append_null(bson *b, const char *name) { + if (bson_append_estart(b, BSON_NULL, name, 0) == BSON_ERROR) + return BSON_ERROR; + return BSON_OK; +} + +EJDB_EXPORT int bson_append_undefined(bson *b, const char *name) { + if (bson_append_estart(b, BSON_UNDEFINED, name, 0) == BSON_ERROR) + return BSON_ERROR; + return BSON_OK; +} + +int bson_append_string_base(bson *b, const char *name, + const char *value, int len, bson_type type) { + + int sl = len + 1; + if (bson_check_string(b, (const char *) value, sl - 1) == BSON_ERROR) + return BSON_ERROR; + if (bson_append_estart(b, type, name, 4 + sl) == BSON_ERROR) { + return BSON_ERROR; + } + bson_append32(b, &sl); + bson_append(b, value, sl - 1); + bson_append(b, "\0", 1); + return BSON_OK; +} + +EJDB_EXPORT int bson_append_string(bson *b, const char *name, const char *value) { + return bson_append_string_base(b, name, value, strlen(value), BSON_STRING); +} + +EJDB_EXPORT int bson_append_symbol(bson *b, const char *name, const char *value) { + return bson_append_string_base(b, name, value, strlen(value), BSON_SYMBOL); +} + +EJDB_EXPORT int bson_append_code(bson *b, const char *name, const char *value) { + return bson_append_string_base(b, name, value, strlen(value), BSON_CODE); +} + +EJDB_EXPORT int bson_append_string_n(bson *b, const char *name, const char *value, int len) { + return bson_append_string_base(b, name, value, len, BSON_STRING); +} + +EJDB_EXPORT int bson_append_symbol_n(bson *b, const char *name, const char *value, int len) { + return bson_append_string_base(b, name, value, len, BSON_SYMBOL); +} + +EJDB_EXPORT int bson_append_code_n(bson *b, const char *name, const char *value, int len) { + return bson_append_string_base(b, name, value, len, BSON_CODE); +} + +EJDB_EXPORT int bson_append_code_w_scope_n(bson *b, const char *name, + const char *code, int len, const bson *scope) { + + int sl, size; + if (!scope) return BSON_ERROR; + sl = len + 1; + size = 4 + 4 + sl + bson_size(scope); + if (bson_append_estart(b, BSON_CODEWSCOPE, name, size) == BSON_ERROR) + return BSON_ERROR; + bson_append32(b, &size); + bson_append32(b, &sl); + bson_append(b, code, sl); + bson_append(b, scope->data, bson_size(scope)); + return BSON_OK; +} + +EJDB_EXPORT int bson_append_code_w_scope(bson *b, const char *name, const char *code, const bson *scope) { + return bson_append_code_w_scope_n(b, name, code, strlen(code), scope); +} + +EJDB_EXPORT int bson_append_binary(bson *b, const char *name, char type, const char *str, int len) { + if (type == BSON_BIN_BINARY_OLD) { + int subtwolen = len + 4; + if (bson_append_estart(b, BSON_BINDATA, name, 4 + 1 + 4 + len) == BSON_ERROR) + return BSON_ERROR; + bson_append32(b, &subtwolen); + bson_append_byte(b, type); + bson_append32(b, &len); + bson_append(b, str, len); + } else { + if (bson_append_estart(b, BSON_BINDATA, name, 4 + 1 + len) == BSON_ERROR) + return BSON_ERROR; + bson_append32(b, &len); + bson_append_byte(b, type); + bson_append(b, str, len); + } + return BSON_OK; +} + +EJDB_EXPORT int bson_append_oid(bson *b, const char *name, const bson_oid_t *oid) { + if (bson_append_estart(b, BSON_OID, name, 12) == BSON_ERROR) + return BSON_ERROR; + bson_append(b, oid, 12); + return BSON_OK; +} + +EJDB_EXPORT int bson_append_new_oid(bson *b, const char *name) { + bson_oid_t oid; + bson_oid_gen(&oid); + return bson_append_oid(b, name, &oid); +} + +EJDB_EXPORT int bson_append_regex(bson *b, const char *name, const char *pattern, const char *opts) { + const int plen = strlen(pattern) + 1; + const int olen = strlen(opts) + 1; + if (bson_append_estart(b, BSON_REGEX, name, plen + olen) == BSON_ERROR) + return BSON_ERROR; + if (bson_check_string(b, pattern, plen - 1) == BSON_ERROR) + return BSON_ERROR; + bson_append(b, pattern, plen); + bson_append(b, opts, olen); + return BSON_OK; +} + +EJDB_EXPORT int bson_append_bson(bson *b, const char *name, const bson *bson) { + if (!bson) return BSON_ERROR; + if (bson_append_estart(b, BSON_OBJECT, name, bson_size(bson)) == BSON_ERROR) + return BSON_ERROR; + bson_append(b, bson->data, bson_size(bson)); + return BSON_OK; +} + +EJDB_EXPORT int bson_append_element(bson *b, const char *name_or_null, const bson_iterator *elem) { + bson_iterator next = *elem; + int size; + + bson_iterator_next(&next); + size = next.cur - elem->cur; + + if (name_or_null == NULL) { + if (bson_ensure_space(b, size) == BSON_ERROR) + return BSON_ERROR; + bson_append(b, elem->cur, size); + } else { + int data_size = size - 2 - strlen(bson_iterator_key(elem)); + bson_append_estart(b, elem->cur[0], name_or_null, data_size); + bson_append(b, bson_iterator_value(elem), data_size); + } + + return BSON_OK; +} + +EJDB_EXPORT int bson_append_timestamp(bson *b, const char *name, bson_timestamp_t *ts) { + if (bson_append_estart(b, BSON_TIMESTAMP, name, 8) == BSON_ERROR) return BSON_ERROR; + + bson_append32(b, &(ts->i)); + bson_append32(b, &(ts->t)); + + return BSON_OK; +} + +EJDB_EXPORT int bson_append_timestamp2(bson *b, const char *name, int time, int increment) { + if (bson_append_estart(b, BSON_TIMESTAMP, name, 8) == BSON_ERROR) return BSON_ERROR; + + bson_append32(b, &increment); + bson_append32(b, &time); + return BSON_OK; +} + +EJDB_EXPORT int bson_append_date(bson *b, const char *name, bson_date_t millis) { + if (bson_append_estart(b, BSON_DATE, name, 8) == BSON_ERROR) return BSON_ERROR; + bson_append64(b, &millis); + return BSON_OK; +} + +EJDB_EXPORT int bson_append_time_t(bson *b, const char *name, time_t secs) { + return bson_append_date(b, name, (bson_date_t) secs * 1000); +} + +EJDB_EXPORT int bson_append_start_object(bson *b, const char *name) { + if (bson_append_estart(b, BSON_OBJECT, name, 5) == BSON_ERROR) return BSON_ERROR; + b->stack[ b->stackPos++ ] = b->cur - b->data; + bson_append32(b, &zero); + return BSON_OK; +} + +EJDB_EXPORT int bson_append_start_array(bson *b, const char *name) { + if (bson_append_estart(b, BSON_ARRAY, name, 5) == BSON_ERROR) return BSON_ERROR; + b->stack[ b->stackPos++ ] = b->cur - b->data; + bson_append32(b, &zero); + return BSON_OK; +} + +EJDB_EXPORT int bson_append_finish_object(bson *b) { + char *start; + int i; + if (bson_ensure_space(b, 1) == BSON_ERROR) return BSON_ERROR; + bson_append_byte(b, 0); + + start = b->data + b->stack[ --b->stackPos ]; + i = b->cur - start; + bson_little_endian32(start, &i); + + return BSON_OK; +} + +EJDB_EXPORT double bson_int64_to_double(int64_t i64) { + return (double) i64; +} + +EJDB_EXPORT int bson_append_finish_array(bson *b) { + return bson_append_finish_object(b); +} + +/* Error handling and allocators. */ + +static bson_err_handler err_handler = NULL; + +EJDB_EXPORT bson_err_handler set_bson_err_handler(bson_err_handler func) { + bson_err_handler old = err_handler; + err_handler = func; + return old; +} + +EJDB_EXPORT void bson_free(void *ptr) { + bson_free_func(ptr); +} + +EJDB_EXPORT void *bson_malloc(int size) { + void *p; + p = bson_malloc_func(size); + bson_fatal_msg(!!p, "malloc() failed"); + return p; +} + +void *bson_realloc(void *ptr, int size) { + void *p; + p = bson_realloc_func(ptr, size); + bson_fatal_msg(!!p, "realloc() failed"); + return p; +} + +int _bson_errprintf(const char *format, ...) { + va_list ap; + int ret; + va_start(ap, format); +#ifndef R_SAFETY_NET + ret = vfprintf(stderr, format, ap); +#endif + va_end(ap); + + return ret; +} + +/** + * This method is invoked when a non-fatal bson error is encountered. + * Calls the error handler if available. + * + * @param + */ +void bson_builder_error(bson *b) { + if (err_handler) + err_handler("BSON error."); +} + +void bson_fatal(int ok) { + bson_fatal_msg(ok, ""); +} + +void bson_fatal_msg(int ok, const char *msg) { + if (ok) + return; + + if (err_handler) { + err_handler(msg); + } +#ifndef R_SAFETY_NET + bson_errprintf("error: %s\n", msg); + exit(-5); +#endif +} + + +/* Efficiently copy an integer to a string. */ +extern const char bson_numstrs[1000][4]; + +EJDB_EXPORT void bson_numstr(char *str, long long int i) { + if (i < 1000) + memcpy(str, bson_numstrs[i], 4); + else + bson_sprintf(str, "%lld", i); +} + +EJDB_EXPORT int bson_numstrn(char *str, int maxbuf, long long int i) { + if (i < 1000 && maxbuf > 4) { + memcpy(str, bson_numstrs[i], 4); + return strlen(bson_numstrs[i]); + } else { + return snprintf(str, maxbuf, "%lld", i); + } +} + +EJDB_EXPORT void bson_swap_endian64(void *outp, const void *inp) { + const char *in = (const char *) inp; + char *out = (char *) outp; + + out[0] = in[7]; + out[1] = in[6]; + out[2] = in[5]; + out[3] = in[4]; + out[4] = in[3]; + out[5] = in[2]; + out[6] = in[1]; + out[7] = in[0]; + +} + +EJDB_EXPORT void bson_swap_endian32(void *outp, const void *inp) { + const char *in = (const char *) inp; + char *out = (char *) outp; + + out[0] = in[3]; + out[1] = in[2]; + out[2] = in[1]; + out[3] = in[0]; +} + +EJDB_EXPORT void bson_append_field_from_iterator(bson_iterator *from, bson *into) { + bson_type t = bson_iterator_type(from); + if (t == BSON_EOO) { + return; + } + const char* key = bson_iterator_key(from); + switch (t) { + case BSON_STRING: + case BSON_SYMBOL: + bson_append_string(into, key, bson_iterator_string(from)); + break; + case BSON_CODE: + bson_append_code(into, key, bson_iterator_code(from)); + break; + case BSON_INT: + bson_append_int(into, key, bson_iterator_int_raw(from)); + break; + case BSON_DOUBLE: + bson_append_double(into, key, bson_iterator_double_raw(from)); + break; + case BSON_LONG: + bson_append_long(into, key, bson_iterator_long_raw(from)); + break; + case BSON_UNDEFINED: + bson_append_undefined(into, key); + break; + case BSON_NULL: + bson_append_null(into, key); + break; + case BSON_BOOL: + bson_append_bool(into, key, bson_iterator_bool_raw(from)); + break; + case BSON_TIMESTAMP: + { + bson_timestamp_t ts = bson_iterator_timestamp(from); + bson_append_timestamp(into, key, &ts); + break; + } + case BSON_DATE: + bson_append_date(into, key, bson_iterator_date(from)); + break; + case BSON_REGEX: + bson_append_regex(into, key, bson_iterator_regex(from), bson_iterator_regex_opts(from)); + break; + case BSON_OID: + bson_append_oid(into, key, bson_iterator_oid(from)); + break; + case BSON_OBJECT: + { + bson_iterator sit; + bson_iterator_subiterator(from, &sit); + bson_append_start_object(into, key); + while (bson_iterator_next(&sit) != BSON_EOO) { + bson_append_field_from_iterator(&sit, into); + } + bson_append_finish_object(into); + break; + } + case BSON_ARRAY: + { + bson_iterator sit; + bson_iterator_subiterator(from, &sit); + bson_append_start_array(into, key); + while (bson_iterator_next(&sit) != BSON_EOO) { + bson_append_field_from_iterator(&sit, into); + } + bson_append_finish_array(into); + break; + } + case BSON_DBREF: + case BSON_CODEWSCOPE: + break; + default: + break; + } + +} + +EJDB_EXPORT int bson_merge(bson *b1, bson *b2, bson_bool_t overwrite, bson *out) { + assert(b1 && b2 && out); + if (!b1->finished || !b2->finished || out->finished) { + return BSON_ERROR; + } + + bson_iterator it1, it2; + bson_type bt1, bt2; + + bson_iterator_init(&it1, b1); + bson_iterator_init(&it2, b2); + + //Append all fields in B1 overwrited by B2 + while ((bt1 = bson_iterator_next(&it1)) != BSON_EOO) { + const char* k1 = bson_iterator_key(&it1); + if (overwrite && (bt2 = bson_find(&it2, b2, k1)) != BSON_EOO) { + bson_append_field_from_iterator(&it2, out); + } else { + bson_append_field_from_iterator(&it1, out); + } + } + + bson_iterator_init(&it1, b1); + bson_iterator_init(&it2, b2); + + //Append all fields from B2 missing in B1 + while ((bt2 = bson_iterator_next(&it2)) != BSON_EOO) { + const char* k2 = bson_iterator_key(&it2); + if ((bt1 = bson_find(&it1, b1, k2)) == BSON_EOO) { + bson_append_field_from_iterator(&it2, out); + } + } + + return BSON_OK; +} + +EJDB_EXPORT int bson_compare_fpaths(const void *bsdata1, const void *bsdata2, const char *fpath1, int fplen1, const char *fpath2, int fplen2) { + assert(bsdata1 && bsdata2 && fpath1 && fpath2); + bson_iterator it1, it2; + bson_type t1, t2; + bson_iterator_from_buffer(&it1, bsdata1); + bson_iterator_from_buffer(&it2, bsdata2); + t1 = bson_find_fieldpath_value2(fpath1, fplen1, &it1); + t2 = bson_find_fieldpath_value2(fpath2, fplen2, &it2); + if (t1 == BSON_BOOL || t1 == BSON_EOO || t1 == BSON_NULL || t1 == BSON_UNDEFINED) { + int v1 = bson_iterator_bool(&it1); + int v2 = bson_iterator_bool(&it2); + return (v1 > v2) ? 1 : ((v1 < v2) ? -1 : 0); + } else if (t1 == BSON_INT || t1 == BSON_LONG || t1 == BSON_DATE || t1 == BSON_TIMESTAMP) { + int64_t v1 = bson_iterator_long_ext(&it1); + int64_t v2 = bson_iterator_long_ext(&it2); + return (v1 > v2) ? 1 : ((v1 < v2) ? -1 : 0); + } else if (t1 == BSON_DOUBLE) { + double v1 = bson_iterator_double_raw(&it1); + double v2 = bson_iterator_double(&it2); + return (v1 > v2) ? 1 : ((v1 < v2) ? -1 : 0); + } else if (t1 == BSON_STRING || t1 == BSON_SYMBOL) { + const char* v1 = bson_iterator_string(&it1); + int l1 = bson_iterator_string_len(&it1); + const char* v2 = bson_iterator_string(&it2); + int l2 = (t2 == BSON_STRING || t2 == BSON_SYMBOL) ? bson_iterator_string_len(&it2) : strlen(v2); + int rv; + TCCMPLEXICAL(rv, v1, l1, v2, l2); + return rv; + } else if (t1 == BSON_BINDATA && t2 == BSON_BINDATA) { + int l1 = bson_iterator_bin_len(&it1); + int l2 = bson_iterator_bin_len(&it2); + return memcmp(bson_iterator_bin_data(&it1), bson_iterator_bin_data(&it2), MIN(l1, l2)); + } + return 0; +} + +EJDB_EXPORT int bson_compare(const void *bsdata1, const void *bsdata2, const char* fpath, int fplen) { + return bson_compare_fpaths(bsdata1, bsdata2, fpath, fplen, fpath, fplen); +} + +EJDB_EXPORT int bson_compare_string(const char *cv, const void *bsdata, const char *fpath) { + assert(cv && bsdata && fpath); + bson *bs1 = bson_create(); + bson_init(bs1); + bson_append_string(bs1, "$", cv); + bson_finish(bs1); + int res = bson_compare_fpaths(bson_data(bs1), bsdata, "$", 1, fpath, strlen(fpath)); + bson_del(bs1); + return res; +} + +EJDB_EXPORT int bson_compare_long(long cv, const void *bsdata, const char *fpath) { + bson *bs1 = bson_create(); + bson_init(bs1); + bson_append_long(bs1, "$", cv); + bson_finish(bs1); + int res = bson_compare_fpaths(bson_data(bs1), bsdata, "$", 1, fpath, strlen(fpath)); + bson_del(bs1); + return res; +} + +EJDB_EXPORT int bson_compare_double(double cv, const void *bsdata, const char *fpath) { + bson *bs1 = bson_create(); + bson_init(bs1); + bson_append_double(bs1, "$", cv); + bson_finish(bs1); + int res = bson_compare_fpaths(bson_data(bs1), bsdata, "$", 1, fpath, strlen(fpath)); + bson_del(bs1); + return res; +} + +EJDB_EXPORT int bson_compare_bool(bson_bool_t cv, const void *bsdata, const char *fpath) { + bson *bs1 = bson_create(); + bson_init(bs1); + bson_append_bool(bs1, "$", cv); + bson_finish(bs1); + int res = bson_compare_fpaths(bson_data(bs1), bsdata, "$", 1, fpath, strlen(fpath)); + bson_del(bs1); + return res; +} + +EJDB_EXPORT bson* bson_dup(const bson* src) { + assert(src); + bson *rv = bson_create(); + const char *raw = bson_data(src); + int sz = bson_size(src); + bson_init_size(rv, sz); + bson_ensure_space(rv, sz - 4); + bson_append(rv, raw + 4, sz - (4 + 1/*BSON_EOO*/)); + bson_finish(rv); + return rv; +} + +EJDB_EXPORT bson* bson_create_from_buffer(const void* buf, int bufsz) { + assert(buf); + assert(bufsz - 4 > 0); + bson *rv = bson_create(); + bson_init_size(rv, bufsz); + bson_ensure_space(rv, bufsz - 4); + bson_append(rv, buf + 4, bufsz - (4 + 1/*BSON_EOO*/)); + bson_finish(rv); + return rv; +} diff --git a/tcejdb/bson.h b/tcejdb/bson.h new file mode 100644 index 0000000..2b0fb30 --- /dev/null +++ b/tcejdb/bson.h @@ -0,0 +1,1089 @@ +/** + * @file bson.h + * @brief BSON Declarations + */ + +/* Copyright 2009-2012 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BSON_H_ +#define BSON_H_ + +#include "myconf.h" + +#ifdef EJDB_BIG_ENDIAN +#define bson_little_endian64(out, in) ( bson_swap_endian64(out, in) ) +#define bson_little_endian32(out, in) ( bson_swap_endian32(out, in) ) +#define bson_big_endian64(out, in) ( memcpy(out, in, 8) ) +#define bson_big_endian32(out, in) ( memcpy(out, in, 4) ) +#else +#define bson_little_endian64(out, in) ( memcpy(out, in, 8) ) +#define bson_little_endian32(out, in) ( memcpy(out, in, 4) ) +#define bson_big_endian64(out, in) ( bson_swap_endian64(out, in) ) +#define bson_big_endian32(out, in) ( bson_swap_endian32(out, in) ) +#endif + +#define BSON_IS_NUM_TYPE(atype) (atype == BSON_INT || atype == BSON_LONG || atype == BSON_DOUBLE) + +EJDB_EXTERN_C_START + +#define BSON_OK 0 +#define BSON_ERROR -1 + +//Maxim field path length +#define BSON_MAX_FPATH_LEN 255 + +enum bson_error_t { + BSON_SIZE_OVERFLOW = 1 /**< Trying to create a BSON object larger than INT_MAX. */ +}; + +enum bson_validity_t { + BSON_VALID = 0, /**< BSON is valid and UTF-8 compliant. */ + BSON_NOT_UTF8 = (1 << 1), /**< A key or a string is not valid UTF-8. */ + BSON_FIELD_HAS_DOT = (1 << 2), /**< Warning: key contains '.' character. */ + BSON_FIELD_INIT_DOLLAR = (1 << 3), /**< Warning: key starts with '$' character. */ + BSON_ALREADY_FINISHED = (1 << 4) /**< Trying to modify a finished BSON object. */ +}; + +enum bson_binary_subtype_t { + BSON_BIN_BINARY = 0, + BSON_BIN_FUNC = 1, + BSON_BIN_BINARY_OLD = 2, + BSON_BIN_UUID = 3, + BSON_BIN_MD5 = 5, + BSON_BIN_USER = 128 +}; + +enum bson_flags_t { + BSON_FLAG_QUERY_MODE = 1 +}; + +typedef enum { + BSON_EOO = 0, + BSON_DOUBLE = 1, + BSON_STRING = 2, + BSON_OBJECT = 3, + BSON_ARRAY = 4, + BSON_BINDATA = 5, + BSON_UNDEFINED = 6, + BSON_OID = 7, + BSON_BOOL = 8, + BSON_DATE = 9, + BSON_NULL = 10, + BSON_REGEX = 11, + BSON_DBREF = 12, /**< Deprecated. */ + BSON_CODE = 13, + BSON_SYMBOL = 14, + BSON_CODEWSCOPE = 15, + BSON_INT = 16, + BSON_TIMESTAMP = 17, + BSON_LONG = 18 +} bson_type; + +typedef int bson_bool_t; + +typedef struct { + const char *cur; + bson_bool_t first; +} bson_iterator; + +typedef struct { + char *data; /**< Pointer to a block of data in this BSON object. */ + char *cur; /**< Pointer to the current position. */ + int dataSize; /**< The number of bytes allocated to char *data. */ + bson_bool_t finished; /**< When finished, the BSON object can no longer be modified. */ + int stack[32]; /**< A stack used to keep track of nested BSON elements. */ + int stackPos; /**< Index of current stack position. */ + int err; /**< Bitfield representing errors or warnings on this buffer */ + char *errstr; /**< A string representation of the most recent error or warning. */ + int flags; +} bson; + +#pragma pack(1) + +typedef union { + char bytes[12]; + int ints[3]; +} bson_oid_t; +#pragma pack() + +typedef int64_t bson_date_t; /* milliseconds since epoch UTC */ + +typedef struct { + int i; /* increment */ + int t; /* time in seconds */ +} bson_timestamp_t; + +/* ---------------------------- + READING + ------------------------------ */ + +EJDB_EXPORT bson* bson_create(void); +EJDB_EXPORT void bson_dispose(bson* b); + +/** + * Size of a BSON object. + * + * @param b the BSON object. + * + * @return the size. + */ +EJDB_EXPORT int bson_size(const bson *b); +EJDB_EXPORT int bson_buffer_size(const bson *b); + +/** + * Print a string representation of a BSON object. + * + * @param b the BSON object to print. + */ +EJDB_EXPORT void bson_print(FILE *f, const bson *b); + +/** + * Return a pointer to the raw buffer stored by this bson object. + * + * @param b a BSON object + */ +EJDB_EXPORT const char *bson_data(const bson *b); + +/** + * Print a string representation of a BSON object. + * + * @param bson the raw data to print. + * @param depth the depth to recurse the object.x + */ +EJDB_EXPORT void bson_print_raw(FILE *f, const char *bson, int depth); + +/** + * Advance a bson_iterator to the named field. + * + * @param it the bson_iterator to use. + * @param obj the BSON object to use. + * @param name the name of the field to find. + * + * @return the type of the found object or BSON_EOO if it is not found. + */ +EJDB_EXPORT bson_type bson_find(bson_iterator *it, const bson *obj, const char *name); + +EJDB_EXPORT bson_type bson_find_from_buffer(bson_iterator *it, const char *buffer, const char *name); + +/** + * Advance specified iterator 'it' to field value pointing by 'fieldpath'/ + * Field path string format: 'field1.nestedfield1.nestedfield.2'. + * If specified field not found BSON_EOO will be returned. + * + * @param fieldpath Path specification to the BSON field. + * @param it the bson_iterator to use. + * @return the type of the found object or BSON_EOO if it is not found. + */ +EJDB_EXPORT bson_type bson_find_fieldpath_value(const char *fieldpath, bson_iterator *it); +EJDB_EXPORT bson_type bson_find_fieldpath_value2(const char *fpath, int fplen, bson_iterator *it); + + +EJDB_EXPORT bson_iterator* bson_iterator_create(void); +EJDB_EXPORT void bson_iterator_dispose(bson_iterator*); +/** + * Initialize a bson_iterator. + * + * @param i the bson_iterator to initialize. + * @param bson the BSON object to associate with the iterator. + */ +EJDB_EXPORT void bson_iterator_init(bson_iterator *i, const bson *b); + +/** + * Initialize a bson iterator from a const char* buffer. Note + * that this is mostly used internally. + * + * @param i the bson_iterator to initialize. + * @param buffer the buffer to point to. + */ +EJDB_EXPORT void bson_iterator_from_buffer(bson_iterator *i, const char *buffer); + +/* more returns true for eoo. best to loop with bson_iterator_next(&it) */ +/** + * Check to see if the bson_iterator has more data. + * + * @param i the iterator. + * + * @return returns true if there is more data. + */ +EJDB_EXPORT bson_bool_t bson_iterator_more(const bson_iterator *i); + +/** + * Point the iterator at the next BSON object. + * + * @param i the bson_iterator. + * + * @return the type of the next BSON object. + */ +EJDB_EXPORT bson_type bson_iterator_next(bson_iterator *i); + +/** + * Get the type of the BSON object currently pointed to by the iterator. + * + * @param i the bson_iterator + * + * @return the type of the current BSON object. + */ +EJDB_EXPORT bson_type bson_iterator_type(const bson_iterator *i); + +/** + * Get the key of the BSON object currently pointed to by the iterator. + * + * @param i the bson_iterator + * + * @return the key of the current BSON object. + */ +EJDB_EXPORT const char *bson_iterator_key(const bson_iterator *i); + +/** + * Get the value of the BSON object currently pointed to by the iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +EJDB_EXPORT const char *bson_iterator_value(const bson_iterator *i); + +/* these convert to the right type (return 0 if non-numeric) */ +/** + * Get the double value of the BSON object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +EJDB_EXPORT double bson_iterator_double(const bson_iterator *i); + +/** + * Get the int value of the BSON object currently pointed to by the iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +EJDB_EXPORT int bson_iterator_int(const bson_iterator *i); + +/** + * Get the long value of the BSON object currently pointed to by the iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +EJDB_EXPORT int64_t bson_iterator_long(const bson_iterator *i); + +/* return the bson timestamp as a whole or in parts */ +/** + * Get the timestamp value of the BSON object currently pointed to by + * the iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +EJDB_EXPORT bson_timestamp_t bson_iterator_timestamp(const bson_iterator *i); +EJDB_EXPORT int bson_iterator_timestamp_time(const bson_iterator *i); +EJDB_EXPORT int bson_iterator_timestamp_increment(const bson_iterator *i); + +/** + * Get the boolean value of the BSON object currently pointed to by + * the iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +/* false: boolean false, 0 in any type, or null */ +/* true: anything else (even empty strings and objects) */ +EJDB_EXPORT bson_bool_t bson_iterator_bool(const bson_iterator *i); + +/** + * Get the double value of the BSON object currently pointed to by the + * iterator. Assumes the correct type is used. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +/* these assume you are using the right type */ +double bson_iterator_double_raw(const bson_iterator *i); + +/** + * Get the int value of the BSON object currently pointed to by the + * iterator. Assumes the correct type is used. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +int bson_iterator_int_raw(const bson_iterator *i); + +/** + * Get the long value of the BSON object currently pointed to by the + * iterator. Assumes the correct type is used. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +int64_t bson_iterator_long_raw(const bson_iterator *i); + +/** + * Get the bson_bool_t value of the BSON object currently pointed to by the + * iterator. Assumes the correct type is used. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +bson_bool_t bson_iterator_bool_raw(const bson_iterator *i); + +/** + * Get the bson_oid_t value of the BSON object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +EJDB_EXPORT bson_oid_t *bson_iterator_oid(const bson_iterator *i); + +/** + * Get the string value of the BSON object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +/* these can also be used with bson_code and bson_symbol*/ +EJDB_EXPORT const char *bson_iterator_string(const bson_iterator *i); + +/** + * Get the string length of the BSON object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the length of the current BSON object. + */ +int bson_iterator_string_len(const bson_iterator *i); + +/** + * Get the code value of the BSON object currently pointed to by the + * iterator. Works with bson_code, bson_codewscope, and BSON_STRING + * returns NULL for everything else. + * + * @param i the bson_iterator + * + * @return the code value of the current BSON object. + */ +/* works with bson_code, bson_codewscope, and BSON_STRING */ +/* returns NULL for everything else */ +EJDB_EXPORT const char *bson_iterator_code(const bson_iterator *i); + +/** + * Calls bson_empty on scope if not a bson_codewscope + * + * @param i the bson_iterator. + * @param scope the bson scope. + */ +/* calls bson_empty on scope if not a bson_codewscope */ +EJDB_EXPORT void bson_iterator_code_scope(const bson_iterator *i, bson *scope); + +/** + * Get the date value of the BSON object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the date value of the current BSON object. + */ +/* both of these only work with bson_date */ +EJDB_EXPORT bson_date_t bson_iterator_date(const bson_iterator *i); + +/** + * Get the time value of the BSON object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the time value of the current BSON object. + */ +EJDB_EXPORT time_t bson_iterator_time_t(const bson_iterator *i); + +/** + * Get the length of the BSON binary object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the length of the current BSON binary object. + */ +EJDB_EXPORT int bson_iterator_bin_len(const bson_iterator *i); + +/** + * Get the type of the BSON binary object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the type of the current BSON binary object. + */ +EJDB_EXPORT char bson_iterator_bin_type(const bson_iterator *i); + +/** + * Get the value of the BSON binary object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON binary object. + */ +EJDB_EXPORT const char *bson_iterator_bin_data(const bson_iterator *i); + +/** + * Get the value of the BSON regex object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON regex object. + */ +EJDB_EXPORT const char *bson_iterator_regex(const bson_iterator *i); + +/** + * Get the options of the BSON regex object currently pointed to by the + * iterator. + * + * @param i the bson_iterator. + * + * @return the options of the current BSON regex object. + */ +EJDB_EXPORT const char *bson_iterator_regex_opts(const bson_iterator *i); + +/* these work with BSON_OBJECT and BSON_ARRAY */ +/** + * Get the BSON subobject currently pointed to by the + * iterator. + * + * @param i the bson_iterator. + * @param sub the BSON subobject destination. + */ +EJDB_EXPORT void bson_iterator_subobject(const bson_iterator *i, bson *sub); + +/** + * Get a bson_iterator that on the BSON subobject. + * + * @param i the bson_iterator. + * @param sub the iterator to point at the BSON subobject. + */ +EJDB_EXPORT void bson_iterator_subiterator(const bson_iterator *i, bson_iterator *sub); + +/* str must be at least 24 hex chars + null byte */ +/** + * Create a bson_oid_t from a string. + * + * @param oid the bson_oid_t destination. + * @param str a null terminated string comprised of at least 24 hex chars. + */ +EJDB_EXPORT void bson_oid_from_string(bson_oid_t *oid, const char *str); + +/** + * Create a string representation of the bson_oid_t. + * + * @param oid the bson_oid_t source. + * @param str the string representation destination. + */ +EJDB_EXPORT void bson_oid_to_string(const bson_oid_t *oid, char *str); + +/** + * Create a bson_oid object. + * + * @param oid the destination for the newly created bson_oid_t. + */ +EJDB_EXPORT void bson_oid_gen(bson_oid_t *oid); + +/** + * Set a function to be used to generate the second four bytes + * of an object id. + * + * @param func a pointer to a function that returns an int. + */ +EJDB_EXPORT void bson_set_oid_fuzz(int ( *func)(void)); + +/** + * Set a function to be used to generate the incrementing part + * of an object id (last four bytes). If you need thread-safety + * in generating object ids, you should set this function. + * + * @param func a pointer to a function that returns an int. + */ +EJDB_EXPORT void bson_set_oid_inc(int ( *func)(void)); + +/** + * Get the time a bson_oid_t was created. + * + * @param oid the bson_oid_t. + */ +EJDB_EXPORT time_t bson_oid_generated_time(bson_oid_t *oid); /* Gives the time the OID was created */ + +/* ---------------------------- + BUILDING + ------------------------------ */ + + +EJDB_EXPORT void bson_append(bson *b, const void *data, int len); + +/** + * Initialize a new bson object. If not created + * with bson_new, you must initialize each new bson + * object using this function. + * + * @note When finished, you must pass the bson object to + * bson_destroy( ). + */ +EJDB_EXPORT void bson_init(bson *b); + + +/** + * Intialize a new bson object. In query contruction mode allowing + * dot and dollar chars in field names. + * @param b + */ +EJDB_EXPORT void bson_init_as_query(bson *b); + +/** + * Initialize a BSON object, and point its data + * pointer to the provided char*. + * + * @param b the BSON object to initialize. + * @param data the raw BSON data. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_init_data(bson *b, char *data); +int bson_init_finished_data(bson *b, char *data); + +/** + * Initialize a BSON object, and set its + * buffer to the given size. + * + * @param b the BSON object to initialize. + * @param size the initial size of the buffer. + * + * @return BSON_OK or BSON_ERROR. + */ +void bson_init_size(bson *b, int size); + +/** + * Grow a bson object. + * + * @param b the bson to grow. + * @param bytesNeeded the additional number of bytes needed. + * + * @return BSON_OK or BSON_ERROR with the bson error object set. + * Exits if allocation fails. + */ +int bson_ensure_space(bson *b, const int bytesNeeded); + +/** + * Finalize a bson object. + * + * @param b the bson object to finalize. + * + * @return the standard error code. To deallocate memory, + * call bson_destroy on the bson object. + */ +EJDB_EXPORT int bson_finish(bson *b); + +/** + * Destroy a bson object. + * + * @param b the bson object to destroy. + * + */ +EJDB_EXPORT void bson_destroy(bson *b); + +EJDB_EXPORT void bson_del(bson *b); + +/** + * Returns a pointer to a static empty BSON object. + * + * @param obj the BSON object to initialize. + * + * @return the empty initialized BSON object. + */ +/* returns pointer to static empty bson object */ +EJDB_EXPORT bson *bson_empty(bson *obj); + +/** + * Make a complete copy of the a BSON object. + * The source bson object must be in a finished + * state; otherwise, the copy will fail. + * + * @param out the copy destination BSON object. + * @param in the copy source BSON object. + */ +EJDB_EXPORT int bson_copy(bson *out, const bson *in); /* puts data in new buffer. NOOP if out==NULL */ + +/** + * Append a previously created bson_oid_t to a bson object. + * + * @param b the bson to append to. + * @param name the key for the bson_oid_t. + * @param oid the bson_oid_t to append. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_oid(bson *b, const char *name, const bson_oid_t *oid); + +/** + * Append a bson_oid_t to a bson. + * + * @param b the bson to append to. + * @param name the key for the bson_oid_t. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_new_oid(bson *b, const char *name); + +/** + * Append an int to a bson. + * + * @param b the bson to append to. + * @param name the key for the int. + * @param i the int to append. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_int(bson *b, const char *name, const int i); + +/** + * Append an long to a bson. + * + * @param b the bson to append to. + * @param name the key for the long. + * @param i the long to append. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_long(bson *b, const char *name, const int64_t i); + +/** + * Append an double to a bson. + * + * @param b the bson to append to. + * @param name the key for the double. + * @param d the double to append. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_double(bson *b, const char *name, const double d); + +/** + * Append a string to a bson. + * + * @param b the bson to append to. + * @param name the key for the string. + * @param str the string to append. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_string(bson *b, const char *name, const char *str); + +/** + * Append len bytes of a string to a bson. + * + * @param b the bson to append to. + * @param name the key for the string. + * @param str the string to append. + * @param len the number of bytes from str to append. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_string_n(bson *b, const char *name, const char *str, int len); + +/** + * Append a symbol to a bson. + * + * @param b the bson to append to. + * @param name the key for the symbol. + * @param str the symbol to append. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_symbol(bson *b, const char *name, const char *str); + +/** + * Append len bytes of a symbol to a bson. + * + * @param b the bson to append to. + * @param name the key for the symbol. + * @param str the symbol to append. + * @param len the number of bytes from str to append. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_symbol_n(bson *b, const char *name, const char *str, int len); + +/** + * Append code to a bson. + * + * @param b the bson to append to. + * @param name the key for the code. + * @param str the code to append. + * @param len the number of bytes from str to append. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_code(bson *b, const char *name, const char *str); + +/** + * Append len bytes of code to a bson. + * + * @param b the bson to append to. + * @param name the key for the code. + * @param str the code to append. + * @param len the number of bytes from str to append. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_code_n(bson *b, const char *name, const char *str, int len); + +/** + * Append code to a bson with scope. + * + * @param b the bson to append to. + * @param name the key for the code. + * @param str the string to append. + * @param scope a BSON object containing the scope. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_code_w_scope(bson *b, const char *name, const char *code, const bson *scope); + +/** + * Append len bytes of code to a bson with scope. + * + * @param b the bson to append to. + * @param name the key for the code. + * @param str the string to append. + * @param len the number of bytes from str to append. + * @param scope a BSON object containing the scope. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_code_w_scope_n(bson *b, const char *name, const char *code, int size, const bson *scope); + +/** + * Append binary data to a bson. + * + * @param b the bson to append to. + * @param name the key for the data. + * @param type the binary data type. + * @param str the binary data. + * @param len the length of the data. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_binary(bson *b, const char *name, char type, const char *str, int len); + +/** + * Append a bson_bool_t to a bson. + * + * @param b the bson to append to. + * @param name the key for the boolean value. + * @param v the bson_bool_t to append. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_bool(bson *b, const char *name, const bson_bool_t v); + +/** + * Append a null value to a bson. + * + * @param b the bson to append to. + * @param name the key for the null value. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_null(bson *b, const char *name); + +/** + * Append an undefined value to a bson. + * + * @param b the bson to append to. + * @param name the key for the undefined value. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_undefined(bson *b, const char *name); + +/** + * Append a regex value to a bson. + * + * @param b the bson to append to. + * @param name the key for the regex value. + * @param pattern the regex pattern to append. + * @param the regex options. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_regex(bson *b, const char *name, const char *pattern, const char *opts); + +/** + * Append bson data to a bson. + * + * @param b the bson to append to. + * @param name the key for the bson data. + * @param bson the bson object to append. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_bson(bson *b, const char *name, const bson *bson); + +/** + * Append a BSON element to a bson from the current point of an iterator. + * + * @param b the bson to append to. + * @param name_or_null the key for the BSON element, or NULL. + * @param elem the bson_iterator. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_element(bson *b, const char *name_or_null, const bson_iterator *elem); + +/** + * Append a bson_timestamp_t value to a bson. + * + * @param b the bson to append to. + * @param name the key for the timestampe value. + * @param ts the bson_timestamp_t value to append. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_timestamp(bson *b, const char *name, bson_timestamp_t *ts); +EJDB_EXPORT int bson_append_timestamp2(bson *b, const char *name, int time, int increment); + +/* these both append a bson_date */ +/** + * Append a bson_date_t value to a bson. + * + * @param b the bson to append to. + * @param name the key for the date value. + * @param millis the bson_date_t to append. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_date(bson *b, const char *name, bson_date_t millis); + +/** + * Append a time_t value to a bson. + * + * @param b the bson to append to. + * @param name the key for the date value. + * @param secs the time_t to append. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_time_t(bson *b, const char *name, time_t secs); + +/** + * Start appending a new object to a bson. + * + * @param b the bson to append to. + * @param name the name of the new object. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_start_object(bson *b, const char *name); + +/** + * Start appending a new array to a bson. + * + * @param b the bson to append to. + * @param name the name of the new array. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_start_array(bson *b, const char *name); + +/** + * Finish appending a new object or array to a bson. + * + * @param b the bson to append to. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_finish_object(bson *b); + +/** + * Finish appending a new object or array to a bson. This + * is simply an alias for bson_append_finish_object. + * + * @param b the bson to append to. + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_append_finish_array(bson *b); + +EJDB_EXPORT void bson_numstr(char *str, long long int i); +EJDB_EXPORT int bson_numstrn(char *str, int maxbuf, long long int i); + +//void bson_incnumstr(char *str); + +/* Error handling and standard library function over-riding. */ +/* -------------------------------------------------------- */ + +/* bson_err_handlers shouldn't return!!! */ +typedef void( *bson_err_handler)(const char *errmsg); + +typedef int (*bson_printf_func)(const char *, ...); +typedef int (*bson_fprintf_func)(FILE *, const char *, ...); +typedef int (*bson_sprintf_func)(char *, const char *, ...); + +extern void *(*bson_malloc_func)(size_t); +extern void *(*bson_realloc_func)(void *, size_t); +extern void ( *bson_free_func)(void *); + +extern bson_printf_func bson_printf; +extern bson_fprintf_func bson_fprintf; +extern bson_sprintf_func bson_sprintf; +extern bson_printf_func bson_errprintf; + +EJDB_EXPORT void bson_free(void *ptr); + +/** + * Allocates memory and checks return value, exiting fatally if malloc() fails. + * + * @param size bytes to allocate. + * + * @return a pointer to the allocated memory. + * + * @sa malloc(3) + */ +EJDB_EXPORT void *bson_malloc(int size); + +/** + * Changes the size of allocated memory and checks return value, + * exiting fatally if realloc() fails. + * + * @param ptr pointer to the space to reallocate. + * @param size bytes to allocate. + * + * @return a pointer to the allocated memory. + * + * @sa realloc() + */ +void *bson_realloc(void *ptr, int size); + +/** + * Set a function for error handling. + * + * @param func a bson_err_handler function. + * + * @return the old error handling function, or NULL. + */ +EJDB_EXPORT bson_err_handler set_bson_err_handler(bson_err_handler func); + +/* does nothing if ok != 0 */ +/** + * Exit fatally. + * + * @param ok exits if ok is equal to 0. + */ +void bson_fatal(int ok); + +/** + * Exit fatally with an error message. + * + * @param ok exits if ok is equal to 0. + * @param msg prints to stderr before exiting. + */ +void bson_fatal_msg(int ok, const char *msg); + +/** + * Invoke the error handler, but do not exit. + * + * @param b the buffer object. + */ +void bson_builder_error(bson *b); + +/** + * Cast an int64_t to double. This is necessary for embedding in + * certain environments. + * + */ +EJDB_EXPORT double bson_int64_to_double(int64_t i64); + +EJDB_EXPORT void bson_swap_endian32(void *outp, const void *inp); +EJDB_EXPORT void bson_swap_endian64(void *outp, const void *inp); + + + +/** + * Append current field from iterator into bson object. + * @param from + * @param into + */ +EJDB_EXPORT void bson_append_field_from_iterator(bson_iterator *from, bson *into); + + +/** + * Merge bson 'b2' into 'b1' saving result the 'out' object. + * 'b1' & 'b2' bson must be finished BSONS. + * Resulting 'out' bson must be allocated and not finished. + * + * Nested object skipped and usupported. + * + * @param b1 BSON to to be merged into out + * @param b2 BSON to to be merged into out + * @param overwrite if True All b1 fields will be overwriten by corresponding b2 fields + * + * @return BSON_OK or BSON_ERROR. + */ +EJDB_EXPORT int bson_merge(bson *b1, bson *b2, bson_bool_t overwrite, bson *out); + + +/** + * Compares field path primitive values of two BSONs + * @param bsdata1 BSON raw data + * @param bsdata2 BSON raw data + * @param fpath Field path to the field + * @param fplen Length of fpath value + */ +EJDB_EXPORT int bson_compare(const void *bsdata1, const void *bsdata2, const char* fpath, int fplen); +EJDB_EXPORT int bson_compare_fpaths(const void *bsdata1, const void *bsdata2, const char *fpath1, int fplen1, const char *fpath2, int fplen2); +EJDB_EXPORT int bson_compare_string(const char* cv, const void *bsdata, const char *fpath); +EJDB_EXPORT int bson_compare_long(long cv, const void *bsdata, const char *fpath); +EJDB_EXPORT int bson_compare_double(double cv, const void *bsdata, const char *fpath); +EJDB_EXPORT int bson_compare_bool(bson_bool_t cv, const void *bsdata, const char *fpath); + + +/** + * Duplicates BSON object. + * @param src BSON + * @return Finished copy of src + */ +EJDB_EXPORT bson* bson_dup(const bson* src); + + +EJDB_EXPORT bson* bson_create_from_buffer(const void* buf, int bufsz); + + +EJDB_EXTERN_C_END +#endif diff --git a/tcejdb/configure b/tcejdb/configure new file mode 100755 index 0000000..74b3f32 --- /dev/null +++ b/tcejdb/configure @@ -0,0 +1,5493 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for tcejdb 1.0.0. +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='tcejdb' +PACKAGE_TARNAME='tcejdb' +PACKAGE_VERSION='1.0.0' +PACKAGE_STRING='tcejdb 1.0.0' +PACKAGE_BUGREPORT='' +PACKAGE_URL='' + +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='LTLIBOBJS +LIBOBJS +MYPOSTCMD +MYLDLIBPATHENV +MYRUNPATH +MYCMDLDFLAGS +MYLDFLAGS +MYCPPFLAGS +MYCFLAGS +MYPCFILES +MYDOCUMENTFILES +MYMAN3FILES +MYMAN1FILES +MYCGIFILES +MYCOMMANDFILES +MYLIBOBJFILES +MYLIBRARYFILES +MYHEADERFILES +MYFORMATVER +MYLIBREV +MYLIBVER +EGREP +GREP +CPP +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_debug +enable_devel +enable_profile +enable_static +enable_fastest +enable_off64 +enable_swab +enable_uyield +enable_ubc +enable_zlib +enable_bzip +enable_pthread +enable_shared +enable_exlzma +enable_exlzo +with_zlib +with_bzip +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures tcejdb 1.0.0 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/tcejdb] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of tcejdb 1.0.0:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-debug build for debugging + --enable-devel build for development + --enable-profile build for profiling + --enable-static build by static linking + --enable-fastest build for fastest run + --enable-off64 build with 64-bit file offset on 32-bit system + --enable-swab build for swapping byte-orders + --enable-uyield build for detecting race conditions + --disable-ubc build without the unified buffer cache assumption + --disable-zlib build without ZLIB compression + --disable-bzip build without BZIP2 compression + --disable-pthread build without POSIX thread support + --disable-shared avoid to build shared libraries + --disable-exlzma build with the custom codec of LZMA + --disable-exlzo build with the custom codec of LZO + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-zlib=DIR search DIR/include and DIR/lib for ZLIB + --with-bzip=DIR search DIR/include and DIR/lib for BZIP2 + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to the package provider. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +tcejdb configure 1.0.0 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by tcejdb $as_me 1.0.0, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +# Package information +MYLIBVER=9 +MYLIBREV=11 +MYFORMATVER="1.0" + +# Targets +MYHEADERFILES="tcutil.h tchdb.h tcbdb.h tcfdb.h tctdb.h tcadb.h ejdb.h ejdefs.h bson.h" +MYLIBRARYFILES="libtcejdb.a" +MYLIBOBJFILES="tcutil.o tchdb.o tcbdb.o tcfdb.o tctdb.o tcadb.o myconf.o md5.o ejdb.o bson.o timsort.o numbers.o encoding.o" +MYCOMMANDFILES="tcutest tcumttest tcucodec tchtest tchmttest tchmgr" +MYCOMMANDFILES="$MYCOMMANDFILES tcbtest tcbmttest tcbmgr tcftest tcfmttest tcfmgr" +MYCOMMANDFILES="$MYCOMMANDFILES tcttest tctmttest tctmgr tcatest tcamttest tcamgr" +MYCGIFILES="tcawmgr.cgi" +MYMAN1FILES="tcutest.1 tcumttest.1 tcucodec.1 tchtest.1 tchmttest.1 tchmgr.1" +MYMAN1FILES="$MYMAN1FILES tcbtest.1 tcbmttest.1 tcbmgr.1 tcftest.1 tcfmttest.1 tcfmgr.1" +MYMAN1FILES="$MYMAN1FILES tcttest.1 tctmttest.1 tctmgr.1 tcatest.1 tcamttest.1 tcamgr.1" +MYMAN3FILES="tokyocabinet.3 tcutil.3 tcxstr.3 tclist.3 tcmap.3 tctree.3 tcmdb.3 tcmpool.3" +MYMAN3FILES="$MYMAN3FILES tchdb.3 tcbdb.3 tcfdb.3 tctdb.3 tcadb.3" +#MYDOCUMENTFILES="COPYING ChangeLog doc tokyocabinet.idl" +MYDOCUMENTFILES="COPYING ChangeLog" +MYPCFILES="tcejdb.pc" + +# Building flags +MYCFLAGS="$MYCFLAGS -std=c99 -Wall -fPIC -fsigned-char -O2" +MYCPPFLAGS="-I. -I\$(INCLUDEDIR) -I$HOME/include -I/usr/local/include" +MYCPPFLAGS="$MYCPPFLAGS -DNDEBUG -D_GNU_SOURCE=1 -D_REENTRANT -D__EXTENSIONS__" +MYLDFLAGS="-L. -L\$(LIBDIR) -L$HOME/lib -L/usr/local/lib" +MYCMDLDFLAGS="" +MYRUNPATH="\$(LIBDIR)" +MYLDLIBPATHENV="LD_LIBRARY_PATH" +MYPOSTCMD="true" + +# Building paths +PATH="$PATH:$HOME/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" +PATH="$PATH:/opt/SUNWspro/bin:/usr/ccs/bin:/usr/xpg4/bin:/usr/xpg6/bin:/usr/ucb" +CPATH="$HOME/include:/usr/local/include:$CPATH" +LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LIBRARY_PATH" +LD_LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LD_LIBRARY_PATH" +PKG_CONFIG_PATH="$HOME/lib/pkgconfig:/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH" +export PATH CPATH LIBRARY_PATH LD_LIBRARY_PATH PKG_CONFIG_PATH + + +#================================================================ +# Options +#================================================================ + + +# Internal variables +enables="" + +# Debug mode +# Check whether --enable-debug was given. +if test "${enable_debug+set}" = set; then : + enableval=$enable_debug; +fi + +if test "$enable_debug" = "yes" +then + MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -O0" + MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG" + MYCMDLDFLAGS="$MYCMDLDFLAGS -static" + enables="$enables (debug)" +fi + +# Developping mode +# Check whether --enable-devel was given. +if test "${enable_devel+set}" = set; then : + enableval=$enable_devel; +fi + +if test "$enable_devel" = "yes" +then + MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -O2 -pipe" + MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG" + enables="$enables (devel)" +fi + +# Profiling mode +# Check whether --enable-profile was given. +if test "${enable_profile+set}" = set; then : + enableval=$enable_profile; +fi + +if test "$enable_profile" = "yes" +then + MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -pg -O2 -pipe" + enables="$enables (profile)" +fi + +# Static mode +# Check whether --enable-static was given. +if test "${enable_static+set}" = set; then : + enableval=$enable_static; +fi + +if test "$enable_static" = "yes" +then + MYCMDLDFLAGS="$MYCMDLDFLAGS -static" + enables="$enables (static)" +fi + +# Fastest mode +# Check whether --enable-fastest was given. +if test "${enable_fastest+set}" = set; then : + enableval=$enable_fastest; +fi + +if test "$enable_fastest" = "yes" +then + MYLIBOBJFILES="tokyocabinet_all.o" + MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -O3" + MYCFLAGS="$MYCFLAGS -fomit-frame-pointer -fforce-addr -minline-all-stringops" + MYCPPFLAGS="$MYCPPFLAGS -D_MYFASTEST" + enables="$enables (fastest)" +fi + +# 64-bit offset mode +# Check whether --enable-off64 was given. +if test "${enable_off64+set}" = set; then : + enableval=$enable_off64; +fi + +if test "$enable_off64" = "yes" +then + MYCPPFLAGS="$MYCPPFLAGS -D_FILE_OFFSET_BITS=64" + enables="$enables (off64)" +fi + +# Swapping byte-orders mode +# Check whether --enable-swab was given. +if test "${enable_swab+set}" = set; then : + enableval=$enable_swab; +fi + +if test "$enable_swab" = "yes" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYSWAB" + enables="$enables (swab)" +fi + +# Micro yield mode +# Check whether --enable-uyield was given. +if test "${enable_uyield+set}" = set; then : + enableval=$enable_uyield; +fi + +if test "$enable_uyield" = "yes" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYMICROYIELD" + enables="$enables (uyield)" +fi + +# Disable the unified buffer cache assumption +# Check whether --enable-ubc was given. +if test "${enable_ubc+set}" = set; then : + enableval=$enable_ubc; +fi + +if test "$enable_ubc" = "no" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYNOUBC" + enables="$enables (no-ubc)" +fi + +# Disable ZLIB compression +# Check whether --enable-zlib was given. +if test "${enable_zlib+set}" = set; then : + enableval=$enable_zlib; +fi + +if test "$enable_zlib" = "no" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYNOZLIB" + enables="$enables (no-zlib)" +fi + +# Disable BZIP2 compression +# Check whether --enable-bzip was given. +if test "${enable_bzip+set}" = set; then : + enableval=$enable_bzip; +fi + +if test "$enable_bzip" = "no" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYNOBZIP" + enables="$enables (no-bzip)" +fi + +# Disable POSIX thread +# Check whether --enable-pthread was given. +if test "${enable_pthread+set}" = set; then : + enableval=$enable_pthread; +fi + +if test "$enable_pthread" = "no" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYNOPTHREAD" + enables="$enables (no-pthread)" +fi + +# Disable shared object +# Check whether --enable-shared was given. +if test "${enable_shared+set}" = set; then : + enableval=$enable_shared; +fi + +if test "$enable_shared" = "no" +then + enables="$enables (no-shared)" +fi + +# Enable custom codec functions of LZMA +# Check whether --enable-exlzma was given. +if test "${enable_exlzma+set}" = set; then : + enableval=$enable_exlzma; +fi + +if test "$enable_exlzma" = "yes" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYEXLZMA" + enables="$enables (exlzma)" +fi + +# Enable custom codec functions of LZO +# Check whether --enable-exlzo was given. +if test "${enable_exlzo+set}" = set; then : + enableval=$enable_exlzo; +fi + +if test "$enable_exlzo" = "yes" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYEXLZO" + enables="$enables (exlzo)" +fi + +# Specify the installation path of ZLIB + +# Check whether --with-zlib was given. +if test "${with_zlib+set}" = set; then : + withval=$with_zlib; +fi + +if test -n "$with_zlib" +then + MYCPPFLAGS="$MYCPPFLAGS -I$with_zlib/include" + MYLDFLAGS="$MYLDFLAGS -L$with_zlib/lib" + MYRUNPATH="$MYRUNPATH:$with_zlib/lib" + CPATH="$CPATH:$with_zlib/include" + LIBRARY_PATH="$LIBRARY_PATH:$with_zlib/lib" + LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$with_zlib/lib" +fi + +# Specify the installation path of BZIP2 + +# Check whether --with-bzip was given. +if test "${with_bzip+set}" = set; then : + withval=$with_bzip; +fi + +if test -n "$with_bzip" +then + MYCPPFLAGS="$MYCPPFLAGS -I$with_bzip/include" + MYLDFLAGS="$MYLDFLAGS -L$with_bzip/lib" + MYRUNPATH="$MYRUNPATH:$with_bzip/lib" + CPATH="$CPATH:$with_bzip/include" + LIBRARY_PATH="$LIBRARY_PATH:$with_bzip/lib" + LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$with_bzip/lib" +fi + +# Messages +printf '#================================================================\n' +printf '# Configuring Tokyo Cabinet version %s%s.\n' "$PACKAGE_VERSION" "$enables" +printf '#================================================================\n' + + + +#================================================================ +# Checking Commands and Libraries +#================================================================ + +# C compiler +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# Reset variables +if test "$GCC" != "yes" +then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: another compiler except for GCC was detected" >&5 +$as_echo "$as_me: WARNING: another compiler except for GCC was detected" >&2;} + MYCFLAGS="" +fi + +# Byte order + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 +$as_echo_n "checking whether byte ordering is bigendian... " >&6; } +if ${ac_cv_c_bigendian+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_c_bigendian=unknown + # See if we're dealing with a universal compiler. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __APPLE_CC__ + not a universal capable compiler + #endif + typedef int dummy; + +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + # Check for potential -arch flags. It is not universal unless + # there are at least two -arch flags with different values. + ac_arch= + ac_prev= + for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do + if test -n "$ac_prev"; then + case $ac_word in + i?86 | x86_64 | ppc | ppc64) + if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then + ac_arch=$ac_word + else + ac_cv_c_bigendian=universal + break + fi + ;; + esac + ac_prev= + elif test "x$ac_word" = "x-arch"; then + ac_prev=arch + fi + done +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + if test $ac_cv_c_bigendian = unknown; then + # See if sys/param.h defines the BYTE_ORDER macro. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main () +{ +#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ + && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ + && LITTLE_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + # It does; now see whether it defined to _BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main () +{ +#ifndef _BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_c_bigendian=yes +else + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # Compile a test program. + if test "$cross_compiling" = yes; then : + # Try to guess by grepping values from an object file. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +short int ascii_mm[] = + { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; + short int ascii_ii[] = + { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; + int use_ascii (int i) { + return ascii_mm[i] + ascii_ii[i]; + } + short int ebcdic_ii[] = + { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; + short int ebcdic_mm[] = + { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; + int use_ebcdic (int i) { + return ebcdic_mm[i] + ebcdic_ii[i]; + } + extern int foo; + +int +main () +{ +return use_ascii (foo) == use_ebcdic (foo); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then + ac_cv_c_bigendian=yes + fi + if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi + fi +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long int l; + char c[sizeof (long int)]; + } u; + u.l = 1; + return u.c[sizeof (long int) - 1] == 1; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + ac_cv_c_bigendian=no +else + ac_cv_c_bigendian=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 +$as_echo "$ac_cv_c_bigendian" >&6; } + case $ac_cv_c_bigendian in #( + yes) + MYCPPFLAGS="$MYCPPFLAGS -D_MYBIGEND";; #( + no) + ;; #( + universal) + +$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h + + ;; #( + *) + as_fn_error $? "unknown endianness + presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; + esac + + +# Underlying libraries +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lc" >&5 +$as_echo_n "checking for main in -lc... " >&6; } +if ${ac_cv_lib_c_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lc $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_c_main=yes +else + ac_cv_lib_c_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_c_main" >&5 +$as_echo "$ac_cv_lib_c_main" >&6; } +if test "x$ac_cv_lib_c_main" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBC 1 +_ACEOF + + LIBS="-lc $LIBS" + +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lm" >&5 +$as_echo_n "checking for main in -lm... " >&6; } +if ${ac_cv_lib_m_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lm $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_m_main=yes +else + ac_cv_lib_m_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_main" >&5 +$as_echo "$ac_cv_lib_m_main" >&6; } +if test "x$ac_cv_lib_m_main" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBM 1 +_ACEOF + + LIBS="-lm $LIBS" + +fi + +if test "$enable_pthread" != "no" +then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lpthread" >&5 +$as_echo_n "checking for main in -lpthread... " >&6; } +if ${ac_cv_lib_pthread_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_pthread_main=yes +else + ac_cv_lib_pthread_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_main" >&5 +$as_echo "$ac_cv_lib_pthread_main" >&6; } +if test "x$ac_cv_lib_pthread_main" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBPTHREAD 1 +_ACEOF + + LIBS="-lpthread $LIBS" + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lrt" >&5 +$as_echo_n "checking for main in -lrt... " >&6; } +if ${ac_cv_lib_rt_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lrt $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_rt_main=yes +else + ac_cv_lib_rt_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_main" >&5 +$as_echo "$ac_cv_lib_rt_main" >&6; } +if test "x$ac_cv_lib_rt_main" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBRT 1 +_ACEOF + + LIBS="-lrt $LIBS" + +fi + +fi +if test "$enable_zlib" != "no" +then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lz" >&5 +$as_echo_n "checking for main in -lz... " >&6; } +if ${ac_cv_lib_z_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lz $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_z_main=yes +else + ac_cv_lib_z_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_main" >&5 +$as_echo "$ac_cv_lib_z_main" >&6; } +if test "x$ac_cv_lib_z_main" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBZ 1 +_ACEOF + + LIBS="-lz $LIBS" + +fi + +fi +if test "$enable_bzip" != "no" +then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lbz2" >&5 +$as_echo_n "checking for main in -lbz2... " >&6; } +if ${ac_cv_lib_bz2_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lbz2 $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_bz2_main=yes +else + ac_cv_lib_bz2_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bz2_main" >&5 +$as_echo "$ac_cv_lib_bz2_main" >&6; } +if test "x$ac_cv_lib_bz2_main" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBBZ2 1 +_ACEOF + + LIBS="-lbz2 $LIBS" + +fi + +fi +if test "$enable_exlzma" = "yes" +then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -llzma" >&5 +$as_echo_n "checking for main in -llzma... " >&6; } +if ${ac_cv_lib_lzma_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-llzma $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_lzma_main=yes +else + ac_cv_lib_lzma_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lzma_main" >&5 +$as_echo "$ac_cv_lib_lzma_main" >&6; } +if test "x$ac_cv_lib_lzma_main" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBLZMA 1 +_ACEOF + + LIBS="-llzma $LIBS" + +fi + +fi +if test "$enable_exlzo" = "yes" +then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -llzo2" >&5 +$as_echo_n "checking for main in -llzo2... " >&6; } +if ${ac_cv_lib_lzo2_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-llzo2 $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_lzo2_main=yes +else + ac_cv_lib_lzo2_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lzo2_main" >&5 +$as_echo "$ac_cv_lib_lzo2_main" >&6; } +if test "x$ac_cv_lib_lzo2_main" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBLZO2 1 +_ACEOF + + LIBS="-llzo2 $LIBS" + +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -ltcejdb" >&5 +$as_echo_n "checking for main in -ltcejdb... " >&6; } +if ${ac_cv_lib_tcejdb_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ltcejdb $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_tcejdb_main=yes +else + ac_cv_lib_tcejdb_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tcejdb_main" >&5 +$as_echo "$ac_cv_lib_tcejdb_main" >&6; } +if test "x$ac_cv_lib_tcejdb_main" = xyes; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: old version of EJDB was found" >&5 +$as_echo "$as_me: WARNING: old version of EJDB was found" >&2;} +fi + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lcunit" >&5 +$as_echo_n "checking for main in -lcunit... " >&6; } +if ${ac_cv_lib_cunit_main+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lcunit $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +int +main () +{ +return main (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_cunit_main=yes +else + ac_cv_lib_cunit_main=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cunit_main" >&5 +$as_echo "$ac_cv_lib_cunit_main" >&6; } +if test "x$ac_cv_lib_cunit_main" = xyes; then : + true +else + as_fn_error $? "CUnit lubrary is required" "$LINENO" 5 +fi + + +# Necessary headers +ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" +if test "x$ac_cv_header_stdlib_h" = xyes; then : + true +else + as_fn_error $? "stdlib.h is required" "$LINENO" 5 +fi + + +ac_fn_c_check_header_mongrel "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default" +if test "x$ac_cv_header_stdint_h" = xyes; then : + true +else + as_fn_error $? "stdint.h is required" "$LINENO" 5 +fi + + +ac_fn_c_check_header_mongrel "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default" +if test "x$ac_cv_header_unistd_h" = xyes; then : + true +else + as_fn_error $? "unistd.h is required" "$LINENO" 5 +fi + + +ac_fn_c_check_header_mongrel "$LINENO" "dirent.h" "ac_cv_header_dirent_h" "$ac_includes_default" +if test "x$ac_cv_header_dirent_h" = xyes; then : + true +else + as_fn_error $? "dirent.h is required" "$LINENO" 5 +fi + + +ac_fn_c_check_header_mongrel "$LINENO" "regex.h" "ac_cv_header_regex_h" "$ac_includes_default" +if test "x$ac_cv_header_regex_h" = xyes; then : + true +else + as_fn_error $? "regex.h is required" "$LINENO" 5 +fi + + +ac_fn_c_check_header_mongrel "$LINENO" "glob.h" "ac_cv_header_glob_h" "$ac_includes_default" +if test "x$ac_cv_header_glob_h" = xyes; then : + true +else + as_fn_error $? "glob.h is required" "$LINENO" 5 +fi + + +if test "$enable_pthread" != "no" +then + ac_fn_c_check_header_mongrel "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default" +if test "x$ac_cv_header_pthread_h" = xyes; then : + true +else + as_fn_error $? "pthread.h is required" "$LINENO" 5 +fi + + +fi +if test "$enable_zlib" != "no" +then + ac_fn_c_check_header_mongrel "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default" +if test "x$ac_cv_header_zlib_h" = xyes; then : + true +else + as_fn_error $? "zlib.h is required" "$LINENO" 5 +fi + + +fi +if test "$enable_bzip" != "no" +then + ac_fn_c_check_header_mongrel "$LINENO" "bzlib.h" "ac_cv_header_bzlib_h" "$ac_includes_default" +if test "x$ac_cv_header_bzlib_h" = xyes; then : + true +else + as_fn_error $? "bzlib.h is required" "$LINENO" 5 +fi + + +fi +if test "$enable_exlzma" = "yes" +then + ac_fn_c_check_header_mongrel "$LINENO" "lzmalib.h" "ac_cv_header_lzmalib_h" "$ac_includes_default" +if test "x$ac_cv_header_lzmalib_h" = xyes; then : + true +else + as_fn_error $? "lzmalib.h is required" "$LINENO" 5 +fi + + +fi +if test "$enable_exlzo" = "yes" +then + ac_fn_c_check_header_mongrel "$LINENO" "lzo/lzo1x.h" "ac_cv_header_lzo_lzo1x_h" "$ac_includes_default" +if test "x$ac_cv_header_lzo_lzo1x_h" = xyes; then : + true +else + as_fn_error $? "lzo/lzo1x.h is required" "$LINENO" 5 +fi + + +fi + +# Shared libraries +if test "$enable_shared" != "no" && test "$enable_profile" != "yes" +then + if uname | grep Darwin >/dev/null + then + MYLIBRARYFILES="$MYLIBRARYFILES libtcejdb.$MYLIBVER.$MYLIBREV.0.dylib" + MYLIBRARYFILES="$MYLIBRARYFILES libtcejdb.$MYLIBVER.dylib" + MYLIBRARYFILES="$MYLIBRARYFILES libtcejdb.dylib" + MYLDLIBPATHENV="DYLD_LIBRARY_PATH" + else + MYLIBRARYFILES="$MYLIBRARYFILES libtcejdb.so.$MYLIBVER.$MYLIBREV.0" + MYLIBRARYFILES="$MYLIBRARYFILES libtcejdb.so.$MYLIBVER" + MYLIBRARYFILES="$MYLIBRARYFILES libtcejdb.so" + fi +fi + + + +#================================================================ +# Generic Settings +#================================================================ + +# Export variables + + + + + + + + + + + + + + + + + + + + +# Targets +ac_config_files="$ac_config_files Makefile tcejdb.pc" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +ac_script=' +:mline +/\\$/{ + N + s,\\\n,, + b mline +} +t clear +:clear +s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g +t quote +s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g +t quote +b any +:quote +s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g +s/\[/\\&/g +s/\]/\\&/g +s/\$/$$/g +H +:any +${ + g + s/^\n// + s/\n/ /g + p +} +' +DEFS=`sed -n "$ac_script" confdefs.h` + + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by tcejdb $as_me 1.0.0, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to the package provider." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +tcejdb config.status 1.0.0 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h | --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "tcejdb.pc") CONFIG_FILES="$CONFIG_FILES tcejdb.pc" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + + +eval set X " :F $CONFIG_FILES " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + +# Messages +printf '#================================================================\n' +printf '# Ready to make.\n' +printf '#================================================================\n' + + + +# END OF FILE diff --git a/tcejdb/configure.in b/tcejdb/configure.in new file mode 100644 index 0000000..a3b2679 --- /dev/null +++ b/tcejdb/configure.in @@ -0,0 +1,365 @@ +# Source of configuration for Tokyo Cabinet + + + +#================================================================ +# Generic Settings +#================================================================ + +test -n "$CFLAGS" && MYCFLAGS="$CFLAGS $MYCFLAGS" +test -n "$CPPFLAGS" && MYCPPFLAGS="$CPPFLAGS $MYCPPFLAGS" +test -n "$LDFLAGS" && MYLDFLAGS="$LDFLAGS $MYLDFLAGS" + +# Package name +AC_INIT(tcejdb, 1.0.0) + +# Package information +MYLIBVER=9 +MYLIBREV=11 +MYFORMATVER="1.0" + +# Targets +MYHEADERFILES="tcutil.h tchdb.h tcbdb.h tcfdb.h tctdb.h tcadb.h ejdb.h ejdefs.h bson.h" +MYLIBRARYFILES="libtcejdb.a" +MYLIBOBJFILES="tcutil.o tchdb.o tcbdb.o tcfdb.o tctdb.o tcadb.o myconf.o md5.o ejdb.o bson.o timsort.o numbers.o encoding.o" +MYCOMMANDFILES="tcutest tcumttest tcucodec tchtest tchmttest tchmgr" +MYCOMMANDFILES="$MYCOMMANDFILES tcbtest tcbmttest tcbmgr tcftest tcfmttest tcfmgr" +MYCOMMANDFILES="$MYCOMMANDFILES tcttest tctmttest tctmgr tcatest tcamttest tcamgr" +MYCGIFILES="tcawmgr.cgi" +MYMAN1FILES="tcutest.1 tcumttest.1 tcucodec.1 tchtest.1 tchmttest.1 tchmgr.1" +MYMAN1FILES="$MYMAN1FILES tcbtest.1 tcbmttest.1 tcbmgr.1 tcftest.1 tcfmttest.1 tcfmgr.1" +MYMAN1FILES="$MYMAN1FILES tcttest.1 tctmttest.1 tctmgr.1 tcatest.1 tcamttest.1 tcamgr.1" +MYMAN3FILES="tokyocabinet.3 tcutil.3 tcxstr.3 tclist.3 tcmap.3 tctree.3 tcmdb.3 tcmpool.3" +MYMAN3FILES="$MYMAN3FILES tchdb.3 tcbdb.3 tcfdb.3 tctdb.3 tcadb.3" +#MYDOCUMENTFILES="COPYING ChangeLog doc tokyocabinet.idl" +MYDOCUMENTFILES="COPYING ChangeLog" +MYPCFILES="tcejdb.pc" + +# Building flags +MYCFLAGS="$MYCFLAGS -std=c99 -Wall -fPIC -fsigned-char -O2" +MYCPPFLAGS="-I. -I\$(INCLUDEDIR) -I$HOME/include -I/usr/local/include" +MYCPPFLAGS="$MYCPPFLAGS -DNDEBUG -D_GNU_SOURCE=1 -D_REENTRANT -D__EXTENSIONS__" +MYLDFLAGS="-L. -L\$(LIBDIR) -L$HOME/lib -L/usr/local/lib" +MYCMDLDFLAGS="" +MYRUNPATH="\$(LIBDIR)" +MYLDLIBPATHENV="LD_LIBRARY_PATH" +MYPOSTCMD="true" + +# Building paths +PATH="$PATH:$HOME/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin" +PATH="$PATH:/opt/SUNWspro/bin:/usr/ccs/bin:/usr/xpg4/bin:/usr/xpg6/bin:/usr/ucb" +CPATH="$HOME/include:/usr/local/include:$CPATH" +LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LIBRARY_PATH" +LD_LIBRARY_PATH="$HOME/lib:/usr/local/lib:$LD_LIBRARY_PATH" +PKG_CONFIG_PATH="$HOME/lib/pkgconfig:/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH" +export PATH CPATH LIBRARY_PATH LD_LIBRARY_PATH PKG_CONFIG_PATH + + +#================================================================ +# Options +#================================================================ + + +# Internal variables +enables="" + +# Debug mode +AC_ARG_ENABLE(debug, + AC_HELP_STRING([--enable-debug], [build for debugging])) +if test "$enable_debug" = "yes" +then + MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -O0" + MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG" + MYCMDLDFLAGS="$MYCMDLDFLAGS -static" + enables="$enables (debug)" +fi + +# Developping mode +AC_ARG_ENABLE(devel, + AC_HELP_STRING([--enable-devel], [build for development])) +if test "$enable_devel" = "yes" +then + MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -O2 -pipe" + MYCPPFLAGS="$MYCPPFLAGS -UNDEBUG" + enables="$enables (devel)" +fi + +# Profiling mode +AC_ARG_ENABLE(profile, + AC_HELP_STRING([--enable-profile], [build for profiling])) +if test "$enable_profile" = "yes" +then + MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -g -pg -O2 -pipe" + enables="$enables (profile)" +fi + +# Static mode +AC_ARG_ENABLE(static, + AC_HELP_STRING([--enable-static], [build by static linking])) +if test "$enable_static" = "yes" +then + MYCMDLDFLAGS="$MYCMDLDFLAGS -static" + enables="$enables (static)" +fi + +# Fastest mode +AC_ARG_ENABLE(fastest, + AC_HELP_STRING([--enable-fastest], [build for fastest run])) +if test "$enable_fastest" = "yes" +then + MYLIBOBJFILES="tokyocabinet_all.o" + MYCFLAGS="-std=c99 -Wall -fPIC -pedantic -fsigned-char -O3" + MYCFLAGS="$MYCFLAGS -fomit-frame-pointer -fforce-addr -minline-all-stringops" + MYCPPFLAGS="$MYCPPFLAGS -D_MYFASTEST" + enables="$enables (fastest)" +fi + +# 64-bit offset mode +AC_ARG_ENABLE(off64, + AC_HELP_STRING([--enable-off64], [build with 64-bit file offset on 32-bit system])) +if test "$enable_off64" = "yes" +then + MYCPPFLAGS="$MYCPPFLAGS -D_FILE_OFFSET_BITS=64" + enables="$enables (off64)" +fi + +# Swapping byte-orders mode +AC_ARG_ENABLE(swab, + AC_HELP_STRING([--enable-swab], [build for swapping byte-orders])) +if test "$enable_swab" = "yes" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYSWAB" + enables="$enables (swab)" +fi + +# Micro yield mode +AC_ARG_ENABLE(uyield, + AC_HELP_STRING([--enable-uyield], [build for detecting race conditions])) +if test "$enable_uyield" = "yes" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYMICROYIELD" + enables="$enables (uyield)" +fi + +# Disable the unified buffer cache assumption +AC_ARG_ENABLE(ubc, + AC_HELP_STRING([--disable-ubc], [build without the unified buffer cache assumption])) +if test "$enable_ubc" = "no" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYNOUBC" + enables="$enables (no-ubc)" +fi + +# Disable ZLIB compression +AC_ARG_ENABLE(zlib, + AC_HELP_STRING([--disable-zlib], [build without ZLIB compression])) +if test "$enable_zlib" = "no" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYNOZLIB" + enables="$enables (no-zlib)" +fi + +# Disable BZIP2 compression +AC_ARG_ENABLE(bzip, + AC_HELP_STRING([--disable-bzip], [build without BZIP2 compression])) +if test "$enable_bzip" = "no" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYNOBZIP" + enables="$enables (no-bzip)" +fi + +# Disable POSIX thread +AC_ARG_ENABLE(pthread, + AC_HELP_STRING([--disable-pthread], [build without POSIX thread support])) +if test "$enable_pthread" = "no" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYNOPTHREAD" + enables="$enables (no-pthread)" +fi + +# Disable shared object +AC_ARG_ENABLE(shared, + AC_HELP_STRING([--disable-shared], [avoid to build shared libraries])) +if test "$enable_shared" = "no" +then + enables="$enables (no-shared)" +fi + +# Enable custom codec functions of LZMA +AC_ARG_ENABLE(exlzma, + AC_HELP_STRING([--disable-exlzma], [build with the custom codec of LZMA])) +if test "$enable_exlzma" = "yes" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYEXLZMA" + enables="$enables (exlzma)" +fi + +# Enable custom codec functions of LZO +AC_ARG_ENABLE(exlzo, + AC_HELP_STRING([--disable-exlzo], [build with the custom codec of LZO])) +if test "$enable_exlzo" = "yes" +then + MYCPPFLAGS="$MYCPPFLAGS -D_MYEXLZO" + enables="$enables (exlzo)" +fi + +# Specify the installation path of ZLIB +AC_ARG_WITH(zlib, + AC_HELP_STRING([--with-zlib=DIR], [search DIR/include and DIR/lib for ZLIB])) +if test -n "$with_zlib" +then + MYCPPFLAGS="$MYCPPFLAGS -I$with_zlib/include" + MYLDFLAGS="$MYLDFLAGS -L$with_zlib/lib" + MYRUNPATH="$MYRUNPATH:$with_zlib/lib" + CPATH="$CPATH:$with_zlib/include" + LIBRARY_PATH="$LIBRARY_PATH:$with_zlib/lib" + LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$with_zlib/lib" +fi + +# Specify the installation path of BZIP2 +AC_ARG_WITH(bzip, + AC_HELP_STRING([--with-bzip=DIR], [search DIR/include and DIR/lib for BZIP2])) +if test -n "$with_bzip" +then + MYCPPFLAGS="$MYCPPFLAGS -I$with_bzip/include" + MYLDFLAGS="$MYLDFLAGS -L$with_bzip/lib" + MYRUNPATH="$MYRUNPATH:$with_bzip/lib" + CPATH="$CPATH:$with_bzip/include" + LIBRARY_PATH="$LIBRARY_PATH:$with_bzip/lib" + LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$with_bzip/lib" +fi + +# Messages +printf '#================================================================\n' +printf '# Configuring Tokyo Cabinet version %s%s.\n' "$PACKAGE_VERSION" "$enables" +printf '#================================================================\n' + + + +#================================================================ +# Checking Commands and Libraries +#================================================================ + +# C compiler +AC_PROG_CC + +# Reset variables +if test "$GCC" != "yes" +then + AC_MSG_WARN([another compiler except for GCC was detected]) + MYCFLAGS="" +fi + +# Byte order +AC_C_BIGENDIAN(MYCPPFLAGS="$MYCPPFLAGS -D_MYBIGEND") + +# Underlying libraries +AC_CHECK_LIB(c, main) +AC_CHECK_LIB(m, main) +if test "$enable_pthread" != "no" +then + AC_CHECK_LIB(pthread, main) + AC_CHECK_LIB(rt, main) +fi +if test "$enable_zlib" != "no" +then + AC_CHECK_LIB(z, main) +fi +if test "$enable_bzip" != "no" +then + AC_CHECK_LIB(bz2, main) +fi +if test "$enable_exlzma" = "yes" +then + AC_CHECK_LIB(lzma, main) +fi +if test "$enable_exlzo" = "yes" +then + AC_CHECK_LIB(lzo2, main) +fi +AC_CHECK_LIB(tcejdb, main, AC_MSG_WARN([old version of EJDB was found])) + +AC_CHECK_LIB(cunit, main, true, AC_MSG_ERROR([CUnit lubrary is required])) + +# Necessary headers +AC_CHECK_HEADER(stdlib.h, true, AC_MSG_ERROR([stdlib.h is required])) +AC_CHECK_HEADER(stdint.h, true, AC_MSG_ERROR([stdint.h is required])) +AC_CHECK_HEADER(unistd.h, true, AC_MSG_ERROR([unistd.h is required])) +AC_CHECK_HEADER(dirent.h, true, AC_MSG_ERROR([dirent.h is required])) +AC_CHECK_HEADER(regex.h, true, AC_MSG_ERROR([regex.h is required])) +AC_CHECK_HEADER(glob.h, true, AC_MSG_ERROR([glob.h is required])) +if test "$enable_pthread" != "no" +then + AC_CHECK_HEADER(pthread.h, true, AC_MSG_ERROR([pthread.h is required])) +fi +if test "$enable_zlib" != "no" +then + AC_CHECK_HEADER(zlib.h, true, AC_MSG_ERROR([zlib.h is required])) +fi +if test "$enable_bzip" != "no" +then + AC_CHECK_HEADER(bzlib.h, true, AC_MSG_ERROR([bzlib.h is required])) +fi +if test "$enable_exlzma" = "yes" +then + AC_CHECK_HEADER(lzmalib.h, true, AC_MSG_ERROR([lzmalib.h is required])) +fi +if test "$enable_exlzo" = "yes" +then + AC_CHECK_HEADER(lzo/lzo1x.h, true, AC_MSG_ERROR([lzo/lzo1x.h is required])) +fi + +# Shared libraries +if test "$enable_shared" != "no" && test "$enable_profile" != "yes" +then + if uname | grep Darwin >/dev/null + then + MYLIBRARYFILES="$MYLIBRARYFILES libtcejdb.$MYLIBVER.$MYLIBREV.0.dylib" + MYLIBRARYFILES="$MYLIBRARYFILES libtcejdb.$MYLIBVER.dylib" + MYLIBRARYFILES="$MYLIBRARYFILES libtcejdb.dylib" + MYLDLIBPATHENV="DYLD_LIBRARY_PATH" + else + MYLIBRARYFILES="$MYLIBRARYFILES libtcejdb.so.$MYLIBVER.$MYLIBREV.0" + MYLIBRARYFILES="$MYLIBRARYFILES libtcejdb.so.$MYLIBVER" + MYLIBRARYFILES="$MYLIBRARYFILES libtcejdb.so" + fi +fi + + + +#================================================================ +# Generic Settings +#================================================================ + +# Export variables +AC_SUBST(MYLIBVER) +AC_SUBST(MYLIBREV) +AC_SUBST(MYFORMATVER) +AC_SUBST(MYHEADERFILES) +AC_SUBST(MYLIBRARYFILES) +AC_SUBST(MYLIBOBJFILES) +AC_SUBST(MYCOMMANDFILES) +AC_SUBST(MYCGIFILES) +AC_SUBST(MYMAN1FILES) +AC_SUBST(MYMAN3FILES) +AC_SUBST(MYDOCUMENTFILES) +AC_SUBST(MYPCFILES) +AC_SUBST(MYCFLAGS) +AC_SUBST(MYCPPFLAGS) +AC_SUBST(MYLDFLAGS) +AC_SUBST(MYCMDLDFLAGS) +AC_SUBST(MYRUNPATH) +AC_SUBST(MYLDLIBPATHENV) +AC_SUBST(MYPOSTCMD) + +# Targets +AC_OUTPUT(Makefile tcejdb.pc) + +# Messages +printf '#================================================================\n' +printf '# Ready to make.\n' +printf '#================================================================\n' + + + +# END OF FILE diff --git a/tcejdb/doc/benchmark.pdf b/tcejdb/doc/benchmark.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9ad82d73204980442ff1d603d401ef0a0f00b6dd GIT binary patch literal 33444 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 ew$S~rZ{*}JLPs$|HCQMCYLR~H)q+) z4V%`_+xBABp_d0vTsVL6+<~hfPhI@<>dlL1Z+`sw_nU*8?Y_8nJJ1%!k|4ie28U-i z(mZ0qy)UwR?G Y%;-&?#_10BfV>FVdQ&MBb@0NLE1Y5)KL literal 0 HcmV?d00001 diff --git a/tcejdb/doc/index.html b/tcejdb/doc/index.html new file mode 100644 index 0000000..1440a08 --- /dev/null +++ b/tcejdb/doc/index.html @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + +Tokyo Cabinet: a modern implementation of DBM + + + + + +

Tokyo Cabinet: a modern implementation of DBM

+ +
Copyright (C) 2006-2012 FAL Labs
+
Last Update: Sat, 18 Aug 2012 11:05:00 +0900
+ + + + +
+ +

Overview

+ +

Tokyo Cabinet is a library of routines for managing a database. The database is a simple data file containing records, each is a pair of a key and a value. Every key and value is serial bytes with variable length. Both binary data and character string can be used as a key and a value. There is neither concept of data tables nor data types. Records are organized in hash table, B+ tree, or fixed-length array.

+ +

Tokyo Cabinet is developed as the successor of GDBM and QDBM on the following purposes. They are achieved and Tokyo Cabinet replaces conventional DBM products.

+ +
    +
  • improves space efficiency : smaller size of database file.
  • +
  • improves time efficiency : faster processing speed.
  • +
  • improves parallelism : higher performance in multi-thread environment.
  • +
  • improves usability : simplified API.
  • +
  • improves robustness : database file is not corrupted even under catastrophic situation.
  • +
  • supports 64-bit architecture : enormous memory space and database file are available.
  • +
+ +

Tokyo Cabinet is written in the C language, and provided as API of C, Perl, Ruby, Java, and Lua. Tokyo Cabinet is available on platforms which have API conforming to C99 and POSIX. Tokyo Cabinet is a free software licensed under the GNU Lesser General Public License.

+ +
+ +

Documents

+ +

The following are documents of Tokyo Cabinet. They are contained also in the source package.

+ + + + + + + +
+ +

Packages

+ +

The following are the source packages of Tokyo Cabinet. As for binary packages, see the site of each distributor.

+ + + + + + + +
+ +

Information

+ +

Tokyo Cabinet was written and is maintained by FAL Labs. You can contact the author by e-mail to `info@fallabs.com'.

+ +

The following are sibling projects of Tokyo Cabinet.

+ + + + + +
+ + + + + + diff --git a/tcejdb/doc/index.ja.html b/tcejdb/doc/index.ja.html new file mode 100644 index 0000000..60f2863 --- /dev/null +++ b/tcejdb/doc/index.ja.html @@ -0,0 +1,200 @@ + + + + + + + + + + + + + + + + + + + +データベースマネージャ Tokyo Cabinet + + + + + +

Tokyo Cabinet: DBMの現代的な壱実装

+ +
Copyright (C) 2006-2012 FAL Labs
+
Last Update: Sat, 18 Aug 2012 11:05:00 +0900
+ + + + +
+ +

概要

+ +

Tokyo Cabinetはデータベースを扱うルーチン群のライブラリです。データベースといっても単純なもので、キーと値のペアからなるレコード群を格納したデータファイルです。キーと値は任意の長さを持つ一連のバイト列であり、文字列でもバイナリでも扱うことができます。テーブルやデータ型の概念はありません。レコードはハッシュ表かB+木か固定長配列で編成されます。

+ +

Tokyo CabinetはGDBMやQDBMの後継として次の点を目標として開発されました。これらの目標は達成されており、Tokyo Cabinetは従来のDBMを置き換える製品だと言えます。

+ +
    +
  • 空間効率の向上 : データベースファイルがより小さい
  • +
  • 時間効率の向上 : 処理がより高速である
  • +
  • 並列性の向上 : マルチスレッド環境での同時実行性能の向上
  • +
  • 利便性の向上 : APIがより単純である
  • +
  • 堅牢性の向上 : 不慮の事態でもデータベースファイルが壊れにくい
  • +
  • 64ビット対応 : 巨大なメモリ空間とデータベースファイルを扱える
  • +
+ +

Tokyo CabinetはC言語で記述され、CとPerlとRubyとJavaとLuaのAPIとして提供されます。Tokyo CabinetはC99およびPOSIX準拠のAPIを備えるプラットフォームで利用できます。Tokyo CabinetはGNU Lesser General Public Licenseに基づくフリーソフトウェアです。

+ +
+ +

文書

+ +

以下の文書を読んでください。ソースパッケージにも同じものが含まれています。

+ + + + + + + +
+ +

ダウンロード

+ +

以下のソースパッケージをダウンロードしてください。バイナリパッケージについては、各ディストリビュータのサイトをご覧ください。

+ + + + + + + +
+ +

その他の情報

+ +

Tokyo CabinetはFAL Labsが作成しました。作者と連絡をとるには、`info@fallabs.com' 宛に電子メールを送ってください。

+ +

Tokyo Cabinetの兄弟プロジェクトもあります。

+ + + + + +
+ + + + + + diff --git a/tcejdb/doc/logo-ja.png b/tcejdb/doc/logo-ja.png new file mode 100644 index 0000000000000000000000000000000000000000..7c80a1d4c0029301062fb54f57fa6b3c54228214 GIT binary patch literal 2094 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 + + + + + + + + + + + + + + + + + +Fundamental Specifications of Tokyo Cabinet Version 1 + + + + +

Fundamental Specifications of Tokyo Cabinet Version 1

+ +
Copyright (C) 2006-2012 FAL Labs
+
Last Update: Sat, 18 Aug 2012 11:05:00 +0900
+ + +
+ +

Table of Contents

+ +
    +
  1. Introduction
  2. +
  3. Features
  4. +
  5. Installation
  6. +
  7. The Utility API
  8. +
  9. The Hash Database API
  10. +
  11. The B+ Tree Database API
  12. +
  13. The Fixed-length Database API
  14. +
  15. The Table Database API
  16. +
  17. The Abstract Database API
  18. +
  19. File Format
  20. +
  21. License
  22. +
+ +
+ +

Introduction

+ +

Tokyo Cabinet is a library of routines for managing a database. The database is a simple data file containing records, each is a pair of a key and a value. Every key and value is serial bytes with variable length. Both binary data and character string can be used as a key and a value. There is neither concept of data tables nor data types. Records are organized in hash table, B+ tree, or fixed-length array.

+ +

As for database of hash table, each key must be unique within a database, so it is impossible to store two or more records with a key overlaps. The following access methods are provided to the database: storing a record with a key and a value, deleting a record by a key, retrieving a record by a key. Moreover, traversal access to every key are provided, although the order is arbitrary. These access methods are similar to ones of DBM (or its followers: NDBM and GDBM) library defined in the UNIX standard. Tokyo Cabinet is an alternative for DBM because of its higher performance.

+ +

As for database of B+ tree, records whose keys are duplicated can be stored. Access methods of storing, deleting, and retrieving are provided as with the database of hash table. Records are stored in order by a comparison function assigned by a user. It is possible to access each record with the cursor in ascending or descending order. According to this mechanism, forward matching search for strings and range search for integers are realized.

+ +

As for database of fixed-length array, records are stored with unique natural numbers. It is impossible to store two or more records with a key overlaps. Moreover, the length of each record is limited by the specified length. Provided operations are the same as ones of hash database.

+ +

Table database is also provided as a variant of hash database. Each record is identified by the primary key and has a set of named columns. Although there is no concept of data schema, it is possible to search for records with complex conditions efficiently by using indices of arbitrary columns.

+ +

Tokyo Cabinet is written in the C language, and provided as API of C, Perl, Ruby, Java, and Lua. Tokyo Cabinet is available on platforms which have API conforming to C99 and POSIX. Tokyo Cabinet is a free software licensed under the GNU Lesser General Public License.

+ +
+ +

Features

+ +

Tokyo Cabinet is the successor of QDBM and improves time and space efficiency. This section describes the features of Tokyo Cabinet.

+ +

The Dinosaur Wing of the DBM Forks

+ +

Tokyo Cabinet is developed as the successor of GDBM and QDBM on the following purposes. They are achieved and Tokyo Cabinet replaces conventional DBM products.

+ +
    +
  • improves space efficiency : smaller size of database file.
  • +
  • improves time efficiency : faster processing speed.
  • +
  • improves parallelism : higher performance in multi-thread environment.
  • +
  • improves usability : simplified API.
  • +
  • improves robustness : database file is not corrupted even under catastrophic situation.
  • +
  • supports 64-bit architecture : enormous memory space and database file are available.
  • +
+ +

As with QDBM, the following three restrictions of traditional DBM: a process can handle only one database, the size of a key and a value is bounded, a database file is sparse, are cleared. Moreover, the following three restrictions of QDBM: the size of a database file is limited to 2GB, environments with different byte orders can not share a database file, only one thread can search a database at the same time, are cleared.

+ +

Tokyo Cabinet runs very fast. For example, elapsed time to store 1 million records is 0.7 seconds for hash database, and 1.6 seconds for B+ tree database. Moreover, the size of database of Tokyo Cabinet is very small. For example, overhead for a record is 16 bytes for hash database, and 5 bytes for B+ tree database. Furthermore, scalability of Tokyo Cabinet is great. The database size can be up to 8EB (9.22e18 bytes).

+ +

Effective Implementation of Hash Database

+ +

Tokyo Cabinet uses hash algorithm to retrieve records. If a bucket array has sufficient number of elements, the time complexity of retrieval is "O(1)". That is, time required for retrieving a record is constant, regardless of the scale of a database. It is also the same about storing and deleting. Collision of hash values is managed by separate chaining. Data structure of the chains is binary search tree. Even if a bucket array has unusually scarce elements, the time complexity of retrieval is "O(log n)".

+ +

Tokyo Cabinet attains improvement in retrieval by loading RAM with the whole of a bucket array. If a bucket array is on RAM, it is possible to access a region of a target record by about one path of file operations. A bucket array saved in a file is not read into RAM with the `read' call but directly mapped to RAM with the `mmap' call. Therefore, preparation time on connecting to a database is very short, and two or more processes can share the same memory map.

+ +

If the number of elements of a bucket array is about half of records stored within a database, although it depends on characteristic of the input, the probability of collision of hash values is about 56.7% (36.8% if the same, 21.3% if twice, 11.5% if four times, 6.0% if eight times). In such case, it is possible to retrieve a record by two or less paths of file operations. If it is made into a performance index, in order to handle a database containing one million of records, a bucket array with half a million of elements is needed. The size of each element is 4 bytes. That is, if 2M bytes of RAM is available, a database containing one million records can be handled.

+ +

Traditional DBM provides two modes of the storing operations: "insert" and "replace". In the case a key overlaps an existing record, the insert mode keeps the existing value, while the replace mode transposes it to the specified value. In addition to the two modes, Tokyo Cabinet provides "concatenate" mode. In the mode, the specified value is concatenated at the end of the existing value and stored. This feature is useful when adding an element to a value as an array.

+ +

Generally speaking, while succession of updating, fragmentation of available regions occurs, and the size of a database grows rapidly. Tokyo Cabinet deal with this problem by coalescence of dispensable regions and reuse of them. When overwriting a record with a value whose size is greater than the existing one, it is necessary to remove the region to another position of the file. Because the time complexity of the operation depends on the size of the region of a record, extending values successively is inefficient. However, Tokyo Cabinet deal with this problem by alignment. If increment can be put in padding, it is not necessary to remove the region.

+ +

The "free block pool" to reuse dispensable regions efficiently is also implemented. It keeps a list of dispensable regions and reuse the "best fit" region, that is the smallest region in the list, when a new block is requested. Because fragmentation is inevitable even then, two kinds of optimization (defragmentation) mechanisms are implemented. The first is called static optimization which deploys all records into another file and then writes them back to the original file at once. The second is called dynamic optimization which gathers up dispensable regions by replacing the locations of records and dispensable regions gradually.

+ +

Useful Implementation of B+ Tree Database

+ +

Although B+ tree database is slower than hash database, it features ordering access to each record. The order can be assigned by users. Records of B+ tree are sorted and arranged in logical pages. Sparse index organized in B tree that is multiway balanced tree are maintained for each page. Thus, the time complexity of retrieval and so on is "O(log n)". Cursor is provided to access each record in order. The cursor can jump to a position specified by a key and can step forward or backward from the current position. Because each page is arranged as double linked list, the time complexity of stepping cursor is "O(1)".

+ +

B+ tree database is implemented, based on the above hash database. Because each page of B+ tree is stored as each record of hash database, B+ tree database inherits efficiency of storage management of hash database. Because the header of each record is smaller and alignment of each page is adjusted according to the page size, in most cases, the size of database file is cut by half compared to one of hash database. Although operation of many pages are required to update B+ tree, Tokyo Cabinet expedites the process by caching pages and reducing file operations. In most cases, because whole of the sparse index is cached on memory, it is possible to retrieve a record by one or less path of file operations.

+ +

Each pages of B+ tree can be stored with compressed. Two compression method; Deflate of ZLIB and Block Sorting of BZIP2, are supported. Because each record in a page has similar patterns, high efficiency of compression is expected due to the Lempel-Ziv or the BWT algorithms. In case handling text data, the size of a database is reduced to about 25%. If the scale of a database is large and disk I/O is the bottleneck, featuring compression makes the processing speed improved to a large extent.

+ +

Naive Implementation of Fixed-length Database

+ +

Fixed-length database has restrictions that each key should be a natural number and that the length of each value is limited. However, time efficiency and space efficiency are higher than the other data structures as long as the use case is within the restriction.

+ +

Because the whole region of the database is mapped on memory by the `mmap' call and referred as a multidimensional array, the overhead related to the file I/O is minimized. Due to this simple structure, fixed-length database works faster than hash database, and its concurrency in multi-thread environment is prominent.

+ +

The size of the database is proportional to the range of keys and the limit size of each value. That is, the smaller the range of keys is or the smaller the length of each value is, the higher the space efficiency is. For example, if the maximum key is 1000000 and the limit size of the value is 100 bytes, the size of the database will be about 100MB. Because regions around referred records are only loaded on the RAM, you can increase the size of the database to the size of the virtual memory.

+ +

Flexible Implementation of Table Database

+ +

Table database does not express simple key/value structure but expresses a structure like a table of relational database. Each record is identified by the primary key and has a set of multiple columns named with arbitrary strings. For example, a stuff in your company can be expressed by a record identified by the primary key of the employee ID number and structured by columns of his name, division, salary, and so on. Unlike relational database, table database does not need to define any data schema and can contain records of various structures different from each other.

+ +

Table database supports query functions with not only the primary key but also with conditions about arbitrary columns. Each column condition is composed of the name of a column and a condition expression. Operators of full matching, forward matching, regular expression matching, and so on are provided for the string type. Operators of full matching, range matching and so on are provided for the number type. Operators for tag search and full-text search are also provided. A query can contain multiple conditions for logical intersection. Search by multiple queries for logical union is also available. The order of the result set can be specified as the ascending or descending order of strings or numbers.

+ +

You can create indices for arbitrary columns to improve performance of search and sorting. Although columns do not have data types, indices have types for strings or numbers. Inverted indices for space separated tokens and character N-gram tokens are also supported. The query optimizer uses indices in suitable way according to each query. Indices are implemented as different files of B+ tree database.

+ +

Practical Functionality

+ +

Databases on the filesystem feature transaction mechanisms. It is possible to commit a series of operations between the beginning and the end of the transaction in a lump, or to abort the transaction and perform rollback to the state before the transaction. Two isolation levels are supported; serializable and read uncommitted. Durability is secured by write ahead logging and shadow paging.

+ +

Tokyo Cabinet provides two modes to connect to a database: "reader" and "writer". A reader can perform retrieving but neither storing nor deleting. A writer can perform all access methods. Exclusion control between processes is performed when connecting to a database by file locking. While a writer is connected to a database, neither readers nor writers can be connected. While a reader is connected to a database, other readers can be connect, but writers can not. According to this mechanism, data consistency is guaranteed with simultaneous connections in multitasking environment.

+ +

Functions of API of Tokyo cabinet are reentrant and available in multi-thread environment. Discrete database object can be operated in parallel entirely. For simultaneous operations of the same database object, read-write lock is used for exclusion control. That is, while a writing thread is operating the database, other reading threads and writing threads are blocked. However, while a reading thread is operating the database, reading threads are not blocked. The locking granularity of hash database and fixed-length database is per record, and that of the other databases is per file.

+ +

Simple but Various Interfaces

+ +

Tokyo Cabinet provides simple API based on the object oriented design. Every operation for database is encapsulated and published as lucid methods as `open' (connect), `close' (disconnect), `put' (insert), `out' (remove), `get' (retrieve), and so on. Because the three of hash, B+ tree, and fixed-length array database APIs are very similar with each other, porting an application from one to the other is easy. Moreover, the abstract API is provided to handle these databases with the same interface. Applications of the abstract API can determine the type of the database in runtime.

+ +

The utility API is also provided. Such fundamental data structure as list and map are included. And, some useful features; memory pool, string processing, encoding, are also included.

+ +

Six kinds of API; the utility API, the hash database API, the B+ tree database API, the fixed-length database API, the table database API, and the abstract database API, are provided for the C language. Command line interfaces are also provided corresponding to each API. They are useful for prototyping, test, and debugging. Except for C, Tokyo Cabinet provides APIs for Perl, Ruby, Java, and Lua. APIs for other languages will hopefully be provided by third party.

+ +

In cases that multiple processes access a database at the same time or some processes access a database on a remote host, the remote service is useful. The remote service is composed of a database server and its access library. Applications can access the database server by using the remote database API. The server implements HTTP and the memcached protocol partly so that client programs on almost all platforms can access the server easily.

+ +
+ +

Installation

+ +

This section describes how to install Tokyo Cabinet with the source package. As for a binary package, see its installation manual.

+ +

Preparation

+ +

Tokyo Cabinet is available on UNIX-like systems. At least, the following environments are supported.

+ +
    +
  • Linux 2.4 and later (x86-32/x86-64/PowerPC/Alpha/SPARC)
  • +
  • Mac OS X 10.3 and later (x86-32/x86-64/PowerPC)
  • +
+ +

gcc 3.1 or later and make are required to install Tokyo Cabinet with the source package. They are installed by default on Linux, FreeBSD and so on.

+ +

As Tokyo Cabinet depends on the following libraries, install them beforehand.

+ +
    +
  • zlib : for loss-less data compression. 1.2.3 or later is suggested.
  • +
  • bzip2 : for loss-less data compression. 1.0.5 or later is suggested.
  • +
+ +

Installation

+ +

When an archive file of Tokyo Cabinet is extracted, change the current working directory to the generated directory and perform installation.

+ +

Run the configuration script.

+ +
./configure
+
+ +

Build programs.

+ +
make
+
+ +

Perform self-diagnostic test.

+ +
make check
+
+ +

Install programs. This operation must be carried out by the root user.

+ +
make install
+
+ +

Result

+ +

When a series of work finishes, the following files will be installed.

+ +
/usr/local/include/tcutil.h
+/usr/local/include/tchdb.h
+/usr/local/include/tcbdb.h
+/usr/local/include/tcfdb.h
+/usr/local/include/tctdb.h
+/usr/local/include/tcadb.h
+/usr/local/lib/libtokyocabinet.a
+/usr/local/lib/libtokyocabinet.so.x.y.z
+/usr/local/lib/libtokyocabinet.so.x
+/usr/local/lib/libtokyocabinet.so
+/usr/local/lib/pkgconfig/tokyocabinet.pc
+/usr/local/bin/tcutest
+/usr/local/bin/tcumttest
+/usr/local/bin/tcucodec
+/usr/local/bin/tchtest
+/usr/local/bin/tchmttest
+/usr/local/bin/tchmgr
+/usr/local/bin/tcbmgr
+/usr/local/bin/tcbtest
+/usr/local/bin/tcbmttest
+/usr/local/bin/tcftest
+/usr/local/bin/tcfmttest
+/usr/local/bin/tcfmgr
+/usr/local/bin/tcttest
+/usr/local/bin/tctmttest
+/usr/local/bin/tctmgr
+/usr/local/bin/tcatest
+/usr/local/bin/tcamttest
+/usr/local/bin/tcamgr
+/usr/local/libexec/tcawmgr.cgi
+/usr/local/share/tokyocabinet/...
+/usr/local/man/man1/...
+/usr/local/man/man3/...
+
+ +

Options of Configure

+ +

The following options can be specified with `./configure'.

+ +
    +
  • --enable-debug : build for debugging. Enable debugging symbols, do not perform optimization, and perform static linking.
  • +
  • --enable-devel : build for development. Enable debugging symbols, perform optimization, and perform dynamic linking.
  • +
  • --enable-profile : build for profiling. Enable profiling symbols, perform optimization, and perform dynamic linking.
  • +
  • --enable-static : build by static linking.
  • +
  • --enable-fastest : build for fastest run.
  • +
  • --enable-off64 : build with 64-bit file offset on 32-bit system.
  • +
  • --enable-swab : build for swapping byte-orders.
  • +
  • --enable-uyield : build for detecting race conditions.
  • +
  • --disable-zlib : build without ZLIB compression.
  • +
  • --disable-bzip : build without BZIP2 compression.
  • +
  • --disable-pthread : build without POSIX thread support.
  • +
  • --disable-shared : avoid to build shared libraries.
  • +
+ +

`--prefix' and other options are also available as with usual UNIX software packages. If you want to install Tokyo Cabinet under `/usr' not `/usr/local', specify `--prefix=/usr'. As well, the library search path does not include `/usr/local/lib', it is necessary to set the environment variable `LD_LIBRARY_PATH' to include `/usr/local/lib' before running applications of Tokyo Cabinet.

+ +

How to Use the Library

+ +

Tokyo Cabinet provides API of the C language and it is available by programs conforming to the C89 (ANSI C) standard or the C99 standard. As the header files of Tokyo Cabinet are provided as `tcutil.h', `tchdb.h', `tcbdb.h', and `tcadb.h', applications should include one or more of them accordingly to use the API. As the library is provided as `libtokyocabinet.a' and `libtokyocabinet.so' and they depend on `libz.so', `libbz2.so', `librt.so', `libpthread.so', `libm.so', and `libc.so', linker options corresponding to them are required by the build command. The typical build command is the following.

+ +
gcc -I/usr/local/include tc_example.c -o tc_example \
+  -L/usr/local/lib -ltokyocabinet -lz -lbz2 -lrt -lpthread -lm -lc
+
+ +

You can also use Tokyo Cabinet in programs written in C++. Because each header is wrapped in C linkage (`extern "C"' block), you can simply include them into your C++ programs.

+ +
+ +

The Utility API

+ +

The utility API is a set of routines to handle records on memory easily. Especially, extensible string, array list, hash map, and ordered tree are useful. See `tcutil.h' for the entire specification.

+ +

Description

+ +

To use the utility API, include `tcutil.h' and related standard header files. Usually, write the following description near the front of a source file.

+ +
+
#include <tcutil.h>
+
#include <stdlib.h>
+
#include <stdbool.h>
+
#include <stdint.h>
+
+ +

Objects whose type is pointer to `TCXSTR' are used for extensible string. An extensible string object is created with the function `tcxstrnew' and is deleted with the function `tcxstrdel'. Objects whose type is pointer to `TCLIST' are used for array list. A list object is created with the function `tclistnew' and is deleted with the function `tclistdel'. Objects whose type is pointer to `TCMAP' are used for hash map. A map object is created with the function `tcmapnew' and is deleted with the function `tcmapdel'. Objects whose type is pointer to `TCTREE' are used for ordered tree. A tree object is created with the function `tctreenew' and is deleted with the function `tctreedel'. To avoid memory leak, it is important to delete every object when it is no longer in use.

+ +

API of Basic Utilities

+ +

The constant `tcversion' is the string containing the version information.

+ +
+
extern const char *tcversion;
+
+ +

The variable `tcfatalfunc' is the pointer to the call back function for handling a fatal error.

+ +
+
extern void (*tcfatalfunc)(const char *);
+
The argument specifies the error message.
+
The initial value of this variable is `NULL'. If the value is `NULL', the default function is called when a fatal error occurs. A fatal error occurs when memory allocation is failed.
+
+ +

The function `tcmalloc' is used in order to allocate a region on memory.

+ +
+
void *tcmalloc(size_t size);
+
`size' specifies the size of the region.
+
The return value is the pointer to the allocated region.
+
This function handles failure of memory allocation implicitly. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tccalloc' is used in order to allocate a nullified region on memory.

+ +
+
void *tccalloc(size_t nmemb, size_t size);
+
`nmemb' specifies the number of elements.
+
`size' specifies the size of each element.
+
The return value is the pointer to the allocated nullified region.
+
This function handles failure of memory allocation implicitly. Because the region of the return value is allocated with the `calloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcrealloc' is used in order to re-allocate a region on memory.

+ +
+
void *tcrealloc(void *ptr, size_t size);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
The return value is the pointer to the re-allocated region.
+
This function handles failure of memory allocation implicitly. Because the region of the return value is allocated with the `realloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmemdup' is used in order to duplicate a region on memory.

+ +
+
void *tcmemdup(const void *ptr, size_t size);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
The return value is the pointer to the allocated region of the duplicate.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcstrdup' is used in order to duplicate a string on memory.

+ +
+
char *tcstrdup(const void *str);
+
`str' specifies the string.
+
The return value is the allocated string equivalent to the specified string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcfree' is used in order to free a region on memory.

+ +
+
void tcfree(void *ptr);
+
`ptr' specifies the pointer to the region. If it is `NULL', this function has no effect.
+
Although this function is just a wrapper of `free' call, this is useful in applications using another package of the `malloc' series.
+
+ +

API of Extensible String

+ +

The function `tcxstrnew' is used in order to create an extensible string object.

+ +
+
TCXSTR *tcxstrnew(void);
+
The return value is the new extensible string object.
+
+ +

The function `tcxstrnew2' is used in order to create an extensible string object from a character string.

+ +
+
TCXSTR *tcxstrnew2(const char *str);
+
`str' specifies the string of the initial content.
+
The return value is the new extensible string object containing the specified string.
+
+ +

The function `tcxstrnew3' is used in order to create an extensible string object with the initial allocation size.

+ +
+
TCXSTR *tcxstrnew3(int asiz);
+
`asiz' specifies the initial allocation size.
+
The return value is the new extensible string object.
+
+ +

The function `tcxstrdup' is used in order to copy an extensible string object.

+ +
+
TCXSTR *tcxstrdup(const TCXSTR *xstr);
+
`xstr' specifies the extensible string object.
+
The return value is the new extensible string object equivalent to the specified object.
+
+ +

The function `tcxstrdel' is used in order to delete an extensible string object.

+ +
+
void tcxstrdel(TCXSTR *xstr);
+
`xstr' specifies the extensible string object.
+
Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tcxstrcat' is used in order to concatenate a region to the end of an extensible string object.

+ +
+
void tcxstrcat(TCXSTR *xstr, const void *ptr, int size);
+
`xstr' specifies the extensible string object.
+
`ptr' specifies the pointer to the region to be appended.
+
`size' specifies the size of the region.
+
+ +

The function `tcxstrcat2' is used in order to concatenate a character string to the end of an extensible string object.

+ +
+
void tcxstrcat2(TCXSTR *xstr, const char *str);
+
`xstr' specifies the extensible string object.
+
`str' specifies the string to be appended.
+
+ +

The function `tcxstrptr' is used in order to get the pointer of the region of an extensible string object.

+ +
+
const void *tcxstrptr(const TCXSTR *xstr);
+
`xstr' specifies the extensible string object.
+
The return value is the pointer of the region of the object.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string.
+
+ +

The function `tcxstrsize' is used in order to get the size of the region of an extensible string object.

+ +
+
int tcxstrsize(const TCXSTR *xstr);
+
`xstr' specifies the extensible string object.
+
The return value is the size of the region of the object.
+
+ +

The function `tcxstrclear' is used in order to clear an extensible string object.

+ +
+
void tcxstrclear(TCXSTR *xstr);
+
`xstr' specifies the extensible string object.
+
The internal buffer of the object is cleared and the size is set zero.
+
+ +

The function `tcxstrprintf' is used in order to perform formatted output into an extensible string object.

+ +
+
void tcxstrprintf(TCXSTR *xstr, const char *format, ...);
+
`xstr' specifies the extensible string object.
+
`format' specifies the printf-like format string. The conversion character `%' can be used with such flag characters as `s', `d', `o', `u', `x', `X', `c', `e', `E', `f', `g', `G', `@', `?', `b', and `%'. `@' works as with `s' but escapes meta characters of XML. `?' works as with `s' but escapes meta characters of URL. `b' converts an integer to the string as binary numbers. The other conversion character work as with each original.
+
The other arguments are used according to the format string.
+
+ +

The function `tcsprintf' is used in order to allocate a formatted string on memory.

+ +
+
char *tcsprintf(const char *format, ...);
+
`format' specifies the printf-like format string. The conversion character `%' can be used with such flag characters as `s', `d', `o', `u', `x', `X', `c', `e', `E', `f', `g', `G', `@', `?', `b', and `%'. `@' works as with `s' but escapes meta characters of XML. `?' works as with `s' but escapes meta characters of URL. `b' converts an integer to the string as binary numbers. The other conversion character work as with each original.
+
The other arguments are used according to the format string.
+
The return value is the pointer to the region of the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

API of Array List

+ +

The function `tclistnew' is used in order to create a list object.

+ +
+
TCLIST *tclistnew(void);
+
The return value is the new list object.
+
+ +

The function `tclistnew2' is used in order to create a list object with expecting the number of elements.

+ +
+
TCLIST *tclistnew2(int anum);
+
`anum' specifies the number of elements expected to be stored in the list.
+
The return value is the new list object.
+
+ +

The function `tclistnew3' is used in order to create a list object with initial string elements.

+ +
+
TCLIST *tclistnew3(const char *str, ...);
+
`str' specifies the string of the first element.
+
The other arguments are other elements. They should be trailed by a `NULL' argument.
+
The return value is the new list object.
+
+ +

The function `tclistdup' is used in order to copy a list object.

+ +
+
TCLIST *tclistdup(const TCLIST *list);
+
`list' specifies the list object.
+
The return value is the new list object equivalent to the specified object.
+
+ +

The function `tclistdel' is used in order to delete a list object.

+ +
+
void tclistdel(TCLIST *list);
+
`list' specifies the list object.
+
Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tclistnum' is used in order to get the number of elements of a list object.

+ +
+
int tclistnum(const TCLIST *list);
+
`list' specifies the list object.
+
The return value is the number of elements of the list.
+
+ +

The function `tclistval' is used in order to get the pointer to the region of an element of a list object.

+ +
+
const void *tclistval(const TCLIST *list, int index, int *sp);
+
`list' specifies the list object.
+
`index' specifies the index of the element.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the value.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. If `index' is equal to or more than the number of elements, the return value is `NULL'.
+
+ +

The function `tclistval2' is used in order to get the string of an element of a list object.

+ +
+
const char *tclistval2(const TCLIST *list, int index);
+
`list' specifies the list object.
+
`index' specifies the index of the element.
+
The return value is the string of the value.
+
If `index' is equal to or more than the number of elements, the return value is `NULL'.
+
+ +

The function `tclistpush' is used in order to add an element at the end of a list object.

+ +
+
void tclistpush(TCLIST *list, const void *ptr, int size);
+
`list' specifies the list object.
+
`ptr' specifies the pointer to the region of the new element.
+
`size' specifies the size of the region.
+
+ +

The function `tclistpush2' is used in order to add a string element at the end of a list object.

+ +
+
void tclistpush2(TCLIST *list, const char *str);
+
`list' specifies the list object.
+
`str' specifies the string of the new element.
+
+ +

The function `tclistpop' is used in order to remove an element of the end of a list object.

+ +
+
void *tclistpop(TCLIST *list, int *sp);
+
`list' specifies the list object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the removed element.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. If the list is empty, the return value is `NULL'.
+
+ +

The function `tclistpop2' is used in order to remove a string element of the end of a list object.

+ +
+
char *tclistpop2(TCLIST *list);
+
`list' specifies the list object.
+
The return value is the string of the removed element.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. If the list is empty, the return value is `NULL'.
+
+ +

The function `tclistunshift' is used in order to add an element at the top of a list object.

+ +
+
void tclistunshift(TCLIST *list, const void *ptr, int size);
+
`list' specifies the list object.
+
`ptr' specifies the pointer to the region of the new element.
+
`size' specifies the size of the region.
+
+ +

The function `tclistunshift2' is used in order to add a string element at the top of a list object.

+ +
+
void tclistunshift2(TCLIST *list, const char *str);
+
`list' specifies the list object.
+
`str' specifies the string of the new element.
+
+ +

The function `tclistshift' is used in order to remove an element of the top of a list object.

+ +
+
void *tclistshift(TCLIST *list, int *sp);
+
`list' specifies the list object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the removed element.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. If the list is empty, the return value is `NULL'.
+
+ +

The function `tclistshift2' is used in order to remove a string element of the top of a list object.

+ +
+
char *tclistshift2(TCLIST *list);
+
`list' specifies the list object.
+
The return value is the string of the removed element.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. If the list is empty, the return value is `NULL'.
+
+ +

The function `tclistinsert' is used in order to add an element at the specified location of a list object.

+ +
+
void tclistinsert(TCLIST *list, int index, const void *ptr, int size);
+
`list' specifies the list object.
+
`index' specifies the index of the new element.
+
`ptr' specifies the pointer to the region of the new element.
+
`size' specifies the size of the region.
+
If `index' is equal to or more than the number of elements, this function has no effect.
+
+ +

The function `tclistinsert2' is used in order to add a string element at the specified location of a list object.

+ +
+
void tclistinsert2(TCLIST *list, int index, const char *str);
+
`list' specifies the list object.
+
`index' specifies the index of the new element.
+
`str' specifies the string of the new element.
+
If `index' is equal to or more than the number of elements, this function has no effect.
+
+ +

The function `tclistremove' is used in order to remove an element at the specified location of a list object.

+ +
+
void *tclistremove(TCLIST *list, int index, int *sp);
+
`list' specifies the list object.
+
`index' specifies the index of the element to be removed.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the removed element.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. If `index' is equal to or more than the number of elements, no element is removed and the return value is `NULL'.
+
+ +

The function `tclistremove2' is used in order to remove a string element at the specified location of a list object.

+ +
+
char *tclistremove2(TCLIST *list, int index);
+
`list' specifies the list object.
+
`index' specifies the index of the element to be removed.
+
The return value is the string of the removed element.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. If `index' is equal to or more than the number of elements, no element is removed and the return value is `NULL'.
+
+ +

The function `tclistover' is used in order to overwrite an element at the specified location of a list object.

+ +
+
void tclistover(TCLIST *list, int index, const void *ptr, int size);
+
`list' specifies the list object.
+
`index' specifies the index of the element to be overwritten.
+
`ptr' specifies the pointer to the region of the new content.
+
`size' specifies the size of the new content.
+
If `index' is equal to or more than the number of elements, this function has no effect.
+
+ +

The function `tclistover2' is used in order to overwrite a string element at the specified location of a list object.

+ +
+
void tclistover2(TCLIST *list, int index, const char *str);
+
`list' specifies the list object.
+
`index' specifies the index of the element to be overwritten.
+
`str' specifies the string of the new content.
+
If `index' is equal to or more than the number of elements, this function has no effect.
+
+ +

The function `tclistsort' is used in order to sort elements of a list object in lexical order.

+ +
+
void tclistsort(TCLIST *list);
+
`list' specifies the list object.
+
+ +

The function `tclistlsearch' is used in order to search a list object for an element using liner search.

+ +
+
int tclistlsearch(const TCLIST *list, const void *ptr, int size);
+
`list' specifies the list object.
+
`ptr' specifies the pointer to the region of the key.
+
`size' specifies the size of the region.
+
The return value is the index of a corresponding element or -1 if there is no corresponding element.
+
If two or more elements correspond, the former returns.
+
+ +

The function `tclistbsearch' is used in order to search a list object for an element using binary search.

+ +
+
int tclistbsearch(const TCLIST *list, const void *ptr, int size);
+
`list' specifies the list object. It should be sorted in lexical order.
+
`ptr' specifies the pointer to the region of the key.
+
`size' specifies the size of the region.
+
The return value is the index of a corresponding element or -1 if there is no corresponding element.
+
If two or more elements correspond, which returns is not defined.
+
+ +

The function `tclistclear' is used in order to clear a list object.

+ +
+
void tclistclear(TCLIST *list);
+
`list' specifies the list object.
+
All elements are removed.
+
+ +

The function `tclistdump' is used in order to serialize a list object into a byte array.

+ +
+
void *tclistdump(const TCLIST *list, int *sp);
+
`list' specifies the list object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the result serial region.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tclistload' is used in order to create a list object from a serialized byte array.

+ +
+
TCLIST *tclistload(const void *ptr, int size);
+
`ptr' specifies the pointer to the region of serialized byte array.
+
`size' specifies the size of the region.
+
The return value is a new list object.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

API of Hash Map

+ +

The function `tcmapnew' is used in order to create a map object.

+ +
+
TCMAP *tcmapnew(void);
+
The return value is the new map object.
+
+ +

The function `tcmapnew2' is used in order to create a map object with specifying the number of the buckets.

+ +
+
TCMAP *tcmapnew2(uint32_t bnum);
+
`bnum' specifies the number of the buckets.
+
The return value is the new map object.
+
+ +

The function `tcmapnew3' is used in order to create a map object with initial string elements.

+ +
+
TCMAP *tcmapnew3(const char *str, ...);
+
`str' specifies the string of the first element.
+
The other arguments are other elements. They should be trailed by a `NULL' argument.
+
The return value is the new map object.
+
The key and the value of each record are situated one after the other.
+
+ +

The function `tcmapdup' is used in order to copy a map object.

+ +
+
TCMAP *tcmapdup(const TCMAP *map);
+
`map' specifies the map object.
+
The return value is the new map object equivalent to the specified object.
+
+ +

The function `tcmapdel' is used in order to delete a map object.

+ +
+
void tcmapdel(TCMAP *map);
+
`map' specifies the map object.
+
Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tcmapput' is used in order to store a record into a map object.

+ +
+
void tcmapput(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If a record with the same key exists in the map, it is overwritten.
+
+ +

The function `tcmapput2' is used in order to store a string record into a map object.

+ +
+
void tcmapput2(TCMAP *map, const char *kstr, const char *vstr);
+
`map' specifies the map object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If a record with the same key exists in the map, it is overwritten.
+
+ +

The function `tcmapputkeep' is used in order to store a new record into a map object.

+ +
+
bool tcmapputkeep(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the map, this function has no effect.
+
+ +

The function `tcmapputkeep2' is used in order to store a new string record into a map object.

+ +
+
bool tcmapputkeep2(TCMAP *map, const char *kstr, const char *vstr);
+
`map' specifies the map object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the map, this function has no effect.
+
+ +

The function `tcmapputcat' is used in order to concatenate a value at the end of the value of the existing record in a map object.

+ +
+
void tcmapputcat(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcmapputcat2' is used in order to concatenate a string value at the end of the value of the existing record in a map object.

+ +
+
void tcmapputcat2(TCMAP *map, const char *kstr, const char *vstr);
+
`map' specifies the map object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcmapout' is used in order to remove a record of a map object.

+ +
+
bool tcmapout(TCMAP *map, const void *kbuf, int ksiz);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcmapout2' is used in order to remove a string record of a map object.

+ +
+
bool tcmapout2(TCMAP *map, const char *kstr);
+
`map' specifies the map object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcmapget' is used in order to retrieve a record in a map object.

+ +
+
const void *tcmapget(const TCMAP *map, const void *kbuf, int ksiz, int *sp);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string.
+
+ +

The function `tcmapget2' is used in order to retrieve a string record in a map object.

+ +
+
const char *tcmapget2(const TCMAP *map, const char *kstr);
+
`map' specifies the map object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
+ +

The function `tcmapmove' is used in order to move a record to the edge of a map object.

+ +
+
bool tcmapmove(TCMAP *map, const void *kbuf, int ksiz, bool head);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of a key.
+
`ksiz' specifies the size of the region of the key.
+
`head' specifies the destination which is the head if it is true or the tail if else.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcmapmove2' is used in order to move a string record to the edge of a map object.

+ +
+
bool tcmapmove2(TCMAP *map, const char *kstr, bool head);
+
`map' specifies the map object.
+
`kstr' specifies the string of a key.
+
`head' specifies the destination which is the head if it is true or the tail if else.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcmapiterinit' is used in order to initialize the iterator of a map object.

+ +
+
void tcmapiterinit(TCMAP *map);
+
`map' specifies the map object.
+
The iterator is used in order to access the key of every record stored in the map object.
+
+ +

The function `tcmapiternext' is used in order to get the next key of the iterator of a map object.

+ +
+
const void *tcmapiternext(TCMAP *map, int *sp);
+
`map' specifies the map object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. The order of iteration is assured to be the same as the stored order.
+
+ +

The function `tcmapiternext2' is used in order to get the next key string of the iterator of a map object.

+ +
+
const char *tcmapiternext2(TCMAP *map);
+
`map' specifies the map object.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
The order of iteration is assured to be the same as the stored order.
+
+ +

The function `tcmaprnum' is used in order to get the number of records stored in a map object.

+ +
+
uint64_t tcmaprnum(const TCMAP *map);
+
`map' specifies the map object.
+
The return value is the number of the records stored in the map object.
+
+ +

The function `tcmapmsiz' is used in order to get the total size of memory used in a map object.

+ +
+
uint64_t tcmapmsiz(const TCMAP *map);
+
`map' specifies the map object.
+
The return value is the total size of memory used in a map object.
+
+ +

The function `tcmapkeys' is used in order to create a list object containing all keys in a map object.

+ +
+
TCLIST *tcmapkeys(const TCMAP *map);
+
`map' specifies the map object.
+
The return value is the new list object containing all keys in the map object.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcmapvals' is used in order to create a list object containing all values in a map object.

+ +
+
TCLIST *tcmapvals(const TCMAP *map);
+
`map' specifies the map object.
+
The return value is the new list object containing all values in the map object.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcmapaddint' is used in order to add an integer to a record in a map object.

+ +
+
int tcmapaddint(TCMAP *map, const void *kbuf, int ksiz, int num);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcmapadddouble' is used in order to add a real number to a record in a map object.

+ +
+
double tcmapadddouble(TCMAP *map, const void *kbuf, int ksiz, double num);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcmapclear' is used in order to clear a map object.

+ +
+
void tcmapclear(TCMAP *map);
+
`map' specifies the map object.
+
All records are removed.
+
+ +

The function `tcmapcutfront' is used in order to remove front records of a map object.

+ +
+
void tcmapcutfront(TCMAP *map, int num);
+
`map' specifies the map object.
+
`num' specifies the number of records to be removed.
+
+ +

The function `tcmapdump' is used in order to serialize a map object into a byte array.

+ +
+
void *tcmapdump(const TCMAP *map, int *sp);
+
`map' specifies the map object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the result serial region.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmapload' is used in order to create a map object from a serialized byte array.

+ +
+
TCMAP *tcmapload(const void *ptr, int size);
+
`ptr' specifies the pointer to the region of serialized byte array.
+
`size' specifies the size of the region.
+
The return value is a new map object.
+
Because the object of the return value is created with the function `tcmapnew', it should be deleted with the function `tcmapdel' when it is no longer in use.
+
+ +

API of Ordered Tree

+ +

The function `tctreenew' is used in order to create a tree object.

+ +
+
TCTREE *tctreenew(void);
+
The return value is the new tree object.
+
+ +

The function `tctreenew2' is used in order to create a tree object with specifying the custom comparison function.

+ +
+
TCTREE *tctreenew2(TCCMP cmp, void *cmpop);
+
`cmp' specifies the pointer to the custom comparison function. It receives five parameters. The first parameter is the pointer to the region of one key. The second parameter is the size of the region of one key. The third parameter is the pointer to the region of the other key. The fourth parameter is the size of the region of the other key. The fifth parameter is the pointer to the optional opaque object. It returns positive if the former is big, negative if the latter is big, 0 if both are equivalent.
+
`cmpop' specifies an arbitrary pointer to be given as a parameter of the comparison function. If it is not needed, `NULL' can be specified.
+
The return value is the new tree object.
+
The default comparison function compares keys of two records by lexical order. The functions `tccmplexical' (dafault), `tccmpdecimal', `tccmpint32', and `tccmpint64' are built-in.
+
+ +

The function `tctreedup' is used in order to copy a tree object.

+ +
+
TCTREE *tctreedup(const TCTREE *tree);
+
`tree' specifies the tree object.
+
The return value is the new tree object equivalent to the specified object.
+
+ +

The function `tctreedel' is used in order to delete a tree object.

+ +
+
void tctreedel(TCTREE *tree);
+
`tree' specifies the tree object.
+
Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tctreeput' is used in order to store a record into a tree object.

+ +
+
void tctreeput(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`tree' specifies the tree object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If a record with the same key exists in the tree, it is overwritten.
+
+ +

The function `tctreeput2' is used in order to store a string record into a tree object.

+ +
+
void tctreeput2(TCTREE *tree, const char *kstr, const char *vstr);
+
`tree' specifies the tree object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If a record with the same key exists in the tree, it is overwritten.
+
+ +

The function `tctreeputkeep' is used in order to store a new record into a tree object.

+ +
+
bool tctreeputkeep(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`tree' specifies the tree object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the tree, this function has no effect.
+
+ +

The function `tctreeputkeep2' is used in order to store a new string record into a tree object.

+ +
+
bool tctreeputkeep2(TCTREE *tree, const char *kstr, const char *vstr);
+
`tree' specifies the tree object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the tree, this function has no effect.
+
+ +

The function `tctreeputcat' is used in order to concatenate a value at the end of the value of the existing record in a tree object.

+ +
+
void tctreeputcat(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`tree' specifies the tree object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tctreeputcat2' is used in order to concatenate a string value at the end of the value of the existing record in a tree object.

+ +
+
void tctreeputcat2(TCTREE *tree, const char *kstr, const char *vstr);
+
`tree' specifies the tree object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tctreeout' is used in order to remove a record of a tree object.

+ +
+
bool tctreeout(TCTREE *tree, const void *kbuf, int ksiz);
+
`tree' specifies the tree object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tctreeout2' is used in order to remove a string record of a tree object.

+ +
+
bool tctreeout2(TCTREE *tree, const char *kstr);
+
`tree' specifies the tree object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tctreeget' is used in order to retrieve a record in a tree object.

+ +
+
const void *tctreeget(TCTREE *tree, const void *kbuf, int ksiz, int *sp);
+
`tree' specifies the tree object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string.
+
+ +

The function `tctreeget2' is used in order to retrieve a string record in a tree object.

+ +
+
const char *tctreeget2(TCTREE *tree, const char *kstr);
+
`tree' specifies the tree object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
+ +

The function `tctreeiterinit' is used in order to initialize the iterator of a tree object.

+ +
+
void tctreeiterinit(TCTREE *tree);
+
`tree' specifies the tree object.
+
The iterator is used in order to access the key of every record stored in the tree object.
+
+ +

The function `tctreeiternext' is used in order to get the next key of the iterator of a tree object.

+ +
+
const void *tctreeiternext(TCTREE *tree, int *sp);
+
`tree' specifies the tree object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. The order of iteration is assured to be ascending of the keys.
+
+ +

The function `tctreeiternext2' is used in order to get the next key string of the iterator of a tree object.

+ +
+
const char *tctreeiternext2(TCTREE *tree);
+
`tree' specifies the tree object.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
The order of iteration is assured to be ascending of the keys.
+
+ +

The function `tctreernum' is used in order to get the number of records stored in a tree object.

+ +
+
uint64_t tctreernum(const TCTREE *tree);
+
`tree' specifies the tree object.
+
The return value is the number of the records stored in the tree object.
+
+ +

The function `tctreemsiz' is used in order to get the total size of memory used in a tree object.

+ +
+
uint64_t tctreemsiz(const TCTREE *tree);
+
`tree' specifies the tree object.
+
The return value is the total size of memory used in a tree object.
+
+ +

The function `tctreekeys' is used in order to create a list object containing all keys in a tree object.

+ +
+
TCLIST *tctreekeys(const TCTREE *tree);
+
`tree' specifies the tree object.
+
The return value is the new list object containing all keys in the tree object.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tctreevals' is used in order to create a list object containing all values in a tree object.

+ +
+
TCLIST *tctreevals(const TCTREE *tree);
+
`tree' specifies the tree object.
+
The return value is the new list object containing all values in the tree object.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tctreeaddint' is used in order to add an integer to a record in a tree object.

+ +
+
int tctreeaddint(TCTREE *tree, const void *kbuf, int ksiz, int num);
+
`tree' specifies the tree object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tctreeadddouble' is used in order to add a real number to a record in a tree object.

+ +
+
double tctreeadddouble(TCTREE *tree, const void *kbuf, int ksiz, double num);
+
`tree' specifies the tree object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tctreeclear' is used in order to clear a tree object.

+ +
+
void tctreeclear(TCTREE *tree);
+
`tree' specifies the tree object.
+
All records are removed.
+
+ +

The function `tctreecutfringe' is used in order to remove fringe records of a tree object.

+ +
+
void tctreecutfringe(TCTREE *tree, int num);
+
`tree' specifies the tree object.
+
`num' specifies the number of records to be removed.
+
+ +

The function `tctreedump' is used in order to serialize a tree object into a byte array.

+ +
+
void *tctreedump(const TCTREE *tree, int *sp);
+
`tree' specifies the tree object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the result serial region.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tctreeload' is used in order to create a tree object from a serialized byte array.

+ +
+
TCTREE *tctreeload(const void *ptr, int size, TCCMP cmp, void *cmpop);
+
`ptr' specifies the pointer to the region of serialized byte array.
+
`size' specifies the size of the region.
+
`cmp' specifies the pointer to the custom comparison function.
+
`cmpop' specifies an arbitrary pointer to be given as a parameter of the comparison function.
+
If it is not needed, `NULL' can be specified.
+
The return value is a new tree object.
+
Because the object of the return value is created with the function `tctreenew', it should be deleted with the function `tctreedel' when it is no longer in use.
+
+ +

API of On-memory Hash Database

+ +

The function `tcmdbnew' is used in order to create an on-memory hash database object.

+ +
+
TCMDB *tcmdbnew(void);
+
The return value is the new on-memory hash database object.
+
The object can be shared by plural threads because of the internal mutex.
+
+ +

The function `tcmdbnew2' is used in order to create an on-memory hash database object with specifying the number of the buckets.

+ +
+
TCMDB *tcmdbnew2(uint32_t bnum);
+
`bnum' specifies the number of the buckets.
+
The return value is the new on-memory hash database object.
+
The object can be shared by plural threads because of the internal mutex.
+
+ +

The function `tcmdbdel' is used in order to delete an on-memory hash database object.

+ +
+
void tcmdbdel(TCMDB *mdb);
+
`mdb' specifies the on-memory hash database object.
+
+ +

The function `tcmdbput' is used in order to store a record into an on-memory hash database object.

+ +
+
void tcmdbput(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcmdbput2' is used in order to store a string record into an on-memory hash database object.

+ +
+
void tcmdbput2(TCMDB *mdb, const char *kstr, const char *vstr);
+
`mdb' specifies the on-memory hash database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcmdbputkeep' is used in order to store a new record into an on-memory hash database object.

+ +
+
bool tcmdbputkeep(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcmdbputkeep2' is used in order to store a new string record into an on-memory hash database object.

+ +
+
bool tcmdbputkeep2(TCMDB *mdb, const char *kstr, const char *vstr);
+
`mdb' specifies the on-memory hash database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcmdbputcat' is used in order to concatenate a value at the end of the existing record in an on-memory hash database.

+ +
+
void tcmdbputcat(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcmdbputcat2' is used in order to concatenate a string at the end of the existing record in an on-memory hash database.

+ +
+
void tcmdbputcat2(TCMDB *mdb, const char *kstr, const char *vstr);
+
`mdb' specifies the on-memory hash database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcmdbout' is used in order to remove a record of an on-memory hash database object.

+ +
+
bool tcmdbout(TCMDB *mdb, const void *kbuf, int ksiz);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcmdbout2' is used in order to remove a string record of an on-memory hash database object.

+ +
+
bool tcmdbout2(TCMDB *mdb, const char *kstr);
+
`mdb' specifies the on-memory hash database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcmdbget' is used in order to retrieve a record in an on-memory hash database object.

+ +
+
void *tcmdbget(TCMDB *mdb, const void *kbuf, int ksiz, int *sp);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmdbget2' is used in order to retrieve a string record in an on-memory hash database object.

+ +
+
char *tcmdbget2(TCMDB *mdb, const char *kstr);
+
`mdb' specifies the on-memory hash database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmdbvsiz' is used in order to get the size of the value of a record in an on-memory hash database object.

+ +
+
int tcmdbvsiz(TCMDB *mdb, const void *kbuf, int ksiz);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcmdbvsiz2' is used in order to get the size of the value of a string record in an on-memory hash database object.

+ +
+
int tcmdbvsiz2(TCMDB *mdb, const char *kstr);
+
`mdb' specifies the on-memory hash database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcmdbiterinit' is used in order to initialize the iterator of an on-memory hash database object.

+ +
+
void tcmdbiterinit(TCMDB *mdb);
+
`mdb' specifies the on-memory hash database object.
+
The iterator is used in order to access the key of every record stored in the on-memory hash database.
+
+ +

The function `tcmdbiternext' is used in order to get the next key of the iterator of an on-memory hash database object.

+ +
+
void *tcmdbiternext(TCMDB *mdb, int *sp);
+
`mdb' specifies the on-memory hash database object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return
+
value is assigned.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. The order of iteration is assured to be the same as the stored order.
+
+ +

The function `tcmdbiternext2' is used in order to get the next key string of the iterator of an on-memory hash database object.

+ +
+
char *tcmdbiternext2(TCMDB *mdb);
+
`mdb' specifies the on-memory hash database object.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. The order of iteration is assured to be the same as the stored order.
+
+ +

The function `tcmdbfwmkeys' is used in order to get forward matching keys in an on-memory hash database object.

+ +
+
TCLIST *tcmdbfwmkeys(TCMDB *mdb, const void *pbuf, int psiz, int max);
+
`mdb' specifies the on-memory hash database object.
+
`pbuf' specifies the pointer to the region of the prefix.
+
`psiz' specifies the size of the region of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcmdbfwmkeys2' is used in order to get forward matching string keys in an on-memory hash database object.

+ +
+
TCLIST *tcmdbfwmkeys2(TCMDB *mdb, const char *pstr, int max);
+
`mdb' specifies the on-memory hash database object.
+
`pstr' specifies the string of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcmdbrnum' is used in order to get the number of records stored in an on-memory hash database object.

+ +
+
uint64_t tcmdbrnum(TCMDB *mdb);
+
`mdb' specifies the on-memory hash database object.
+
The return value is the number of the records stored in the database.
+
+ +

The function `tcmdbmsiz' is used in order to get the total size of memory used in an on-memory hash database object.

+ +
+
uint64_t tcmdbmsiz(TCMDB *mdb);
+
`mdb' specifies the on-memory hash database object.
+
The return value is the total size of memory used in the database.
+
+ +

The function `tcmdbaddint' is used in order to add an integer to a record in an on-memory hash database object.

+ +
+
int tcmdbaddint(TCMDB *mdb, const void *kbuf, int ksiz, int num);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcmdbadddouble' is used in order to add a real number to a record in an on-memory hash database object.

+ +
+
double tcmdbadddouble(TCMDB *mdb, const void *kbuf, int ksiz, double num);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcmdbvanish' is used in order to clear an on-memory hash database object.

+ +
+
void tcmdbvanish(TCMDB *mdb);
+
`mdb' specifies the on-memory hash database object.
+
All records are removed.
+
+ +

The function `tcmdbcutfront' is used in order to remove front records of an on-memory hash database object.

+ +
+
void tcmdbcutfront(TCMDB *mdb, int num);
+
`mdb' specifies the on-memory hash database object.
+
`num' specifies the number of records to be removed.
+
+ +

API of On-memory Tree Database

+ +

The function `tcndbnew' is used in order to create an on-memory tree database object.

+ +
+
TCNDB *tcndbnew(void);
+
The return value is the new on-memory tree database object.
+
The object can be shared by plural threads because of the internal mutex.
+
+ +

The function `tcndbnew2' is used in order to create an on-memory tree database object with specifying the custom comparison function.

+ +
+
TCNDB *tcndbnew2(TCCMP cmp, void *cmpop);
+
`cmp' specifies the pointer to the custom comparison function.
+
`cmpop' specifies an arbitrary pointer to be given as a parameter of the comparison function. If it is not needed, `NULL' can be specified.
+
The return value is the new on-memory tree database object.
+
The default comparison function compares keys of two records by lexical order. The functions `tccmplexical' (dafault), `tccmpdecimal', `tccmpint32', and `tccmpint64' are built-in. The object can be shared by plural threads because of the internal mutex.
+
+ +

The function `tcndbdel' is used in order to delete an on-memory tree database object.

+ +
+
void tcndbdel(TCNDB *ndb);
+
`ndb' specifies the on-memory tree database object.
+
+ +

The function `tcndbput' is used in order to store a record into an on-memory tree database object.

+ +
+
void tcndbput(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcndbput2' is used in order to store a string record into an on-memory tree database object.

+ +
+
void tcndbput2(TCNDB *ndb, const char *kstr, const char *vstr);
+
`ndb' specifies the on-memory tree database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcndbputkeep' is used in order to store a new record into an on-memory tree database object.

+ +
+
bool tcndbputkeep(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcndbputkeep2' is used in order to store a new string record into an on-memory tree database object.

+ +
+
bool tcndbputkeep2(TCNDB *ndb, const char *kstr, const char *vstr);
+
`ndb' specifies the on-memory tree database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcndbputcat' is used in order to concatenate a value at the end of the existing record in an on-memory tree database.

+ +
+
void tcndbputcat(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcndbputcat2' is used in order to concatenate a string at the end of the existing record in an on-memory tree database.

+ +
+
void tcndbputcat2(TCNDB *ndb, const char *kstr, const char *vstr);
+
`ndb' specifies the on-memory tree database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcndbout' is used in order to remove a record of an on-memory tree database object.

+ +
+
bool tcndbout(TCNDB *ndb, const void *kbuf, int ksiz);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcndbout2' is used in order to remove a string record of an on-memory tree database object.

+ +
+
bool tcndbout2(TCNDB *ndb, const char *kstr);
+
`ndb' specifies the on-memory tree database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcndbget' is used in order to retrieve a record in an on-memory tree database object.

+ +
+
void *tcndbget(TCNDB *ndb, const void *kbuf, int ksiz, int *sp);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcndbget2' is used in order to retrieve a string record in an on-memory tree database object.

+ +
+
char *tcndbget2(TCNDB *ndb, const char *kstr);
+
`ndb' specifies the on-memory tree database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcndbvsiz' is used in order to get the size of the value of a record in an on-memory tree database object.

+ +
+
int tcndbvsiz(TCNDB *ndb, const void *kbuf, int ksiz);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcndbvsiz2' is used in order to get the size of the value of a string record in an on-memory tree database object.

+ +
+
int tcndbvsiz2(TCNDB *ndb, const char *kstr);
+
`ndb' specifies the on-memory tree database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcndbiterinit' is used in order to initialize the iterator of an on-memory tree database object.

+ +
+
void tcndbiterinit(TCNDB *ndb);
+
`ndb' specifies the on-memory tree database object.
+
The iterator is used in order to access the key of every record stored in the on-memory database.
+
+ +

The function `tcndbiternext' is used in order to get the next key of the iterator of an on-memory tree database object.

+ +
+
void *tcndbiternext(TCNDB *ndb, int *sp);
+
`ndb' specifies the on-memory tree database object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. The order of iteration is assured to be the same as the stored order.
+
+ +

The function `tcndbiternext2' is used in order to get the next key string of the iterator of an on-memory tree database object.

+ +
+
char *tcndbiternext2(TCNDB *ndb);
+
`ndb' specifies the on-memory tree database object.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. The order of iteration is assured to be the same as the stored order.
+
+ +

The function `tcndbfwmkeys' is used in order to get forward matching keys in an on-memory tree database object.

+ +
+
TCLIST *tcndbfwmkeys(TCNDB *ndb, const void *pbuf, int psiz, int max);
+
`ndb' specifies the on-memory tree database object.
+
`pbuf' specifies the pointer to the region of the prefix.
+
`psiz' specifies the size of the region of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcndbfwmkeys2' is used in order to get forward matching string keys in an on-memory tree database object.

+ +
+
TCLIST *tcndbfwmkeys2(TCNDB *ndb, const char *pstr, int max);
+
`ndb' specifies the on-memory tree database object.
+
`pstr' specifies the string of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcndbrnum' is used in order to get the number of records stored in an on-memory tree database object.

+ +
+
uint64_t tcndbrnum(TCNDB *ndb);
+
`ndb' specifies the on-memory tree database object.
+
The return value is the number of the records stored in the database.
+
+ +

The function `tcndbmsiz' is used in order to get the total size of memory used in an on-memory tree database object.

+ +
+
uint64_t tcndbmsiz(TCNDB *ndb);
+
`ndb' specifies the on-memory tree database object.
+
The return value is the total size of memory used in the database.
+
+ +

The function `tcndbaddint' is used in order to add an integer to a record in an on-memory tree database object.

+ +
+
int tcndbaddint(TCNDB *ndb, const void *kbuf, int ksiz, int num);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcndbadddouble' is used in order to add a real number to a record in an on-memory tree database object.

+ +
+
double tcndbadddouble(TCNDB *ndb, const void *kbuf, int ksiz, double num);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcndbvanish' is used in order to clear an on-memory tree database object.

+ +
+
void tcndbvanish(TCNDB *ndb);
+
`ndb' specifies the on-memory tree database object.
+
All records are removed.
+
+ +

The function `tcndbcutfringe' is used in order to remove fringe records of an on-memory tree database object.

+ +
+
void tcndbcutfringe(TCNDB *ndb, int num);
+
`ndb' specifies the on-memory tree database object.
+
`num' specifies the number of records to be removed.
+
+ +

API of Memory Pool

+ +

The function `tcmpoolnew' is used in order to create a memory pool object.

+ +
+
TCMPOOL *tcmpoolnew(void);
+
The return value is the new memory pool object.
+
+ +

The function `tcmpooldel' is used in order to delete a memory pool object.

+ +
+
void tcmpooldel(TCMPOOL *mpool);
+
`mpool' specifies the memory pool object.
+
Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tcmpoolpush' is used in order to relegate an arbitrary object to a memory pool object.

+ +
+
void *tcmpoolpush(TCMPOOL *mpool, void *ptr, void (*del)(void *));
+
`mpool' specifies the memory pool object.
+
`ptr' specifies the pointer to the object to be relegated. If it is `NULL', this function has no effect.
+
`del' specifies the pointer to the function to delete the object.
+
The return value is the pointer to the given object.
+
This function assures that the specified object is deleted when the memory pool object is deleted.
+
+ +

The function `tcmpoolpushptr' is used in order to relegate an allocated region to a memory pool object.

+ +
+
void *tcmpoolpushptr(TCMPOOL *mpool, void *ptr);
+
`mpool' specifies the memory pool object.
+
`ptr' specifies the pointer to the region to be relegated. If it is `NULL', this function has no effect.
+
The return value is the pointer to the given object.
+
This function assures that the specified region is released when the memory pool object is deleted.
+
+ +

The function `tcmpoolpushxstr' is used in order to relegate an extensible string object to a memory pool object.

+ +
+
TCXSTR *tcmpoolpushxstr(TCMPOOL *mpool, TCXSTR *xstr);
+
`mpool' specifies the memory pool object.
+
`xstr' specifies the extensible string object. If it is `NULL', this function has no effect.
+
The return value is the pointer to the given object.
+
This function assures that the specified object is deleted when the memory pool object is deleted.
+
+ +

The function `tcmpoolpushlist' is used in order to relegate a list object to a memory pool object.

+ +
+
TCLIST *tcmpoolpushlist(TCMPOOL *mpool, TCLIST *list);
+
`mpool' specifies the memory pool object.
+
`list' specifies the list object. If it is `NULL', this function has no effect.
+
The return value is the pointer to the given object.
+
This function assures that the specified object is deleted when the memory pool object is deleted.
+
+ +

The function `tcmpoolpushmap' is used in order to relegate a map object to a memory pool object.

+ +
+
TCMAP *tcmpoolpushmap(TCMPOOL *mpool, TCMAP *map);
+
`mpool' specifies the memory pool object.
+
`map' specifies the map object. If it is `NULL', this function has no effect.
+
The return value is the pointer to the given object.
+
This function assures that the specified object is deleted when the memory pool object is deleted.
+
+ +

The function `tcmpoolpushtree' is used in order to relegate a tree object to a memory pool object.

+ +
+
TCTREE *tcmpoolpushtree(TCMPOOL *mpool, TCTREE *tree);
+
`mpool' specifies the memory pool object.
+
`tree' specifies the tree object. If it is `NULL', this function has no effect.
+
The return value is the pointer to the given object.
+
This function assures that the specified object is deleted when the memory pool object is deleted.
+
+ +

The function `tcmpoolmalloc' is used in order to allocate a region relegated to a memory pool object.

+ +
+
void *tcmpoolmalloc(TCMPOOL *mpool, size_t size);
+
`mpool' specifies the memory pool object.
+
The return value is the pointer to the allocated region under the memory pool.
+
+ +

The function `tcmpoolxstrnew' is used in order to create an extensible string object relegated to a memory pool object.

+ +
+
TCXSTR *tcmpoolxstrnew(TCMPOOL *mpool);
+
The return value is the new extensible string object under the memory pool.
+
+ +

The function `tcmpoollistnew' is used in order to create a list object relegated to a memory pool object.

+ +
+
TCLIST *tcmpoollistnew(TCMPOOL *mpool);
+
The return value is the new list object under the memory pool.
+
+ +

The function `tcmpoolmapnew' is used in order to create a map object relegated to a memory pool object.

+ +
+
TCMAP *tcmpoolmapnew(TCMPOOL *mpool);
+
The return value is the new map object under the memory pool.
+
+ +

The function `tcmpooltreenew' is used in order to create a tree object relegated to a memory pool object.

+ +
+
TCTREE *tcmpooltreenew(TCMPOOL *mpool);
+
The return value is the new tree object under the memory pool.
+
+ +

The function `tcmpoolpop' is used in order to remove the most recently installed cleanup handler of a memory pool object.

+ +
+
void tcmpoolpop(TCMPOOL *mpool, bool exe);
+
`mpool' specifies the memory pool object.
+
`exe' specifies whether to execute the destructor of the removed handler.
+
+ +

The function `tcmpoolclear' is used in order to remove all cleanup handler of a memory pool object.

+ +
+
void tcmpoolclear(TCMPOOL *mpool, bool exe);
+
`mpool' specifies the memory pool object.
+
`exe' specifies whether to execute the destructors of the removed handlers.
+
+ +

The function `tcmpoolglobal' is used in order to get the global memory pool object.

+ +
+
TCMPOOL *tcmpoolglobal(void);
+
The return value is the global memory pool object.
+
The global memory pool object is a singleton and assured to be deleted when the process is terminating normally.
+
+ +

API of Miscellaneous Utilities

+ +

The function `tclmax' is used in order to get the larger value of two integers.

+ +
+
long tclmax(long a, long b);
+
`a' specifies an integer.
+
`b' specifies the other integer.
+
The return value is the larger value of the two.
+
+ +

The function `tclmin' is used in order to get the lesser value of two integers.

+ +
+
long tclmin(long a, long b);
+
`a' specifies an integer.
+
`b' specifies the other integer.
+
The return value is the lesser value of the two.
+
+ +

The function `tclrand' is used in order to get a random number as long integer based on uniform distribution.

+ +
+
unsigned long tclrand(void);
+
The return value is the random number between 0 and `ULONG_MAX'.
+
This function uses the random number source device and generates a real random number if possible.
+
+ +

The function `tcdrand' is used in order to get a random number as double decimal based on uniform distribution.

+ +
+
double tcdrand(void);
+
The return value is the random number equal to or greater than 0, and less than 1.0.
+
This function uses the random number source device and generates a real random number if possible.
+
+ +

The function `tcdrandnd' is used in order to get a random number as double decimal based on normal distribution.

+ +
+
double tcdrandnd(double avg, double sd);
+
`avg' specifies the average.
+
`sd' specifies the standard deviation.
+
The return value is the random number.
+
This function uses the random number source device and generates a real random number if possible.
+
+ +

The function `tcstricmp' is used in order to compare two strings with case insensitive evaluation.

+ +
+
int tcstricmp(const char *astr, const char *bstr);
+
`astr' specifies a string.
+
`bstr' specifies of the other string.
+
The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent.
+
+ +

The function `tcstrfwm' is used in order to check whether a string begins with a key.

+ +
+
bool tcstrfwm(const char *str, const char *key);
+
`str' specifies the target string.
+
`key' specifies the forward matching key string.
+
The return value is true if the target string begins with the key, else, it is false.
+
+ +

The function `tcstrifwm' is used in order to check whether a string begins with a key with case insensitive evaluation.

+ +
+
bool tcstrifwm(const char *str, const char *key);
+
`str' specifies the target string.
+
`key' specifies the forward matching key string.
+
The return value is true if the target string begins with the key, else, it is false.
+
+ +

The function `tcstrbwm' is used in order to check whether a string ends with a key.

+ +
+
bool tcstrbwm(const char *str, const char *key);
+
`str' specifies the target string.
+
`key' specifies the backward matching key string.
+
The return value is true if the target string ends with the key, else, it is false.
+
+ +

The function `tcstribwm' is used in order to check whether a string ends with a key with case insensitive evaluation.

+ +
+
bool tcstribwm(const char *str, const char *key);
+
`str' specifies the target string.
+
`key' specifies the backward matching key string.
+
The return value is true if the target string ends with the key, else, it is false.
+
+ +

The function `tcstrdist' is used in order to calculate the edit distance of two strings.

+ +
+
int tcstrdist(const char *astr, const char *bstr);
+
`astr' specifies a string.
+
`bstr' specifies of the other string.
+
The return value is the edit distance which is known as the Levenshtein distance. The cost is calculated by byte.
+
+ +

The function `tcstrdistutf' is used in order to calculate the edit distance of two UTF-8 strings.

+ +
+
int tcstrdistutf(const char *astr, const char *bstr);
+
`astr' specifies a string.
+
`bstr' specifies of the other string.
+
The return value is the edit distance which is known as the Levenshtein distance. The cost is calculated by Unicode character.
+
+ +

The function `tcstrtoupper' is used in order to convert the letters of a string into upper case.

+ +
+
char *tcstrtoupper(char *str);
+
`str' specifies the string to be converted.
+
The return value is the string itself.
+
+ +

The function `tcstrtolower' is used in order to convert the letters of a string into lower case.

+ +
+
char *tcstrtolower(char *str);
+
`str' specifies the string to be converted.
+
The return value is the string itself.
+
+ +

The function `tcstrtrim' is used in order to cut space characters at head or tail of a string.

+ +
+
char *tcstrtrim(char *str);
+
`str' specifies the string to be converted.
+
The return value is the string itself.
+
+ +

The function `tcstrsqzspc' is used in order to squeeze space characters in a string and trim it.

+ +
+
char *tcstrsqzspc(char *str);
+
`str' specifies the string to be converted.
+
The return value is the string itself.
+
+ +

The function `tcstrsubchr' is used in order to substitute characters in a string.

+ +
+
char *tcstrsubchr(char *str, const char *rstr, const char *sstr);
+
`str' specifies the string to be converted.
+
`rstr' specifies the string containing characters to be replaced.
+
`sstr' specifies the string containing characters to be substituted.
+
If the substitute string is shorter then the replacement string, corresponding characters are removed.
+
+ +

The function `tcstrcntutf' is used in order to count the number of characters in a string of UTF-8.

+ +
+
int tcstrcntutf(const char *str);
+
`str' specifies the string of UTF-8.
+
The return value is the number of characters in the string.
+
+ +

The function `tcstrcututf' is used in order to cut a string of UTF-8 at the specified number of characters.

+ +
+
char *tcstrcututf(char *str, int num);
+
`str' specifies the string of UTF-8.
+
`num' specifies the number of characters to be kept.
+
The return value is the string itself.
+
+ +

The function `tcstrutftoucs' is used in order to convert a UTF-8 string into a UCS-2 array.

+ +
+
void tcstrutftoucs(const char *str, uint16_t *ary, int *np);
+
`str' specifies the UTF-8 string.
+
`ary' specifies the pointer to the region into which the result UCS-2 codes are written. The size of the buffer should be sufficient.
+
`np' specifies the pointer to a variable into which the number of elements of the result array is assigned.
+
+ +

The function `tcstrucstoutf' is used in order to convert a UCS-2 array into a UTF-8 string.

+ +
+
int tcstrucstoutf(const uint16_t *ary, int num, char *str);
+
`ary' specifies the array of UCS-2 codes.
+
`num' specifies the number of the array.
+
`str' specifies the pointer to the region into which the result UTF-8 string is written. The size of the buffer should be sufficient.
+
The return value is the length of the result string.
+
+ +

The function `tcstrsplit' is used in order to create a list object by splitting a string.

+ +
+
TCLIST *tcstrsplit(const char *str, const char *delims);
+
`str' specifies the source string.
+
`delims' specifies a string containing delimiting characters.
+
The return value is a list object of the split elements.
+
If two delimiters are successive, it is assumed that an empty element is between the two. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcstrjoin' is used in order to create a string by joining all elements of a list object.

+ +
+
char *tcstrjoin(const TCLIST *list, char delim);
+
`list' specifies a list object.
+
`delim' specifies a delimiting character.
+
The return value is the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcatoi' is used in order to convert a string to an integer.

+ +
+
int64_t tcatoi(const char *str);
+
`str' specifies the string.
+
The return value is the integer. If the string does not contain numeric expression, 0 is returned.
+
This function is equivalent to `atoll' except that it does not depend on the locale.
+
+ +

The function `tcatoix' is used in order to convert a string with a metric prefix to an integer.

+ +
+
int64_t tcatoix(const char *str);
+
`str' specifies the string, which can be trailed by a binary metric prefix. "K", "M", "G", "T", "P", and "E" are supported. They are case-insensitive.
+
The return value is the integer. If the string does not contain numeric expression, 0 is returned. If the integer overflows the domain, `INT64_MAX' or `INT64_MIN' is returned according to the sign.
+
+ +

The function `tcatof' is used in order to convert a string to a real number.

+ +
+
double tcatof(const char *str);
+
`str' specifies the string.
+
The return value is the real number. If the string does not contain numeric expression, 0.0 is returned.
+
This function is equivalent to `atof' except that it does not depend on the locale.
+
+ +

The function `tcregexmatch' is used in order to check whether a string matches a regular expression.

+ +
+
bool tcregexmatch(const char *str, const char *regex);
+
`str' specifies the target string.
+
`regex' specifies the regular expression string. If it begins with `*', the trailing substring is used as a case-insensitive regular expression.
+
The return value is true if matching is success, else, it is false.
+
+ +

The function `tcregexreplace' is used in order to replace each substring matching a regular expression string.

+ +
+
char *tcregexreplace(const char *str, const char *regex, const char *alt);
+
`str' specifies the target string.
+
`regex' specifies the regular expression string for substrings. If it begins with `*', the trailing substring is used as a case-insensitive regular expression.
+
`alt' specifies the alternative string with which each substrings is replaced. Each `&' in the string is replaced with the matched substring. Each `\' in the string escapes the following character. Special escapes "\1" through "\9" referring to the corresponding matching sub-expressions in the regular expression string are supported.
+
The return value is a new converted string. Even if the regular expression is invalid, a copy of the original string is returned.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmd5hash' is used in order to get the MD5 hash value of a serial object.

+ +
+
void tcmd5hash(const void *ptr, int size, char *buf);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`buf' specifies the pointer to the region into which the result string is written. The size of the buffer should be equal to or more than 48 bytes.
+
+ +

The function `tcarccipher' is used in order to cipher or decipher a serial object with the Arcfour stream cipher.

+ +
+
void tcarccipher(const void *ptr, int size, const void *kbuf, int ksiz, void *obuf);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`kbuf' specifies the pointer to the region of the cipher key.
+
`ksiz' specifies the size of the region of the cipher key.
+
`obuf' specifies the pointer to the region into which the result data is written. The size of the buffer should be equal to or more than the input region.
+
+ +

The function `tctime' is used in order to get the time of day in seconds.

+ +
+
double tctime(void);
+
The return value is the time of day in seconds. The accuracy is in microseconds.
+
+ +

The function `tccalendar' is used in order to get the Gregorian calendar of a time.

+ +
+
void tccalendar(int64_t t, int jl, int *yearp, int *monp, int *dayp, int *hourp, int *minp, int *secp);
+
`t' specifies the source time in seconds from the epoch. If it is `INT64_MAX', the current time is specified.
+
`jl' specifies the jet lag of a location in seconds. If it is `INT_MAX', the local jet lag is specified.
+
`yearp' specifies the pointer to a variable to which the year is assigned. If it is `NULL', it is not used.
+
`monp' specifies the pointer to a variable to which the month is assigned. If it is `NULL', it is not used. 1 means January and 12 means December.
+
`dayp' specifies the pointer to a variable to which the day of the month is assigned. If it is `NULL', it is not used.
+
`hourp' specifies the pointer to a variable to which the hours is assigned. If it is `NULL', it is not used.
+
`minp' specifies the pointer to a variable to which the minutes is assigned. If it is `NULL', it is not used.
+
`secp' specifies the pointer to a variable to which the seconds is assigned. If it is `NULL', it is not used.
+
+ +

The function `tcdatestrwww' is used in order to format a date as a string in W3CDTF.

+ +
+
void tcdatestrwww(int64_t t, int jl, char *buf);
+
`t' specifies the source time in seconds from the epoch. If it is `INT64_MAX', the current time is specified.
+
`jl' specifies the jet lag of a location in seconds. If it is `INT_MAX', the local jet lag is specified.
+
`buf' specifies the pointer to the region into which the result string is written. The size of the buffer should be equal to or more than 48 bytes.
+
W3CDTF represents a date as "YYYY-MM-DDThh:mm:ddTZD".
+
+ +

The function `tcdatestrhttp' is used in order to format a date as a string in RFC 1123 format.

+ +
+
void tcdatestrhttp(int64_t t, int jl, char *buf);
+
`t' specifies the source time in seconds from the epoch. If it is `INT64_MAX', the current time is specified.
+
`jl' specifies the jet lag of a location in seconds. If it is `INT_MAX', the local jet lag is specified.
+
`buf' specifies the pointer to the region into which the result string is written. The size of the buffer should be equal to or more than 48 bytes.
+
RFC 1123 format represents a date as "Wdy, DD-Mon-YYYY hh:mm:dd TZD".
+
+ +

The function `tcstrmktime' is used in order to get the time value of a date string.

+ +
+
int64_t tcstrmktime(const char *str);
+
`str' specifies the date string in decimal, hexadecimal, W3CDTF, or RFC 822 (1123). Decimal can be trailed by "s" for in seconds, "m" for in minutes, "h" for in hours, and "d" for in days.
+
The return value is the time value of the date or `INT64_MIN' if the format is invalid.
+
+ +

The function `tcjetlag' is used in order to get the jet lag of the local time.

+ +
+
int tcjetlag(void);
+
The return value is the jet lag of the local time in seconds.
+
+ +

The function `tcdayofweek' is used in order to get the day of week of a date.

+ +
+
int tcdayofweek(int year, int mon, int day);
+
`year' specifies the year of a date.
+
`mon' specifies the month of the date.
+
`day' specifies the day of the date.
+
The return value is the day of week of the date. 0 means Sunday and 6 means Saturday.
+
+ +

API of Filesystem Utilities

+ +

The function `tcrealpath' is used in order to get the canonicalized absolute path of a file.

+ +
+
char *tcrealpath(const char *path);
+
`path' specifies the path of the file.
+
The return value is the canonicalized absolute path of a file, or `NULL' if the path is invalid.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcstatfile' is used in order to get the status information of a file.

+ +
+
bool tcstatfile(const char *path, bool *isdirp, int64_t *sizep, int64_t *mtimep);
+
`path' specifies the path of the file.
+
`isdirp' specifies the pointer to a variable into which whether the file is a directory is assigned. If it is `NULL', it is ignored.
+
`sizep' specifies the pointer to a variable into which the size of the file is assigned. If it is `NULL', it is ignored.
+
`ntimep' specifies the pointer to a variable into which the size of the file is assigned. If it is `NULL', it is ignored.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcreadfile' is used in order to read whole data of a file.

+ +
+
void *tcreadfile(const char *path, int limit, int *sp);
+
`path' specifies the path of the file. If it is `NULL', the standard input is specified.
+
`limit' specifies the limiting size of reading data. If it is not more than 0, the limitation is not specified.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned. If it is `NULL', it is not used.
+
The return value is the pointer to the allocated region of the read data, or `NULL' if the file could not be opened.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when when is no longer in use.
+
+ +

The function `tcreadfilelines' is used in order to read every line of a file.

+ +
+
TCLIST *tcreadfilelines(const char *path);
+
`path' specifies the path of the file. If it is `NULL', the standard input is specified.
+
The return value is a list object of every lines if successful, else it is `NULL'.
+
Line separators are cut out. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcwritefile' is used in order to write data into a file.

+ +
+
bool tcwritefile(const char *path, const void *ptr, int size);
+
`path' specifies the path of the file. If it is `NULL', the standard output is specified.
+
`ptr' specifies the pointer to the data region.
+
`size' specifies the size of the region.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tccopyfile' is used in order to copy a file.

+ +
+
bool tccopyfile(const char *src, const char *dest);
+
`src' specifies the path of the source file.
+
`dest' specifies the path of the destination file.
+
The return value is true if successful, else, it is false.
+
If the destination file exists, it is overwritten.
+
+ +

The function `tcreaddir' is used in order to read names of files in a directory.

+ +
+
TCLIST *tcreaddir(const char *path);
+
`path' specifies the path of the directory.
+
The return value is a list object of names if successful, else it is `NULL'.
+
Links to the directory itself and to the parent directory are ignored.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcglobpat' is used in order to expand a pattern into a list of matched paths.

+ +
+
TCLIST *tcglobpat(const char *pattern);
+
`pattern' specifies the matching pattern.
+
The return value is a list object of matched paths. If no path is matched, an empty list is returned.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcremovelink' is used in order to remove a file or a directory and its sub ones recursively.

+ +
+
bool tcremovelink(const char *path);
+
`path' specifies the path of the link.
+
If successful, the return value is true, else, it is false. False is returned when the link does not exist or the permission is denied.
+
+ +

The function `tcwrite' is used in order to write data into a file.

+ +
+
bool tcwrite(int fd, const void *buf, size_t size);
+
`fd' specifies the file descriptor.
+
`buf' specifies the buffer to be written.
+
`size' specifies the size of the buffer.
+
The return value is true if successful, else, it is false.
+
+ +

The function `tcread' is used in order to read data from a file.

+ +
+
bool tcread(int fd, void *buf, size_t size);
+
`fd' specifies the file descriptor.
+
`buf' specifies the buffer to store into.
+
`size' specifies the size of the buffer.
+
The return value is true if successful, else, it is false.
+
+ +

The function `tclock' is used in order to lock a file.

+ +
+
bool tclock(int fd, bool ex, bool nb);
+
`fd' specifies the file descriptor.
+
`ex' specifies whether an exclusive lock or a shared lock is performed.
+
`nb' specifies whether to request with non-blocking.
+
The return value is true if successful, else, it is false.
+
+ +

The function `tcunlock' is used in order to unlock a file.

+ +
+
bool tcunlock(int fd);
+
`fd' specifies the file descriptor.
+
The return value is true if successful, else, it is false.
+
+ +

The function `tcsystem' is used in order to execute a shell command.

+ +
+
int tcsystem(const char **args, int anum);
+
`args' specifies an array of the command name and its arguments.
+
`anum' specifies the number of elements of the array.
+
The return value is the exit code of the command or `INT_MAX' on failure.
+
The command name and the arguments are quoted and meta characters are escaped.
+
+ +

API of Encoding Utilities

+ +

The function `tcurlencode' is used in order to encode a serial object with URL encoding.

+ +
+
char *tcurlencode(const char *ptr, int size);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
The return value is the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if when is no longer in use.
+
+ +

The function `tcurldecode' is used in order to decode a string encoded with URL encoding.

+ +
+
char *tcurldecode(const char *str, int *sp);
+
`str' specifies the encoded string.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the result.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcurlbreak' is used in order to break up a URL into elements.

+ +
+
TCMAP *tcurlbreak(const char *str);
+
`str' specifies the URL string.
+
The return value is the map object whose keys are the name of elements. The key "self" indicates the URL itself. The key "scheme" indicates the scheme. The key "host" indicates the host of the server. The key "port" indicates the port number of the server. The key "authority" indicates the authority information. The key "path" indicates the path of the resource. The key "file" indicates the file name without the directory section. The key "query" indicates the query string. The key "fragment" indicates the fragment string.
+
Supported schema are HTTP, HTTPS, FTP, and FILE. Absolute URL and relative URL are supported. Because the object of the return value is created with the function `tcmapnew', it should be deleted with the function `tcmapdel' when it is no longer in use.
+
+ +

The function `tcurlresolve' is used in order to resolve a relative URL with an absolute URL.

+ +
+
char *tcurlresolve(const char *base, const char *target);
+
`base' specifies the absolute URL of the base location.
+
`target' specifies the URL to be resolved.
+
The return value is the resolved URL. If the target URL is relative, a new URL of relative location from the base location is returned. Else, a copy of the target URL is returned.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbaseencode' is used in order to encode a serial object with Base64 encoding.

+ +
+
char *tcbaseencode(const char *ptr, int size);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
The return value is the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if when is no longer in use.
+
+ +

The function `tcbasedecode' is used in order to decode a string encoded with Base64 encoding.

+ +
+
char *tcbasedecode(const char *str, int *sp);
+
`str' specifies the encoded string.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the result.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcquoteencode' is used in order to encode a serial object with Quoted-printable encoding.

+ +
+
char *tcquoteencode(const char *ptr, int size);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
The return value is the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if when is no longer in use.
+
+ +

The function `tcquotedecode' is used in order to decode a string encoded with Quoted-printable encoding.

+ +
+
char *tcquotedecode(const char *str, int *sp);
+
`str' specifies the encoded string.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the result.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmimeencode' is used in order to encode a string with MIME encoding.

+ +
+
char *tcmimeencode(const char *str, const char *encname, bool base);
+
`str' specifies the string.
+
`encname' specifies the string of the name of the character encoding.
+
`base' specifies whether to use Base64 encoding. If it is false, Quoted-printable is used.
+
The return value is the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmimedecode' is used in order to decode a string encoded with MIME encoding.

+ +
+
char *tcmimedecode(const char *str, char *enp);
+
`str' specifies the encoded string.
+
`enp' specifies the pointer to the region into which the name of encoding is written. If it is `NULL', it is not used. The size of the buffer should be equal to or more than 32 bytes.
+
The return value is the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmimebreak' is used in order to split a string of MIME into headers and the body.

+ +
+
char *tcmimebreak(const char *ptr, int size, TCMAP *headers, int *sp);
+
`ptr' specifies the pointer to the region of MIME data.
+
`size' specifies the size of the region.
+
`headers' specifies a map object to store headers. If it is `NULL', it is not used. Each key of the map is an uncapitalized header name.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the body data.
+
If the content type is defined, the header map has the key "TYPE" specifying the type. If the character encoding is defined, the key "CHARSET" indicates the encoding name. If the boundary string of multipart is defined, the key "BOUNDARY" indicates the string. If the content disposition is defined, the key "DISPOSITION" indicates the direction. If the file name is defined, the key "FILENAME" indicates the name. If the attribute name is defined, the key "NAME" indicates the name. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmimeparts' is used in order to split multipart data of MIME into its parts.

+ +
+
TCLIST *tcmimeparts(const char *ptr, int size, const char *boundary);
+
`ptr' specifies the pointer to the region of multipart data of MIME.
+
`size' specifies the size of the region.
+
`boundary' specifies the boundary string.
+
The return value is a list object. Each element of the list is the data of a part.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tchexencode' is used in order to encode a serial object with hexadecimal encoding.

+ +
+
char *tchexencode(const char *ptr, int size);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
The return value is the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if when is no longer in use.
+
+ +

The function `tchexdecode' is used in order to decode a string encoded with hexadecimal encoding.

+ +
+
char *tchexdecode(const char *str, int *sp);
+
`str' specifies the encoded string.
+
`sp' specifies the pointer to a variable into which the size of the region of the return
+
value is assigned.
+
The return value is the pointer to the region of the result.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcpackencode' is used in order to compress a serial object with Packbits encoding.

+ +
+
char *tcpackencode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcpackdecode' is used in order to decompress a serial object compressed with Packbits encoding.

+ +
+
char *tcpackdecode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbsencode' is used in order to compress a serial object with TCBS encoding.

+ +
+
char *tcbsencode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbsdecode' is used in order to decompress a serial object compressed with TCBS encoding.

+ +
+
char *tcbsdecode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcdeflate' is used in order to compress a serial object with Deflate encoding.

+ +
+
char *tcdeflate(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcinflate' is used in order to decompress a serial object compressed with Deflate encoding.

+ +
+
char *tcinflate(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcgzipencode' is used in order to compress a serial object with GZIP encoding.

+ +
+
char *tcgzipencode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcgzipdecode' is used in order to decompress a serial object compressed with GZIP encoding.

+ +
+
char *tcgzipdecode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcgetcrc' is used in order to get the CRC32 checksum of a serial object.

+ +
+
unsigned int tcgetcrc(const char *ptr, int size);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
The return value is the CRC32 checksum of the object.
+
+ +

The function `tcbzipencode' is used in order to compress a serial object with BZIP2 encoding.

+ +
+
char *tcbzipencode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbzipdecode' is used in order to decompress a serial object compressed with BZIP2 encoding.

+ +
+
char *tcbzipdecode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcberencode' is used in order to encode an array of nonnegative integers with BER encoding.

+ +
+
char *tcberencode(const unsigned int *ary, int anum, int *sp);
+
`ary' specifies the pointer to the array of nonnegative integers.
+
`anum' specifies the size of the array.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the result.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if when is no longer in use.
+
+ +

The function `tcberdecode' is used in order to decode a serial object encoded with BER encoding.

+ +
+
unsigned int *tcberdecode(const char *ptr, int size, int *np);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`np' specifies the pointer to a variable into which the number of elements of the return value is assigned.
+
The return value is the pointer to the array of the result.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if when is no longer in use.
+
+ +

The function `tcxmlescape' is used in order to escape meta characters in a string with the entity references of XML.

+ +
+
char *tcxmlescape(const char *str);
+
`str' specifies the string.
+
The return value is the pointer to the escaped string.
+
This function escapes only `&', `<', `>', and `"'. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcxmlunescape' is used in order to unescape entity references in a string of XML.

+ +
+
char *tcxmlunescape(const char *str);
+
`str' specifies the string.
+
The return value is the unescaped string.
+
This function restores only `&amp;', `&lt;', `&gt;', and `&quot;'. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

Example Code

+ +

The following code is an example using extensible string, array list, and hash map.

+ +
#include <tcutil.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+int main(int argc, char **argv){
+
+  { /* example to use an extensible string object */
+    TCXSTR *xstr;
+    /* create the object */
+    xstr = tcxstrnew();
+    /* concatenate strings */
+    tcxstrcat2(xstr, "hop");
+    tcxstrcat2(xstr, "step");
+    tcxstrcat2(xstr, "jump");
+    /* print the size and the content */
+    printf("%d:%s\n", tcxstrsize(xstr), (char *)tcxstrptr(xstr));
+    /* delete the object */
+    tcxstrdel(xstr);
+  }
+
+  { /* example to use a list object */
+    TCLIST *list;
+    int i;
+    /* create the object */
+    list = tclistnew();
+    /* add strings to the tail */
+    tclistpush2(list, "hop");
+    tclistpush2(list, "step");
+    tclistpush2(list, "jump");
+    /* print all elements */
+    for(i = 0; i < tclistnum(list); i++){
+      printf("%d:%s\n", i, tclistval2(list, i));
+    }
+    /* delete the object */
+    tclistdel(list);
+  }
+
+  { /* example to use a map object */
+    TCMAP *map;
+    const char *key;
+    /* create the object */
+    map = tcmapnew();
+    /* add records */
+    tcmapput2(map, "foo", "hop");
+    tcmapput2(map, "bar", "step");
+    tcmapput2(map, "baz", "jump");
+    /* print all records */
+    tcmapiterinit(map);
+    while((key = tcmapiternext2(map)) != NULL){
+      printf("%s:%s\n", key, tcmapget2(map, key));
+    }
+    /* delete the object */
+    tcmapdel(map);
+  }
+
+  { /* example to use a tree object */
+    TCTREE *tree;
+    const char *key;
+    /* create the object */
+    tree = tctreenew();
+    /* add records */
+    tctreeput2(tree, "foo", "hop");
+    tctreeput2(tree, "bar", "step");
+    tctreeput2(tree, "baz", "jump");
+    /* print all records */
+    tctreeiterinit(tree);
+    while((key = tctreeiternext2(tree)) != NULL){
+      printf("%s:%s\n", key, tctreeget2(tree, key));
+    }
+    /* delete the object */
+    tctreedel(tree);
+  }
+
+  return 0;
+}
+
+ +

CLI

+ +

To use the utility API easily, the commands `tcutest', `tcumttest', and `tcucodec' are provided.

+ +

The command `tcutest' is a utility for facility test and performance test. This command is used in the following format. `rnum' specifies the number of iterations. `anum' specifies the initial number of elements of array. `bnum' specifies the number of buckets.

+ +
+
tcutest xstr rnum
+
Perform test of extensible string.
+
tcutest list [-rd] rnum [anum]
+
Perform test of array list.
+
tcutest map [-rd] [-tr] [-rnd] [-dk|-dc|-dai|-dad|-dpr] rnum [bnum]
+
Perform test of hash map.
+
tcutest tree [-rd] [-tr] [-rnd] [-dk|-dc|-dai|-dad|-dpr] rnum
+
Perform test of ordered tree.
+
tcutest mdb [-rd] [-tr] [-rnd] [-dk|-dc|-dai|-dad|-dpr] rnum [bnum]
+
Perform test of on-memory hash database.
+
tcutest ndb [-rd] [-tr] [-rnd] [-dk|-dc|-dai|-dad|-dpr] rnum
+
Perform test of on-memory tree database.
+
tcutest misc rnum
+
Perform test of miscellaneous routines.
+
tcutest wicked rnum
+
Perform updating operations of list and map selected at random.
+
+ +

Options feature the following.

+ +
    +
  • -rd : perform the reading test also.
  • +
  • -tr : perform the iterator test also.
  • +
  • -rnd : select keys at random.
  • +
  • -dk : use the function `tcxxxputkeep' instead of `tcxxxput'.
  • +
  • -dc : use the function `tcxxxputcat' instead of `tcxxxput'.
  • +
  • -dai : use the function `tcxxxaddint' instead of `tcxxxput'.
  • +
  • -dad : use the function `tcxxxadddouble' instead of `tcxxxput'.
  • +
  • -dpr : use the function `tcxxxputproc' instead of `tcxxxput'.
  • +
+ +

This command returns 0 on success, another on failure.

+ +

The command `tcumttest' is a utility for facility test under multi-thread situation. This command is used in the following format. `tnum' specifies the number of running threads. `rnum' specifies the number of iterations. `bnum' specifies the number of buckets.

+ +
+
tcumttest combo [-rnd] tnum rnum [bnum]
+
Peform storing, retrieving, and removing in turn.
+
tcumttest typical [-nc] [-rr num] tnum rnum [bnum]
+
Perform typical operations selected at random.
+
+ +

Options feature the following.

+ +
    +
  • -rnd : select keys at random.
  • +
  • -nc : omit the comparison test.
  • +
  • -rr num : specify the ratio of reading operation by percentage.
  • +
+ +

This command returns 0 on success, another on failure.

+ +

The command `tcucodec' is a tool to use encoding and decoding features. This command is used in the following format. `file' specifies a input file. If it is omitted, the standard input is read.

+ +
+
tcucodec url [-d] [-br] [-rs base] [file]
+
Perform URL encoding and its decoding.
+
tcucodec base [-d] [file]
+
Perform Base64 encoding and its decoding.
+
tcucodec quote [-d] [file]
+
Perform quoted-printable encoding and its decoding.
+
tcucodec mime [-d] [-en name] [-q] [-on] [-hd] [-bd] [-part num] [file]
+
Perform MIME encoding and its decoding.
+
tcucodec hex [-d] [file]
+
Perform hexadecimal encoding and its decoding.
+
tcucodec pack [-d] [-bwt] [file]
+
Perform Packbits encoding and its decoding.
+
tcucodec tcbs [-d] [file]
+
Perform TCBS encoding and its decoding.
+
tcucodec zlib [-d] [-gz] [file]
+
Perform ZLIB encoding and its decoding.
+
tcucodec bzip [-d] [file]
+
Perform BZIP2 encoding and its decoding.
+
tcucodec xml [-d] [-br] [file]
+
Process XML. By default, escape meta characters.
+
tcucodec cstr [-d] [-js] [file]
+
Perform C-string escaping and its unescaping.
+
tcucodec ucs [-d] [-un] [-kw str] [file]
+
Convert UTF-8 string into UCS-2 array.
+
tcucodec hash [-crc] [-ch num] [file]
+
Calculate the hash value. By default, use MD5 function.
+
tcucodec cipher [-key str] [file]
+
Perform stream cipher and its decipher.
+
tcucodec date [-ds str] [-jl num] [-wf] [-rf]
+
Process date string. By default, print the current UNIX time.
+
tcucodec tmpl [-var name value] [file]
+
Perform template serialization.
+
tcucodec conf [-v|-i|-l|-p]
+
Print some configurations.
+
+ +

Options feature the following.

+ +
    +
  • -d : perform decoding (unescaping), not encoding (escaping).
  • +
  • -br : break up URL or XML into elements.
  • +
  • -rs base : specify the base URL and resolve the relative URL.
  • +
  • -en name : specify the input encoding, which is UTF-8 by default.
  • +
  • -q : use quoted-printable encoding, which is Base64 by default.
  • +
  • -on : output the charset name when decoding.
  • +
  • -bd : perform MIME parsing and output the body.
  • +
  • -hd : perform MIME parsing and output the headers.
  • +
  • -part num : perform MIME parsing and output the specified part.
  • +
  • -bwt : convert by BWT as preprocessing.
  • +
  • -gz : use GZIP format.
  • +
  • -crc : use CRC32 function.
  • +
  • -js : use JSON compatible format.
  • +
  • -un : perform UCS normalization.
  • +
  • -kw str : generate KWIC string.
  • +
  • -ch num : use consistent hashing function.
  • +
  • -key str : specify the cipher key.
  • +
  • -ds str : specify the time.
  • +
  • -jl num : specify the jet lag.
  • +
  • -wf : format the output in W3CDTF.
  • +
  • -rf : format the output in RFC 1123 format.
  • +
  • -var name value : specify a template variable.
  • +
  • -v : show the version number of Tokyo Cabinet.
  • +
  • -i : show options to include the headers of Tokyo Cabinet.
  • +
  • -l : show options to link the library of Tokyo Cabinet.
  • +
  • -p : show the directory path of the commands of Tokyo Cabinet.
  • +
+ +

This command returns 0 on success, another on failure.

+ +
+ +

The Hash Database API

+ +

Hash database is a file containing a hash table and is handled with the hash database API. See `tchdb.h' for the entire specification.

+ +

Description

+ +

To use the hash database API, include `tcutil.h', `tchdb.h', and related standard header files. Usually, write the following description near the front of a source file.

+ +
+
#include <tcutil.h>
+
#include <tchdb.h>
+
#include <stdlib.h>
+
#include <stdbool.h>
+
#include <stdint.h>
+
+ +

Objects whose type is pointer to `TCHDB' are used to handle hash databases. A hash database object is created with the function `tchdbnew' and is deleted with the function `tchdbdel'. To avoid memory leak, it is important to delete every object when it is no longer in use.

+ +

Before operations to store or retrieve records, it is necessary to open a database file and connect the hash database object to it. The function `tchdbopen' is used to open a database file and the function `tchdbclose' is used to close the database file. To avoid data missing or corruption, it is important to close every database file when it is no longer in use. It is forbidden for multible database objects in a process to open the same database at the same time.

+ +

API

+ +

The function `tchdberrmsg' is used in order to get the message string corresponding to an error code.

+ +
+
const char *tchdberrmsg(int ecode);
+
`ecode' specifies the error code.
+
The return value is the message string of the error code.
+
+ +

The function `tchdbnew' is used in order to create a hash database object.

+ +
+
TCHDB *tchdbnew(void);
+
The return value is the new hash database object.
+
+ +

The function `tchdbdel' is used in order to delete a hash database object.

+ +
+
void tchdbdel(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
If the database is not closed, it is closed implicitly. Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tchdbecode' is used in order to get the last happened error code of a hash database object.

+ +
+
int tchdbecode(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
The return value is the last happened error code.
+
The following error codes are defined: `TCESUCCESS' for success, `TCETHREAD' for threading error, `TCEINVALID' for invalid operation, `TCENOFILE' for file not found, `TCENOPERM' for no permission, `TCEMETA' for invalid meta data, `TCERHEAD' for invalid record header, `TCEOPEN' for open error, `TCECLOSE' for close error, `TCETRUNC' for trunc error, `TCESYNC' for sync error, `TCESTAT' for stat error, `TCESEEK' for seek error, `TCEREAD' for read error, `TCEWRITE' for write error, `TCEMMAP' for mmap error, `TCELOCK' for lock error, `TCEUNLINK' for unlink error, `TCERENAME' for rename error, `TCEMKDIR' for mkdir error, `TCERMDIR' for rmdir error, `TCEKEEP' for existing record, `TCENOREC' for no record found, and `TCEMISC' for miscellaneous error.
+
+ +

The function `tchdbsetmutex' is used in order to set mutual exclusion control of a hash database object for threading.

+ +
+
bool tchdbsetmutex(TCHDB *hdb);
+
`hdb' specifies the hash database object which is not opened.
+
If successful, the return value is true, else, it is false.
+
Note that the mutual exclusion control of the database should be set before the database is opened.
+
+ +

The function `tchdbtune' is used in order to set the tuning parameters of a hash database object.

+ +
+
bool tchdbtune(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
`hdb' specifies the hash database object which is not opened.
+
`bnum' specifies the number of elements of the bucket array. If it is not more than 0, the default value is specified. The default value is 131071. Suggested size of the bucket array is about from 0.5 to 4 times of the number of all records to be stored.
+
`apow' specifies the size of record alignment by power of 2. If it is negative, the default value is specified. The default value is 4 standing for 2^4=16.
+
`fpow' specifies the maximum number of elements of the free block pool by power of 2. If it is negative, the default value is specified. The default value is 10 standing for 2^10=1024.
+
`opts' specifies options by bitwise-or: `HDBTLARGE' specifies that the size of the database can be larger than 2GB by using 64-bit bucket array, `HDBTDEFLATE' specifies that each record is compressed with Deflate encoding, `HDBTBZIP' specifies that each record is compressed with BZIP2 encoding, `HDBTTCBS' specifies that each record is compressed with TCBS encoding.
+
If successful, the return value is true, else, it is false.
+
Note that the tuning parameters should be set before the database is opened.
+
+ +

The function `tchdbsetcache' is used in order to set the caching parameters of a hash database object.

+ +
+
bool tchdbsetcache(TCHDB *hdb, int32_t rcnum);
+
`hdb' specifies the hash database object which is not opened.
+
`rcnum' specifies the maximum number of records to be cached. If it is not more than 0, the record cache is disabled. It is disabled by default.
+
If successful, the return value is true, else, it is false.
+
Note that the caching parameters should be set before the database is opened.
+
+ +

The function `tchdbsetxmsiz' is used in order to set the size of the extra mapped memory of a hash database object.

+ +
+
bool tchdbsetxmsiz(TCHDB *hdb, int64_t xmsiz);
+
`hdb' specifies the hash database object which is not opened.
+
`xmsiz' specifies the size of the extra mapped memory. If it is not more than 0, the extra mapped memory is disabled. The default size is 67108864.
+
If successful, the return value is true, else, it is false.
+
Note that the mapping parameters should be set before the database is opened.
+
+ +

The function `tchdbsetdfunit' is used in order to set the unit step number of auto defragmentation of a hash database object.

+ +
+
bool tchdbsetdfunit(TCHDB *hdb, int32_t dfunit);
+
`hdb' specifies the hash database object which is not opened.
+
`dfunit' specifie the unit step number. If it is not more than 0, the auto defragmentation is disabled. It is disabled by default.
+
If successful, the return value is true, else, it is false.
+
Note that the defragmentation parameters should be set before the database is opened.
+
+ +

The function `tchdbopen' is used in order to open a database file and connect a hash database object.

+ +
+
bool tchdbopen(TCHDB *hdb, const char *path, int omode);
+
`hdb' specifies the hash database object which is not opened.
+
`path' specifies the path of the database file.
+
`omode' specifies the connection mode: `HDBOWRITER' as a writer, `HDBOREADER' as a reader. If the mode is `HDBOWRITER', the following may be added by bitwise-or: `HDBOCREAT', which means it creates a new database if not exist, `HDBOTRUNC', which means it creates a new database regardless if one exists, `HDBOTSYNC', which means every transaction synchronizes updated contents with the device. Both of `HDBOREADER' and `HDBOWRITER' can be added to by bitwise-or: `HDBONOLCK', which means it opens the database file without file locking, or `HDBOLCKNB', which means locking is performed without blocking.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tchdbclose' is used in order to close a hash database object.

+ +
+
bool tchdbclose(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
If successful, the return value is true, else, it is false.
+
Update of a database is assured to be written when the database is closed. If a writer opens a database but does not close it appropriately, the database will be broken.
+
+ +

The function `tchdbput' is used in order to store a record into a hash database object.

+ +
+
bool tchdbput(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`hdb' specifies the hash database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tchdbput2' is used in order to store a string record into a hash database object.

+ +
+
bool tchdbput2(TCHDB *hdb, const char *kstr, const char *vstr);
+
`hdb' specifies the hash database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tchdbputkeep' is used in order to store a new record into a hash database object.

+ +
+
bool tchdbputkeep(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`hdb' specifies the hash database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tchdbputkeep2' is used in order to store a new string record into a hash database object.

+ +
+
bool tchdbputkeep2(TCHDB *hdb, const char *kstr, const char *vstr);
+
`hdb' specifies the hash database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tchdbputcat' is used in order to concatenate a value at the end of the existing record in a hash database object.

+ +
+
bool tchdbputcat(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`hdb' specifies the hash database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tchdbputcat2' is used in order to concatenate a string value at the end of the existing record in a hash database object.

+ +
+
bool tchdbputcat2(TCHDB *hdb, const char *kstr, const char *vstr);
+
`hdb' specifies the hash database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tchdbputasync' is used in order to store a record into a hash database object in asynchronous fashion.

+ +
+
bool tchdbputasync(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`hdb' specifies the hash database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten. Records passed to this function are accumulated into the inner buffer and wrote into the file at a blast.
+
+ +

The function `tchdbputasync2' is used in order to store a string record into a hash database object in asynchronous fashion.

+ +
+
bool tchdbputasync2(TCHDB *hdb, const char *kstr, const char *vstr);
+
`hdb' specifies the hash database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten. Records passed to this function are accumulated into the inner buffer and wrote into the file at a blast.
+
+ +

The function `tchdbout' is used in order to remove a record of a hash database object.

+ +
+
bool tchdbout(TCHDB *hdb, const void *kbuf, int ksiz);
+
`hdb' specifies the hash database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tchdbout2' is used in order to remove a string record of a hash database object.

+ +
+
bool tchdbout2(TCHDB *hdb, const char *kstr);
+
`hdb' specifies the hash database object connected as a writer.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tchdbget' is used in order to retrieve a record in a hash database object.

+ +
+
void *tchdbget(TCHDB *hdb, const void *kbuf, int ksiz, int *sp);
+
`hdb' specifies the hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tchdbget2' is used in order to retrieve a string record in a hash database object.

+ +
+
char *tchdbget2(TCHDB *hdb, const char *kstr);
+
`hdb' specifies the hash database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tchdbget3' is used in order to retrieve a record in a hash database object and write the value into a buffer.

+ +
+
int tchdbget3(TCHDB *hdb, const void *kbuf, int ksiz, void *vbuf, int max);
+
`hdb' specifies the hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the buffer into which the value of the corresponding record is written.
+
`max' specifies the size of the buffer.
+
If successful, the return value is the size of the written data, else, it is -1. -1 is returned if no record corresponds to the specified key.
+
Note that an additional zero code is not appended at the end of the region of the writing buffer.
+
+ +

The function `tchdbvsiz' is used in order to get the size of the value of a record in a hash database object.

+ +
+
int tchdbvsiz(TCHDB *hdb, const void *kbuf, int ksiz);
+
`hdb' specifies the hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tchdbvsiz2' is used in order to get the size of the value of a string record in a hash database object.

+ +
+
int tchdbvsiz2(TCHDB *hdb, const char *kstr);
+
`hdb' specifies the hash database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tchdbiterinit' is used in order to initialize the iterator of a hash database object.

+ +
+
bool tchdbiterinit(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
If successful, the return value is true, else, it is false.
+
The iterator is used in order to access the key of every record stored in a database.
+
+ +

The function `tchdbiternext' is used in order to get the next key of the iterator of a hash database object.

+ +
+
void *tchdbiternext(TCHDB *hdb, int *sp);
+
`hdb' specifies the hash database object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. It is allowed to update or remove records whose keys are fetched while the iteration. However, it is not assured if updating the database is occurred while the iteration. Besides, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access.
+
+ +

The function `tchdbiternext2' is used in order to get the next key string of the iterator of a hash database object.

+ +
+
char *tchdbiternext2(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
If successful, the return value is the string of the next key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. However, it is not assured if updating the database is occurred while the iteration. Besides, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access.
+
+ +

The function `tchdbiternext3' is used in order to get the next extensible objects of the iterator of a hash database object.

+ +
+
bool tchdbiternext3(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr);
+
`hdb' specifies the hash database object.
+
`kxstr' specifies the object into which the next key is wrote down.
+
`vxstr' specifies the object into which the next value is wrote down.
+
If successful, the return value is true, else, it is false. False is returned when no record is to be get out of the iterator.
+
+ +

The function `tchdbfwmkeys' is used in order to get forward matching keys in a hash database object.

+ +
+
TCLIST *tchdbfwmkeys(TCHDB *hdb, const void *pbuf, int psiz, int max);
+
`hdb' specifies the hash database object.
+
`pbuf' specifies the pointer to the region of the prefix.
+
`psiz' specifies the size of the region of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tchdbfwmkeys2' is used in order to get forward matching string keys in a hash database object.

+ +
+
TCLIST *tchdbfwmkeys2(TCHDB *hdb, const char *pstr, int max);
+
`hdb' specifies the hash database object.
+
`pstr' specifies the string of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tchdbaddint' is used in order to add an integer to a record in a hash database object.

+ +
+
int tchdbaddint(TCHDB *hdb, const void *kbuf, int ksiz, int num);
+
`hdb' specifies the hash database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is `INT_MIN'.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tchdbdbadddouble' is used in order to add a real number to a record in a hash database object.

+ +
+
double tchdbadddouble(TCHDB *hdb, const void *kbuf, int ksiz, double num);
+
`hdb' specifies the hash database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is Not-a-Number.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tchdbsync' is used in order to synchronize updated contents of a hash database object with the file and the device.

+ +
+
bool tchdbsync(TCHDB *hdb);
+
`hdb' specifies the hash database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
This function is useful when another process connects to the same database file.
+
+ +

The function `tchdboptimize' is used in order to optimize the file of a hash database object.

+ +
+
bool tchdboptimize(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
`hdb' specifies the hash database object connected as a writer.
+
`bnum' specifies the number of elements of the bucket array. If it is not more than 0, the default value is specified. The default value is two times of the number of records.
+
`apow' specifies the size of record alignment by power of 2. If it is negative, the current setting is not changed.
+
`fpow' specifies the maximum number of elements of the free block pool by power of 2. If it is negative, the current setting is not changed.
+
`opts' specifies options by bitwise-or: `HDBTLARGE' specifies that the size of the database can be larger than 2GB by using 64-bit bucket array, `HDBTDEFLATE' specifies that each record is compressed with Deflate encoding, `HDBTBZIP' specifies that each record is compressed with BZIP2 encoding, `HDBTTCBS' specifies that each record is compressed with TCBS encoding. If it is `UINT8_MAX', the current setting is not changed.
+
If successful, the return value is true, else, it is false.
+
This function is useful to reduce the size of the database file with data fragmentation by successive updating.
+
+ +

The function `tchdbvanish' is used in order to remove all records of a hash database object.

+ +
+
bool tchdbvanish(TCHDB *hdb);
+
`hdb' specifies the hash database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tchdbcopy' is used in order to copy the database file of a hash database object.

+ +
+
bool tchdbcopy(TCHDB *hdb, const char *path);
+
`hdb' specifies the hash database object.
+
`path' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line.
+
If successful, the return value is true, else, it is false. False is returned if the executed command returns non-zero code.
+
The database file is assured to be kept synchronized and not modified while the copying or executing operation is in progress. So, this function is useful to create a backup file of the database file.
+
+ +

The function `tchdbtranbegin' is used in order to begin the transaction of a hash database object.

+ +
+
bool tchdbtranbegin(TCHDB *hdb);
+
`hdb' specifies the hash database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
The database is locked by the thread while the transaction so that only one transaction can be activated with a database object at the same time. Thus, the serializable isolation level is assumed if every database operation is performed in the transaction. All updated regions are kept track of by write ahead logging while the transaction. If the database is closed during transaction, the transaction is aborted implicitly.
+
+ +

The function `tchdbtrancommit' is used in order to commit the transaction of a hash database object.

+ +
+
bool tchdbtrancommit(TCHDB *hdb);
+
`hdb' specifies the hash database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is fixed when it is committed successfully.
+
+ +

The function `tchdbtranabort' is used in order to abort the transaction of a hash database object.

+ +
+
bool tchdbtranabort(TCHDB *hdb);
+
`hdb' specifies the hash database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is discarded when it is aborted. The state of the database is rollbacked to before transaction.
+
+ +

The function `tchdbpath' is used in order to get the file path of a hash database object.

+ +
+
const char *tchdbpath(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
The return value is the path of the database file or `NULL' if the object does not connect to any database file.
+
+ +

The function `tchdbrnum' is used in order to get the number of records of a hash database object.

+ +
+
uint64_t tchdbrnum(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
The return value is the number of records or 0 if the object does not connect to any database file.
+
+ +

The function `tchdbfsiz' is used in order to get the size of the database file of a hash database object.

+ +
+
uint64_t tchdbfsiz(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
The return value is the size of the database file or 0 if the object does not connect to any database file.
+
+ +

Example Code

+ +

The following code is an example to use a hash database.

+ +
#include <tcutil.h>
+#include <tchdb.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+int main(int argc, char **argv){
+  TCHDB *hdb;
+  int ecode;
+  char *key, *value;
+
+  /* create the object */
+  hdb = tchdbnew();
+
+  /* open the database */
+  if(!tchdbopen(hdb, "casket.tch", HDBOWRITER | HDBOCREAT)){
+    ecode = tchdbecode(hdb);
+    fprintf(stderr, "open error: %s\n", tchdberrmsg(ecode));
+  }
+
+  /* store records */
+  if(!tchdbput2(hdb, "foo", "hop") ||
+     !tchdbput2(hdb, "bar", "step") ||
+     !tchdbput2(hdb, "baz", "jump")){
+    ecode = tchdbecode(hdb);
+    fprintf(stderr, "put error: %s\n", tchdberrmsg(ecode));
+  }
+
+  /* retrieve records */
+  value = tchdbget2(hdb, "foo");
+  if(value){
+    printf("%s\n", value);
+    free(value);
+  } else {
+    ecode = tchdbecode(hdb);
+    fprintf(stderr, "get error: %s\n", tchdberrmsg(ecode));
+  }
+
+  /* traverse records */
+  tchdbiterinit(hdb);
+  while((key = tchdbiternext2(hdb)) != NULL){
+    value = tchdbget2(hdb, key);
+    if(value){
+      printf("%s:%s\n", key, value);
+      free(value);
+    }
+    free(key);
+  }
+
+  /* close the database */
+  if(!tchdbclose(hdb)){
+    ecode = tchdbecode(hdb);
+    fprintf(stderr, "close error: %s\n", tchdberrmsg(ecode));
+  }
+
+  /* delete the object */
+  tchdbdel(hdb);
+
+  return 0;
+}
+
+ +

CLI

+ +

To use the hash database API easily, the commands `tchtest', `tchmttest', and `tchmgr' are provided.

+ +

The command `tchtest' is a utility for facility test and performance test. This command is used in the following format. `path' specifies the path of a database file. `rnum' specifies the number of iterations. `bnum' specifies the number of buckets. `apow' specifies the power of the alignment. `fpow' specifies the power of the free block pool.

+ +
+
tchtest write [-mt] [-tl] [-td|-tb|-tt|-tx] [-rc num] [-xm num] [-df num] [-nl|-nb] [-as] [-rnd] path rnum [bnum [apow [fpow]]]
+
Store records with keys of 8 bytes. They change as `00000001', `00000002'...
+
tchtest read [-mt] [-rc num] [-xm num] [-df num] [-nl|-nb] [-wb] [-rnd] path
+
Retrieve all records of the database above.
+
tchtest remove [-mt] [-rc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path
+
Remove all records of the database above.
+
tchtest rcat [-mt] [-tl] [-td|-tb|-tt|-tx] [-rc num] [-xm num] [-df num] [-nl|-nb] [-pn num] [-dai|-dad|-rl|-ru] path rnum [bnum [apow [fpow]]]
+
Store records with partway duplicated keys using concatenate mode.
+
tchtest misc [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum
+
Perform miscellaneous test of various operations.
+
tchtest wicked [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum
+
Perform updating operations selected at random.
+
+ +

Options feature the following.

+ +
    +
  • -mt : call the function `tchdbsetmutex'.
  • +
  • -tl : enable the option `HDBTLARGE'.
  • +
  • -td : enable the option `HDBTDEFLATE'.
  • +
  • -tb : enable the option `HDBTBZIP'.
  • +
  • -tt : enable the option `HDBTTCBS'.
  • +
  • -tx : enable the option `HDBTEXCODEC'.
  • +
  • -rc num : specify the number of cached records.
  • +
  • -xm num : specify the size of the extra mapped memory.
  • +
  • -df num : specify the unit step number of auto defragmentation.
  • +
  • -nl : enable the option `HDBNOLCK'.
  • +
  • -nb : enable the option `HDBLCKNB'.
  • +
  • -as : use the function `tchdbputasync' instead of `tchdbput'.
  • +
  • -rnd : select keys at random.
  • +
  • -wb : use the function `tchdbget3' instead of `tchdbget'.
  • +
  • -pn num : specify the number of patterns.
  • +
  • -dai : use the function `tchdbaddint' instead of `tchdbputcat'.
  • +
  • -dad : use the function `tchdbadddouble' instead of `tchdbputcat'.
  • +
  • -rl : set the length of values at random.
  • +
  • -ru : select update operations at random.
  • +
+ +

This command returns 0 on success, another on failure.

+ +

The command `tchmttest' is a utility for facility test under multi-thread situation. This command is used in the following format. `path' specifies the path of a database file. `tnum' specifies the number of running threads. `rnum' specifies the number of iterations. `bnum' specifies the number of buckets. `apow' specifies the power of the alignment. `fpow' specifies the power of the free block pool.

+ +
+
tchmttest write [-tl] [-td|-tb|-tt|-tx] [-rc num] [-xm num] [-df num] [-nl|-nb] [-as] [-rnd] path tnum rnum [bnum [apow [fpow]]]
+
Store records with keys of 8 bytes. They change as `00000001', `00000002'...
+
tchmttest read [-rc num] [-xm num] [-df num] [-nl|-nb] [-wb] [-rnd] path tnum
+
Retrieve all records of the database above.
+
tchmttest remove [-rc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path tnum
+
Remove all records of the database above.
+
tchmttest wicked [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] [-nc] path tnum rnum
+
Perform updating operations selected at random.
+
tchmttest typical [-tl] [-td|-tb|-tt|-tx] [-rc num] [-xm num] [-df num] [-nl|-nb] [-nc] [-rr num] path tnum rnum [bnum [apow [fpow]]
+
Perform typical operations selected at random.
+
tchmttest race [-tl] [-td|-tb|-tt|-tx] [-xm num] [-df num] [-nl|-nb] path tnum rnum [bnum [apow [fpow]]
+
Perform race condition test.
+
+ +

Options feature the following.

+ +
    +
  • -tl : enable the option `HDBTLARGE'.
  • +
  • -td : enable the option `HDBTDEFLATE'.
  • +
  • -tb : enable the option `HDBTBZIP'.
  • +
  • -tt : enable the option `HDBTTCBS'.
  • +
  • -tx : enable the option `HDBTEXCODEC'.
  • +
  • -rc num : specify the number of cached records.
  • +
  • -xm num : specify the size of the extra mapped memory.
  • +
  • -df num : specify the unit step number of auto defragmentation.
  • +
  • -nl : enable the option `HDBNOLCK'.
  • +
  • -nb : enable the option `HDBLCKNB'.
  • +
  • -as : use the function `tchdbputasync' instead of `tchdbput'.
  • +
  • -rnd : select keys at random.
  • +
  • -wb : use the function `tchdbget3' instead of `tchdbget'.
  • +
  • -nc : omit the comparison test.
  • +
  • -rr num : specify the ratio of reading operation by percentage.
  • +
+ +

This command returns 0 on success, another on failure.

+ +

The command `tchmgr' is a utility for test and debugging of the hash database API and its applications. `path' specifies the path of a database file. `bnum' specifies the number of buckets. `apow' specifies the power of the alignment. `fpow' specifies the power of the free block pool. `key' specifies the key of a record. `value' specifies the value of a record. `file' specifies the input file.

+ +
+
tchmgr create [-tl] [-td|-tb|-tt|-tx] path [bnum [apow [fpow]]]
+
Create a database file.
+
tchmgr inform [-nl|-nb] path
+
Print miscellaneous information to the standard output.
+
tchmgr put [-nl|-nb] [-sx] [-dk|-dc|-dai|-dad] path key value
+
Store a record.
+
tchmgr out [-nl|-nb] [-sx] path key
+
Remove a record.
+
tchmgr get [-nl|-nb] [-sx] [-px] [-pz] path key
+
Print the value of a record.
+
tchmgr list [-nl|-nb] [-m num] [-pv] [-px] [-fm str] path
+
Print keys of all records, separated by line feeds.
+
tchmgr optimize [-tl] [-td|-tb|-tt|-tx] [-tz] [-nl|-nb] [-df] path [bnum [apow [fpow]]]
+
Optimize a database file.
+
tchmgr importtsv [-nl|-nb] [-sc] path [file]
+
Store records of TSV in each line of a file.
+
tchmgr version
+
Print the version information of Tokyo Cabinet.
+
+ +

Options feature the following.

+ +
    +
  • -tl : enable the option `HDBTLARGE'.
  • +
  • -td : enable the option `HDBTDEFLATE'.
  • +
  • -tb : enable the option `HDBTBZIP'.
  • +
  • -tt : enable the option `HDBTTCBS'.
  • +
  • -tx : enable the option `HDBTEXCODEC'.
  • +
  • -nl : enable the option `HDBNOLCK'.
  • +
  • -nb : enable the option `HDBLCKNB'.
  • +
  • -sx : the input data is evaluated as a hexadecimal data string.
  • +
  • -dk : use the function `tchdbputkeep' instead of `tchdbput'.
  • +
  • -dc : use the function `tchdbputcat' instead of `tchdbput'.
  • +
  • -dai : use the function `tchdbaddint' instead of `tchdbput'.
  • +
  • -dad : use the function `tchdbadddouble' instead of `tchdbput'.
  • +
  • -px : the output data is converted into a hexadecimal data string.
  • +
  • -pz : do not append line feed at the end of the output.
  • +
  • -m num : specify the maximum number of the output.
  • +
  • -pv : print values of records also.
  • +
  • -fm str : specify the prefix of keys.
  • +
  • -tz : enable the option `UINT8_MAX'.
  • +
  • -df : perform defragmentation only.
  • +
  • -sc : normalize keys as lower cases.
  • +
+ +

This command returns 0 on success, another on failure.

+ +
+ +

The B+ Tree Database API

+ +

B+ tree database is a file containing a B+ tree and is handled with the B+ tree database API. See `tcbdb.h' for the entire specification.

+ +

Description

+ +

To use the B+ tree database API, include `tcutil.h', `tcbdb.h', and related standard header files. Usually, write the following description near the front of a source file.

+ +
+
#include <tcutil.h>
+
#include <tcbdb.h>
+
#include <stdlib.h>
+
#include <stdbool.h>
+
#include <stdint.h>
+
+ +

Objects whose type is pointer to `TCBDB' are used to handle B+ tree databases. A B+ tree database object is created with the function `tcbdbnew' and is deleted with the function `tcbdbdel'. To avoid memory leak, it is important to delete every object when it is no longer in use.

+ +

Before operations to store or retrieve records, it is necessary to open a database file and connect the B+ tree database object to it. The function `tcbdbopen' is used to open a database file and the function `tcbdbclose' is used to close the database file. To avoid data missing or corruption, it is important to close every database file when it is no longer in use. It is forbidden for multible database objects in a process to open the same database at the same time.

+ +

API

+ +

The function `tcbdberrmsg' is used in order to get the message string corresponding to an error code.

+ +
+
const char *tcbdberrmsg(int ecode);
+
`ecode' specifies the error code.
+
The return value is the message string of the error code.
+
+ +

The function `tcbdbnew' is used in order to create a B+ tree database object.

+ +
+
TCBDB *tcbdbnew(void);
+
The return value is the new B+ tree database object.
+
+ +

The function `tcbdbdel' is used in order to delete a B+ tree database object.

+ +
+
void tcbdbdel(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object.
+
If the database is not closed, it is closed implicitly. Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tcbdbecode' is used in order to get the last happened error code of a B+ tree database object.

+ +
+
int tcbdbecode(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object.
+
The return value is the last happened error code.
+
The following error codes are defined: `TCESUCCESS' for success, `TCETHREAD' for threading error, `TCEINVALID' for invalid operation, `TCENOFILE' for file not found, `TCENOPERM' for no permission, `TCEMETA' for invalid meta data, `TCERHEAD' for invalid record header, `TCEOPEN' for open error, `TCECLOSE' for close error, `TCETRUNC' for trunc error, `TCESYNC' for sync error, `TCESTAT' for stat error, `TCESEEK' for seek error, `TCEREAD' for read error, `TCEWRITE' for write error, `TCEMMAP' for mmap error, `TCELOCK' for lock error, `TCEUNLINK' for unlink error, `TCERENAME' for rename error, `TCEMKDIR' for mkdir error, `TCERMDIR' for rmdir error, `TCEKEEP' for existing record, `TCENOREC' for no record found, and `TCEMISC' for miscellaneous error.
+
+ +

The function `tcbdbsetmutex' is used in order to set mutual exclusion control of a B+ tree database object for threading.

+ +
+
bool tcbdbsetmutex(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object which is not opened.
+
If successful, the return value is true, else, it is false.
+
Note that the mutual exclusion control of the database should be set before the database is opened.
+
+ +

The function `tcbdbsetcmpfunc' is used in order to set the custom comparison function of a B+ tree database object.

+ +
+
bool tcbdbsetcmpfunc(TCBDB *bdb, TCCMP cmp, void *cmpop);
+
`bdb' specifies the B+ tree database object which is not opened.
+
`cmp' specifies the pointer to the custom comparison function. It receives five parameters. The first parameter is the pointer to the region of one key. The second parameter is the size of the region of one key. The third parameter is the pointer to the region of the other key. The fourth parameter is the size of the region of the other key. The fifth parameter is the pointer to the optional opaque object. It returns positive if the former is big, negative if the latter is big, 0 if both are equivalent.
+
`cmpop' specifies an arbitrary pointer to be given as a parameter of the comparison function. If it is not needed, `NULL' can be specified.
+
If successful, the return value is true, else, it is false.
+
The default comparison function compares keys of two records by lexical order. The functions `tccmplexical' (dafault), `tccmpdecimal', `tccmpint32', and `tccmpint64' are built-in. Note that the comparison function should be set before the database is opened. Moreover, user-defined comparison functions should be set every time the database is being opened.
+
+ +

The function `tcbdbtune' is used in order to set the tuning parameters of a B+ tree database object.

+ +
+
bool tcbdbtune(TCBDB *bdb, int32_t lmemb, int32_t nmemb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
`bdb' specifies the B+ tree database object which is not opened.
+
`lmemb' specifies the number of members in each leaf page. If it is not more than 0, the default value is specified. The default value is 128.
+
`nmemb' specifies the number of members in each non-leaf page. If it is not more than 0, the default value is specified. The default value is 256.
+
`bnum' specifies the number of elements of the bucket array. If it is not more than 0, the default value is specified. The default value is 32749. Suggested size of the bucket array is about from 1 to 4 times of the number of all pages to be stored.
+
`apow' specifies the size of record alignment by power of 2. If it is negative, the default value is specified. The default value is 8 standing for 2^8=256.
+
`fpow' specifies the maximum number of elements of the free block pool by power of 2. If it is negative, the default value is specified. The default value is 10 standing for 2^10=1024.
+
`opts' specifies options by bitwise-or: `BDBTLARGE' specifies that the size of the database can be larger than 2GB by using 64-bit bucket array, `BDBTDEFLATE' specifies that each page is compressed with Deflate encoding, `BDBTBZIP' specifies that each page is compressed with BZIP2 encoding, `BDBTTCBS' specifies that each page is compressed with TCBS encoding.
+
If successful, the return value is true, else, it is false.
+
Note that the tuning parameters should be set before the database is opened.
+
+ +

The function `tcbdbsetcache' is used in order to set the caching parameters of a B+ tree database object.

+ +
+
bool tcbdbsetcache(TCBDB *bdb, int32_t lcnum, int32_t ncnum);
+
`bdb' specifies the B+ tree database object which is not opened.
+
`lcnum' specifies the maximum number of leaf nodes to be cached. If it is not more than 0, the default value is specified. The default value is 1024.
+
`ncnum' specifies the maximum number of non-leaf nodes to be cached. If it is not more than 0, the default value is specified. The default value is 512.
+
If successful, the return value is true, else, it is false.
+
Note that the caching parameters should be set before the database is opened.
+
+ +

The function `tcbdbsetxmsiz' is used in order to set the size of the extra mapped memory of a B+ tree database object.

+ +
+
bool tcbdbsetxmsiz(TCBDB *bdb, int64_t xmsiz);
+
`bdb' specifies the B+ tree database object which is not opened.
+
`xmsiz' specifies the size of the extra mapped memory. If it is not more than 0, the extra mapped memory is disabled. It is disabled by default.
+
If successful, the return value is true, else, it is false.
+
Note that the mapping parameters should be set before the database is opened.
+
+ +

The function `tcbdbsetdfunit' is used in order to set the unit step number of auto defragmentation of a B+ tree database object.

+ +
+
bool tcbdbsetdfunit(TCBDB *bdb, int32_t dfunit);
+
`bdb' specifies the B+ tree database object which is not opened.
+
`dfunit' specifie the unit step number. If it is not more than 0, the auto defragmentation is disabled. It is disabled by default.
+
If successful, the return value is true, else, it is false.
+
Note that the defragmentation parameter should be set before the database is opened.
+
+ +

The function `tcbdbopen' is used in order to open a database file and connect a B+ tree database object.

+ +
+
bool tcbdbopen(TCBDB *bdb, const char *path, int omode);
+
`bdb' specifies the B+ tree database object which is not opened.
+
`path' specifies the path of the database file.
+
`omode' specifies the connection mode: `BDBOWRITER' as a writer, `BDBOREADER' as a reader. If the mode is `BDBOWRITER', the following may be added by bitwise-or: `BDBOCREAT', which means it creates a new database if not exist, `BDBOTRUNC', which means it creates a new database regardless if one exists, `BDBOTSYNC', which means every transaction synchronizes updated contents with the device. Both of `BDBOREADER' and `BDBOWRITER' can be added to by bitwise-or: `BDBONOLCK', which means it opens the database file without file locking, or `BDBOLCKNB', which means locking is performed without blocking.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcbdbclose' is used in order to close a B+ tree database object.

+ +
+
bool tcbdbclose(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object.
+
If successful, the return value is true, else, it is false.
+
Update of a database is assured to be written when the database is closed. If a writer opens a database but does not close it appropriately, the database will be broken.
+
+ +

The function `tcbdbput' is used in order to store a record into a B+ tree database object.

+ +
+
bool tcbdbput(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcbdbput2' is used in order to store a string record into a B+ tree database object.

+ +
+
bool tcbdbput2(TCBDB *bdb, const char *kstr, const char *vstr);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcbdbputkeep' is used in order to store a new record into a B+ tree database object.

+ +
+
bool tcbdbputkeep(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcbdbputkeep2' is used in order to store a new string record into a B+ tree database object.

+ +
+
bool tcbdbputkeep2(TCBDB *bdb, const char *kstr, const char *vstr);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcbdbputcat' is used in order to concatenate a value at the end of the existing record in a B+ tree database object.

+ +
+
bool tcbdbputcat(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcbdbputcat2' is used in order to concatenate a string value at the end of the existing record in a B+ tree database object.

+ +
+
bool tcbdbputcat2(TCBDB *bdb, const char *kstr, const char *vstr);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcbdbputdup' is used in order to store a record into a B+ tree database object with allowing duplication of keys.

+ +
+
bool tcbdbputdup(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, the new record is placed after the existing one.
+
+ +

The function `tcbdbputdup2' is used in order to store a string record into a B+ tree database object with allowing duplication of keys.

+ +
+
bool tcbdbputdup2(TCBDB *bdb, const char *kstr, const char *vstr);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, the new record is placed after the existing one.
+
+ +

The function `tcbdbputdup3' is used in order to store records into a B+ tree database object with allowing duplication of keys.

+ +
+
bool tcbdbputdup3(TCBDB *bdb, const void *kbuf, int ksiz, const TCLIST *vals);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the common key.
+
`ksiz' specifies the size of the region of the common key.
+
`vals' specifies a list object containing values.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, the new records are placed after the existing one.
+
+ +

The function `tcbdbout' is used in order to remove a record of a B+ tree database object.

+ +
+
bool tcbdbout(TCBDB *bdb, const void *kbuf, int ksiz);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true, else, it is false.
+
If the key of duplicated records is specified, the first one is selected.
+
+ +

The function `tcbdbout2' is used in order to remove a string record of a B+ tree database object.

+ +
+
bool tcbdbout2(TCBDB *bdb, const char *kstr);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true, else, it is false.
+
If the key of duplicated records is specified, the first one is selected.
+
+ +

The function `tcbdbout3' is used in order to remove records of a B+ tree database object.

+ +
+
bool tcbdbout3(TCBDB *bdb, const void *kbuf, int ksiz);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true, else, it is false.
+
If the key of duplicated records is specified, all of them are removed.
+
+ +

The function `tcbdbget' is used in order to retrieve a record in a B+ tree database object.

+ +
+
void *tcbdbget(TCBDB *bdb, const void *kbuf, int ksiz, int *sp);
+
`bdb' specifies the B+ tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
If the key of duplicated records is specified, the first one is selected. Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbdbget2' is used in order to retrieve a string record in a B+ tree database object.

+ +
+
char *tcbdbget2(TCBDB *bdb, const char *kstr);
+
`bdb' specifies the B+ tree database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
If the key of duplicated records is specified, the first one is selected. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbdbget3' is used in order to retrieve a record in a B+ tree database object as a volatile buffer.

+ +
+
const void *tcbdbget3(TCBDB *bdb, const void *kbuf, int ksiz, int *sp);
+
`bdb' specifies the B+ tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
If the key of duplicated records is specified, the first one is selected. Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is volatile and it may be spoiled by another operation of the database, the data should be copied into another involatile buffer immediately.
+
+ +

The function `tcbdbget4' is used in order to retrieve records in a B+ tree database object.

+ +
+
TCLIST *tcbdbget4(TCBDB *bdb, const void *kbuf, int ksiz);
+
`bdb' specifies the B+ tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is a list object of the values of the corresponding records. `NULL' is returned if no record corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcbdbvnum' is used in order to get the number of records corresponding a key in a B+ tree database object.

+ +
+
int tcbdbvnum(TCBDB *bdb, const void *kbuf, int ksiz);
+
`bdb' specifies the B+ tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is the number of the corresponding records, else, it is 0.
+
+ +

The function `tcbdbvnum2' is used in order to get the number of records corresponding a string key in a B+ tree database object.

+ +
+
int tcbdbvnum2(TCBDB *bdb, const char *kstr);
+
`bdb' specifies the B+ tree database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the number of the corresponding records, else, it is 0.
+
+ +

The function `tcbdbvsiz' is used in order to get the size of the value of a record in a B+ tree database object.

+ +
+
int tcbdbvsiz(TCBDB *bdb, const void *kbuf, int ksiz);
+
`bdb' specifies the B+ tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
If the key of duplicated records is specified, the first one is selected.
+
+ +

The function `tcbdbvsiz2' is used in order to get the size of the value of a string record in a B+ tree database object.

+ +
+
int tcbdbvsiz2(TCBDB *bdb, const char *kstr);
+
`bdb' specifies the B+ tree database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
If the key of duplicated records is specified, the first one is selected.
+
+ +

The function `tcbdbrange' is used in order to get keys of ranged records in a B+ tree database object.

+ +
+
TCLIST *tcbdbrange(TCBDB *bdb, const void *bkbuf, int bksiz, bool binc, const void *ekbuf, int eksiz, bool einc, int max);
+
`bdb' specifies the B+ tree database object.
+
`bkbuf' specifies the pointer to the region of the key of the beginning border. If it is `NULL', the first record is specified.
+
`bksiz' specifies the size of the region of the beginning key.
+
`binc' specifies whether the beginning border is inclusive or not.
+
`ekbuf' specifies the pointer to the region of the key of the ending border. If it is `NULL', the last record is specified.
+
`eksiz' specifies the size of the region of the ending key.
+
`einc' specifies whether the ending border is inclusive or not.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the keys of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcbdbrange2' is used in order to get string keys of ranged records in a B+ tree database object.

+ +
+
TCLIST *tcbdbrange2(TCBDB *bdb, const char *bkstr, bool binc, const char *ekstr, bool einc, int max);
+
`bdb' specifies the B+ tree database object.
+
`bkstr' specifies the string of the key of the beginning border. If it is `NULL', the first record is specified.
+
`binc' specifies whether the beginning border is inclusive or not.
+
`ekstr' specifies the string of the key of the ending border. If it is `NULL', the last record is specified.
+
`einc' specifies whether the ending border is inclusive or not.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the keys of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcbdbfwmkeys' is used in order to get forward matching keys in a B+ tree database object.

+ +
+
TCLIST *tcbdbfwmkeys(TCBDB *bdb, const void *pbuf, int psiz, int max);
+
`bdb' specifies the B+ tree database object.
+
`pbuf' specifies the pointer to the region of the prefix.
+
`psiz' specifies the size of the region of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcbdbfwmkeys2' is used in order to get forward matching string keys in a B+ tree database object.

+ +
+
TCLIST *tcbdbfwmkeys2(TCBDB *bdb, const char *pstr, int max);
+
`bdb' specifies the B+ tree database object.
+
`pstr' specifies the string of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcbdbaddint' is used in order to add an integer to a record in a B+ tree database object.

+ +
+
int tcbdbaddint(TCBDB *bdb, const void *kbuf, int ksiz, int num);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is `INT_MIN'.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcbdbadddouble' is used in order to add a real number to a record in a B+ tree database object.

+ +
+
double tcbdbadddouble(TCBDB *bdb, const void *kbuf, int ksiz, double num);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is Not-a-Number.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcbdbsync' is used in order to synchronize updated contents of a B+ tree database object with the file and the device.

+ +
+
bool tcbdbsync(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
This function is useful when another process connects to the same database file.
+
+ +

The function `tcbdboptimize' is used in order to optimize the file of a B+ tree database object.

+ +
+
bool tcbdboptimize(TCBDB *bdb, int32_t lmemb, int32_t nmemb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`lmemb' specifies the number of members in each leaf page. If it is not more than 0, the current setting is not changed.
+
`nmemb' specifies the number of members in each non-leaf page. If it is not more than 0, the current setting is not changed.
+
`bnum' specifies the number of elements of the bucket array. If it is not more than 0, the default value is specified. The default value is two times of the number of pages.
+
`apow' specifies the size of record alignment by power of 2. If it is negative, the current setting is not changed.
+
`fpow' specifies the maximum number of elements of the free block pool by power of 2. If it is negative, the current setting is not changed.
+
`opts' specifies options by bitwise-or: `BDBTLARGE' specifies that the size of the database can be larger than 2GB by using 64-bit bucket array, `BDBTDEFLATE' specifies that each record is compressed with Deflate encoding, `BDBTBZIP' specifies that each page is compressed with BZIP2 encoding, `BDBTTCBS' specifies that each page is compressed with TCBS encoding. If it is `UINT8_MAX', the current setting is not changed.
+
If successful, the return value is true, else, it is false.
+
This function is useful to reduce the size of the database file with data fragmentation by successive updating.
+
+ +

The function `tcbdbvanish' is used in order to remove all records of a B+ tree database object.

+ +
+
bool tcbdbvanish(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcbdbcopy' is used in order to copy the database file of a B+ tree database object.

+ +
+
bool tcbdbcopy(TCBDB *bdb, const char *path);
+
`bdb' specifies the B+ tree database object.
+
`path' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line.
+
If successful, the return value is true, else, it is false. False is returned if the executed command returns non-zero code.
+
The database file is assured to be kept synchronized and not modified while the copying or executing operation is in progress. So, this function is useful to create a backup file of the database file.
+
+ +

The function `tcbdbtranbegin' is used in order to begin the transaction of a B+ tree database object.

+ +
+
bool tcbdbtranbegin(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
The database is locked by the thread while the transaction so that only one transaction can be activated with a database object at the same time. Thus, the serializable isolation level is assumed if every database operation is performed in the transaction. Because all pages are cached on memory while the transaction, the amount of referred records is limited by the memory capacity. If the database is closed during transaction, the transaction is aborted implicitly.
+
+ +

The function `tcbdbtrancommit' is used in order to commit the transaction of a B+ tree database object.

+ +
+
bool tcbdbtrancommit(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is fixed when it is committed successfully.
+
+ +

The function `tcbdbtranabort' is used in order to abort the transaction of a B+ tree database object.

+ +
+
bool tcbdbtranabort(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is discarded when it is aborted. The state of the database is rollbacked to before transaction.
+
+ +

The function `tcbdbpath' is used in order to get the file path of a B+ tree database object.

+ +
+
const char *tcbdbpath(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object.
+
The return value is the path of the database file or `NULL' if the object does not connect to any database file.
+
+ +

The function `tcbdbrnum' is used in order to get the number of records of a B+ tree database object.

+ +
+
uint64_t tcbdbrnum(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object.
+
The return value is the number of records or 0 if the object does not connect to any database file.
+
+ +

The function `tcbdbfsiz' is used in order to get the size of the database file of a B+ tree database object.

+ +
+
uint64_t tcbdbfsiz(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object.
+
The return value is the size of the database file or 0 if the object does not connect to any database file.
+
+ +

The function `tcbdbcurnew' is used in order to create a cursor object.

+ +
+
BDBCUR *tcbdbcurnew(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object.
+
The return value is the new cursor object.
+
Note that the cursor is available only after initialization with the `tcbdbcurfirst' or the `tcbdbcurjump' functions and so on. Moreover, the position of the cursor will be indefinite when the database is updated after the initialization of the cursor.
+
+ +

The function `tcbdbcurdel' is used in order to delete a cursor object.

+ +
+
void tcbdbcurdel(BDBCUR *cur);
+
`cur' specifies the cursor object.
+
+ +

The function `tcbdbcurfirst' is used in order to move a cursor object to the first record.

+ +
+
bool tcbdbcurfirst(BDBCUR *cur);
+
`cur' specifies the cursor object.
+
If successful, the return value is true, else, it is false. False is returned if there is no record in the database.
+
+ +

The function `tcbdbcurlast' is used in order to move a cursor object to the last record.

+ +
+
bool tcbdbcurlast(BDBCUR *cur);
+
`cur' specifies the cursor object.
+
If successful, the return value is true, else, it is false. False is returned if there is no record in the database.
+
+ +

The function `tcbdbcurjump' is used in order to move a cursor object to the front of records corresponding a key.

+ +
+
bool tcbdbcurjump(BDBCUR *cur, const void *kbuf, int ksiz);
+
`cur' specifies the cursor object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true, else, it is false. False is returned if there is no record corresponding the condition.
+
The cursor is set to the first record corresponding the key or the next substitute if completely matching record does not exist.
+
+ +

The function `tcbdbcurjump2' is used in order to move a cursor object to the front of records corresponding a key string.

+ +
+
bool tcbdbcurjump2(BDBCUR *cur, const char *kstr);
+
`cur' specifies the cursor object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true, else, it is false. False is returned if there is no record corresponding the condition.
+
The cursor is set to the first record corresponding the key or the next substitute if completely matching record does not exist.
+
+ +

The function `tcbdbcurprev' is used in order to move a cursor object to the previous record.

+ +
+
bool tcbdbcurprev(BDBCUR *cur);
+
`cur' specifies the cursor object.
+
If successful, the return value is true, else, it is false. False is returned if there is no previous record.
+
+ +

The function `tcbdbcurnext' is used in order to move a cursor object to the next record.

+ +
+
bool tcbdbcurnext(BDBCUR *cur);
+
`cur' specifies the cursor object.
+
If successful, the return value is true, else, it is false. False is returned if there is no next record.
+
+ +

The function `tcbdbcurput' is used in order to insert a record around a cursor object.

+ +
+
bool tcbdbcurput(BDBCUR *cur, const void *vbuf, int vsiz, int cpmode);
+
`cur' specifies the cursor object of writer connection.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
`cpmode' specifies detail adjustment: `BDBCPCURRENT', which means that the value of the current record is overwritten, `BDBCPBEFORE', which means that the new record is inserted before the current record, `BDBCPAFTER', which means that the new record is inserted after the current record.
+
If successful, the return value is true, else, it is false. False is returned when the cursor is at invalid position.
+
After insertion, the cursor is moved to the inserted record.
+
+ +

The function `tcbdbcurput2' is used in order to insert a string record around a cursor object.

+ +
+
bool tcbdbcurput2(BDBCUR *cur, const char *vstr, int cpmode);
+
`cur' specifies the cursor object of writer connection.
+
`vstr' specifies the string of the value.
+
`cpmode' specifies detail adjustment: `BDBCPCURRENT', which means that the value of the current record is overwritten, `BDBCPBEFORE', which means that the new record is inserted before the current record, `BDBCPAFTER', which means that the new record is inserted after the current record.
+
If successful, the return value is true, else, it is false. False is returned when the cursor is at invalid position.
+
After insertion, the cursor is moved to the inserted record.
+
+ +

The function `tcbdbcurout' is used in order to remove the record where a cursor object is.

+ +
+
bool tcbdbcurout(BDBCUR *cur);
+
`cur' specifies the cursor object of writer connection.
+
If successful, the return value is true, else, it is false. False is returned when the cursor is at invalid position.
+
After deletion, the cursor is moved to the next record if possible.
+
+ +

The function `tcbdbcurkey' is used in order to get the key of the record where the cursor object is.

+ +
+
void *tcbdbcurkey(BDBCUR *cur, int *sp);
+
`cur' specifies the cursor object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the key, else, it is `NULL'. `NULL' is returned when the cursor is at invalid position.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbdbcurkey2' is used in order to get the key string of the record where the cursor object is.

+ +
+
char *tcbdbcurkey2(BDBCUR *cur);
+
`cur' specifies the cursor object.
+
If successful, the return value is the string of the key, else, it is `NULL'. `NULL' is returned when the cursor is at invalid position.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbdbcurkey3' is used in order to get the key of the record where the cursor object is, as a volatile buffer.

+ +
+
const void *tcbdbcurkey3(BDBCUR *cur, int *sp);
+
`cur' specifies the cursor object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the key, else, it is `NULL'. `NULL' is returned when the cursor is at invalid position.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is volatile and it may be spoiled by another operation of the database, the data should be copied into another involatile buffer immediately.
+
+ +

The function `tcbdbcurval' is used in order to get the value of the record where the cursor object is.

+ +
+
void *tcbdbcurval(BDBCUR *cur, int *sp);
+
`cur' specifies the cursor object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value, else, it is `NULL'. `NULL' is returned when the cursor is at invalid position.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbdbcurval2' is used in order to get the value string of the record where the cursor object is.

+ +
+
char *tcbdbcurval2(BDBCUR *cur);
+
`cur' specifies the cursor object.
+
If successful, the return value is the string of the value, else, it is `NULL'. `NULL' is returned when the cursor is at invalid position.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbdbcurval3' is used in order to get the value of the record where the cursor object is, as a volatile buffer.

+ +
+
const void *tcbdbcurval3(BDBCUR *cur, int *sp);
+
`cur' specifies the cursor object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value, else, it is `NULL'. `NULL' is returned when the cursor is at invalid position.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is volatile and it may be spoiled by another operation of the database, the data should be copied into another involatile buffer immediately.
+
+ +

The function `tcbdbcurrec' is used in order to get the key and the value of the record where the cursor object is.

+ +
+
bool tcbdbcurrec(BDBCUR *cur, TCXSTR *kxstr, TCXSTR *vxstr);
+
`cur' specifies the cursor object.
+
`kxstr' specifies the object into which the key is wrote down.
+
`vxstr' specifies the object into which the value is wrote down.
+
If successful, the return value is true, else, it is false. False is returned when the cursor is at invalid position.
+
+ +

Example Code

+ +

The following code is an example to use a B+ tree database.

+ +
#include <tcutil.h>
+#include <tcbdb.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+int main(int argc, char **argv){
+  TCBDB *bdb;
+  BDBCUR *cur;
+  int ecode;
+  char *key, *value;
+
+  /* create the object */
+  bdb = tcbdbnew();
+
+  /* open the database */
+  if(!tcbdbopen(bdb, "casket.tcb", BDBOWRITER | BDBOCREAT)){
+    ecode = tcbdbecode(bdb);
+    fprintf(stderr, "open error: %s\n", tcbdberrmsg(ecode));
+  }
+
+  /* store records */
+  if(!tcbdbput2(bdb, "foo", "hop") ||
+     !tcbdbput2(bdb, "bar", "step") ||
+     !tcbdbput2(bdb, "baz", "jump")){
+    ecode = tcbdbecode(bdb);
+    fprintf(stderr, "put error: %s\n", tcbdberrmsg(ecode));
+  }
+
+  /* retrieve records */
+  value = tcbdbget2(bdb, "foo");
+  if(value){
+    printf("%s\n", value);
+    free(value);
+  } else {
+    ecode = tcbdbecode(bdb);
+    fprintf(stderr, "get error: %s\n", tcbdberrmsg(ecode));
+  }
+
+  /* traverse records */
+  cur = tcbdbcurnew(bdb);
+  tcbdbcurfirst(cur);
+  while((key = tcbdbcurkey2(cur)) != NULL){
+    value = tcbdbcurval2(cur);
+    if(value){
+      printf("%s:%s\n", key, value);
+      free(value);
+    }
+    free(key);
+    tcbdbcurnext(cur);
+  }
+  tcbdbcurdel(cur);
+
+  /* close the database */
+  if(!tcbdbclose(bdb)){
+    ecode = tcbdbecode(bdb);
+    fprintf(stderr, "close error: %s\n", tcbdberrmsg(ecode));
+  }
+
+  /* delete the object */
+  tcbdbdel(bdb);
+
+  return 0;
+}
+
+ +

CLI

+ +

To use the B+ tree database API easily, the commands `tcbtest', `tcbmttest', and `tcbmgr' are provided.

+ +

The command `tcbtest' is a utility for facility test and performance test. This command is used in the following format. `path' specifies the path of a database file. `rnum' specifies the number of iterations. `lmemb' specifies the number of members in each leaf page. `nmemb' specifies the number of members in each non-leaf page. `bnum' specifies the number of buckets. `apow' specifies the power of the alignment. `fpow' specifies the power of the free block pool.

+ +
+
tcbtest write [-mt] [-cd|-ci|-cj] [-tl] [-td|-tb|-tt|-tx] [-lc num] [-nc num] [-xm num] [-df num] [-ls num] [-ca num] [-nl|-nb] [-rnd] path rnum [lmemb [nmemb [bnum [apow [fpow]]]]]
+
Store records with keys of 8 bytes. They change as `00000001', `00000002'...
+
tcbtest read [-mt] [-cd|-ci|-cj] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-wb] [-rnd] path
+
Retrieve all records of the database above.
+
tcbtest remove [-mt] [-cd|-ci|-cj] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path
+
Remove all records of the database above.
+
tcbtest rcat [-mt] [-cd|-ci|-cj] [-tl] [-td|-tb|-tt|-tx] [-lc num] [-nc num] [-xm num] [-df num] [-ls num] [-ca num] [-nl|-nb] [-pn num] [-dai|-dad|-rl|-ru] path rnum [lmemb [nmemb [bnum [apow [fpow]]]]]
+
Store records with partway duplicated keys using concatenate mode.
+
tcbtest queue [-mt] [-cd|-ci|-cj] [-tl] [-td|-tb|-tt|-tx] [-lc num] [-nc num] [-xm num] [-df num] [-ls num] [-ca num] [-nl|-nb] path rnum [lmemb [nmemb [bnum [apow [fpow]]]]]
+
Perform queueing and dequeueing.
+
tcbtest misc [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum
+
Perform miscellaneous test of various operations.
+
tcbtest wicked [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum
+
Perform updating operations selected at random.
+
+ +

Options feature the following.

+ +
    +
  • -mt : call the function `tchdbsetmutex'.
  • +
  • -cd : use the comparison function `tccmpdecimal'.
  • +
  • -ci : use the comparison function `tccmpint32'.
  • +
  • -cj : use the comparison function `tccmpint64'.
  • +
  • -tl : enable the option `BDBTLARGE'.
  • +
  • -td : enable the option `BDBTDEFLATE'.
  • +
  • -tb : enable the option `BDBTBZIP'.
  • +
  • -tt : enable the option `BDBTTCBS'.
  • +
  • -tx : enable the option `BDBTEXCODEC'.
  • +
  • -lc num : specify the number of cached leaf pages.
  • +
  • -nc num : specify the number of cached non-leaf pages.
  • +
  • -xm num : specify the size of the extra mapped memory.
  • +
  • -df num : specify the unit step number of auto defragmentation.
  • +
  • -ls num : specify the maximum size of each leaf page.
  • +
  • -ca num : specify the capacity number of records.
  • +
  • -nl : enable the option `BDBNOLCK'.
  • +
  • -nb : enable the option `BDBLCKNB'.
  • +
  • -rnd : select keys at random.
  • +
  • -wb : use the function `tcbdbget3' instead of `tcbdbget'.
  • +
  • -pn num : specify the number of patterns.
  • +
  • -dai : use the function `tcbdbaddint' instead of `tcbdbputcat'.
  • +
  • -dad : use the function `tcbdbadddouble' instead of `tcbdbputcat'.
  • +
  • -rl : set the length of values at random.
  • +
  • -ru : select update operations at random.
  • +
+ +

This command returns 0 on success, another on failure.

+ +

The command `tcbmttest' is a utility for facility test and performance test. This command is used in the following format. `path' specifies the path of a database file. `tnum' specifies the number of running threads. `rnum' specifies the number of iterations. `lmemb' specifies the number of members in each leaf page. `nmemb' specifies the number of members in each non-leaf page. `bnum' specifies the number of buckets. `apow' specifies the power of the alignment. `fpow' specifies the power of the free block pool.

+ +
+
tcbmttest write [-tl] [-td|-tb|-tt|-tx] [-xm num] [-df num] [-nl|-nb] [-rnd] path tnum rnum [lmemb [nmemb [bnum [apow [fpow]]]]]
+
Store records with keys of 8 bytes. They change as `00000001', `00000002'...
+
tcbmttest read [-xm num] [-df num] [-nl|-nb] [-wb] [-rnd] path tnum
+
Retrieve all records of the database above.
+
tcbmttest remove [-xm num] [-df num] [-nl|-nb] [-rnd] path tnum
+
Remove all records of the database above.
+
tcbmttest wicked [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] [-nc] path tnum rnum
+
Perform updating operations selected at random.
+
tcbmttest typical [-tl] [-td|-tb|-tt|-tx] [-xm num] [-df num] [-nl|-nb] [-nc] [-rr num] path tnum rnum [lmemb [nmemb [bnum [apow [fpow]]]]]
+
Perform typical operations selected at random.
+
tcbmttest race [-tl] [-td|-tb|-tt|-tx] [-xm num] [-df num] [-nl|-nb] path tnum rnum [lmemb [nmemb [bnum [apow [fpow]]]]]
+
Perform race condition test.
+
+ +

Options feature the following.

+ +
    +
  • -tl : enable the option `BDBTLARGE'.
  • +
  • -td : enable the option `BDBTDEFLATE'.
  • +
  • -tb : enable the option `BDBTBZIP'.
  • +
  • -tt : enable the option `BDBTTCBS'.
  • +
  • -tx : enable the option `BDBTEXCODEC'.
  • +
  • -xm num : specify the size of the extra mapped memory.
  • +
  • -df num : specify the unit step number of auto defragmentation.
  • +
  • -nl : enable the option `BDBNOLCK'.
  • +
  • -nb : enable the option `BDBLCKNB'.
  • +
  • -rnd : select keys at random.
  • +
  • -wb : use the function `tchdbget3' instead of `tchdbget'.
  • +
  • -nc : omit the comparison test.
  • +
  • -rr num : specify the ratio of reading operation by percentage.
  • +
+ +

This command returns 0 on success, another on failure.

+ +

The command `tcbmgr' is a utility for test and debugging of the B+ tree database API and its applications. `path' specifies the path of a database file. `lmemb' specifies the number of members in each leaf page. `nmemb' specifies the number of members in each non-leaf page. `bnum' specifies the number of buckets. `apow' specifies the power of the alignment. `fpow' specifies the power of the free block pool. `key' specifies the key of a record. `value' specifies the value of a record. `file' specifies the input file.

+ +
+
tcbmgr create [-cd|-ci|-cj] [-tl] [-td|-tb|-tt|-tx] path [lmemb [nmemb [bnum [apow [fpow]]]]]
+
Create a database file.
+
tcbmgr inform [-nl|-nb] path
+
Print miscellaneous information to the standard output.
+
tcbmgr put [-cd|-ci|-cj] [-nl|-nb] [-sx] [-dk|-dc|-dd|-db|-dai|-dad] path key value
+
Store a record.
+
tcbmgr out [-cd|-ci|-cj] [-nl|-nb] [-sx] path key
+
Remove a record.
+
tcbmgr get [-cd|-ci|-cj] [-nl|-nb] [-sx] [-px] [-pz] path key
+
Print the value of a record.
+
tcbmgr list [-cd|-ci|-cj] [-nl|-nb] [-m num] [-bk] [-pv] [-px] [-j str] [-rb bkey ekey] [-fm str] path
+
Print keys of all records, separated by line feeds.
+
tcbmgr optimize [-cd|-ci|-cj] [-tl] [-td|-tb|-tt|-tx] [-tz] [-nl|-nb] [-df] path [lmemb [nmemb [bnum [apow [fpow]]]]]
+
Optimize a database file.
+
tcbmgr importtsv [-nl|-nb] [-sc] path [file]
+
Store records of TSV in each line of a file.
+
tcbmgr version
+
Print the version information of Tokyo Cabinet.
+
+ +

Options feature the following.

+ +
    +
  • -cd : use the comparison function `tccmpdecimal'.
  • +
  • -ci : use the comparison function `tccmpint32'.
  • +
  • -cj : use the comparison function `tccmpint64'.
  • +
  • -tl : enable the option `BDBTLARGE'.
  • +
  • -td : enable the option `BDBTDEFLATE'.
  • +
  • -tb : enable the option `BDBTBZIP'.
  • +
  • -tt : enable the option `BDBTTCBS'.
  • +
  • -tx : enable the option `BDBTEXCODEC'.
  • +
  • -nl : enable the option `BDBNOLCK'.
  • +
  • -nb : enable the option `BDBLCKNB'.
  • +
  • -sx : the input data is evaluated as a hexadecimal data string.
  • +
  • -dk : use the function `tcbdbputkeep' instead of `tcbdbput'.
  • +
  • -dc : use the function `tcbdbputcat' instead of `tcbdbput'.
  • +
  • -dd : use the function `tcbdbputdup' instead of `tcbdbput'.
  • +
  • -db : use the function `tcbdbputdupback' instead of `tcbdbput'.
  • +
  • -dai : use the function `tcbdbaddint' instead of `tcbdbput'.
  • +
  • -dad : use the function `tcbdbadddouble' instead of `tcbdbput'.
  • +
  • -px : the output data is converted into a hexadecimal data string.
  • +
  • -pz : do not append line feed at the end of the output.
  • +
  • -m num : specify the maximum number of the output.
  • +
  • -bk : perform backword scanning.
  • +
  • -pv : print values of records also.
  • +
  • -j str : specify the key where the cursor jump to.
  • +
  • -rb bkey ekey : specify the range of keys.
  • +
  • -fm str : specify the prefix of keys.
  • +
  • -tz : enable the option `UINT8_MAX'.
  • +
  • -df : perform defragmentation only.
  • +
  • -sc : normalize keys as lower cases.
  • +
+ +

This command returns 0 on success, another on failure.

+ +
+ +

The Fixed-length Database API

+ +

Fixed-length database is a file containing an array of fixed-length elements and is handled with the fixed-length database API. See `tcfdb.h' for the entire specification.

+ +

Description

+ +

To use the fixed-length database API, include `tcutil.h', `tcfdb.h', and related standard header files. Usually, write the following description near the front of a source file.

+ +
+
#include <tcutil.h>
+
#include <tcfdb.h>
+
#include <stdlib.h>
+
#include <stdbool.h>
+
#include <stdint.h>
+
+ +

Objects whose type is pointer to `TCFDB' are used to handle fixed-length databases. A fixed-length database object is created with the function `tcfdbnew' and is deleted with the function `tcfdbdel'. To avoid memory leak, it is important to delete every object when it is no longer in use.

+ +

Before operations to store or retrieve records, it is necessary to open a database file and connect the fixed-length database object to it. The function `tcfdbopen' is used to open a database file and the function `tcfdbclose' is used to close the database file. To avoid data missing or corruption, it is important to close every database file when it is no longer in use. It is forbidden for multible database objects in a process to open the same database at the same time.

+ +

API

+ +

The function `tcfdberrmsg' is used in order to get the message string corresponding to an error code.

+ +
+
const char *tcfdberrmsg(int ecode);
+
`ecode' specifies the error code.
+
The return value is the message string of the error code.
+
+ +

The function `tcfdbnew' is used in order to create a fixed-length database object.

+ +
+
TCFDB *tcfdbnew(void);
+
The return value is the new fixed-length database object.
+
+ +

The function `tcfdbdel' is used in order to delete a fixed-length database object.

+ +
+
void tcfdbdel(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
If the database is not closed, it is closed implicitly. Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tcfdbecode' is used in order to get the last happened error code of a fixed-length database object.

+ +
+
int tcfdbecode(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
The return value is the last happened error code.
+
The following error codes are defined: `TCESUCCESS' for success, `TCETHREAD' for threading error, `TCEINVALID' for invalid operation, `TCENOFILE' for file not found, `TCENOPERM' for no permission, `TCEMETA' for invalid meta data, `TCERHEAD' for invalid record header, `TCEOPEN' for open error, `TCECLOSE' for close error, `TCETRUNC' for trunc error, `TCESYNC' for sync error, `TCESTAT' for stat error, `TCESEEK' for seek error, `TCEREAD' for read error, `TCEWRITE' for write error, `TCEMMAP' for mmap error, `TCELOCK' for lock error, `TCEUNLINK' for unlink error, `TCERENAME' for rename error, `TCEMKDIR' for mkdir error, `TCERMDIR' for rmdir error, `TCEKEEP' for existing record, `TCENOREC' for no record found, and `TCEMISC' for miscellaneous error.
+
+ +

The function `tcfdbsetmutex' is used in order to set mutual exclusion control of a fixed-length database object for threading.

+ +
+
bool tcfdbsetmutex(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object which is not opened.
+
If successful, the return value is true, else, it is false.
+
Note that the mutual exclusion control is needed if the object is shared by plural threads and this function should be called before the database is opened.
+
+ +

The function `tcfdbtune' is used in order to set the tuning parameters of a fixed-length database object.

+ +
+
bool tcfdbtune(TCFDB *fdb, int32_t width, int64_t limsiz);
+
`fdb' specifies the fixed-length database object which is not opened.
+
`width' specifies the width of the value of each record. If it is not more than 0, the default value is specified. The default value is 255.
+
`limsiz' specifies the limit size of the database file. If it is not more than 0, the default value is specified. The default value is 268435456.
+
If successful, the return value is true, else, it is false.
+
Note that the tuning parameters should be set before the database is opened.
+
+ +

The function `tcfdbopen' is used in order to open a database file and connect a fixed-length database object.

+ +
+
bool tcfdbopen(TCFDB *fdb, const char *path, int omode);
+
`fdb' specifies the fixed-length database object which is not opened.
+
`path' specifies the path of the database file.
+
`omode' specifies the connection mode: `FDBOWRITER' as a writer, `FDBOREADER' as a reader. If the mode is `FDBOWRITER', the following may be added by bitwise-or: `FDBOCREAT', which means it creates a new database if not exist, `FDBOTRUNC', which means it creates a new database regardless if one exists, `FDBOTSYNC', which means every transaction synchronizes updated contents with the device. Both of `FDBOREADER' and `FDBOWRITER' can be added to by bitwise-or: `FDBONOLCK', which means it opens the database file without file locking, or `FDBOLCKNB', which means locking is performed without blocking.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcfdbclose' is used in order to close a fixed-length database object.

+ +
+
bool tcfdbclose(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
If successful, the return value is true, else, it is false.
+
Update of a database is assured to be written when the database is closed. If a writer opens a database but does not close it appropriately, the database will be broken.
+
+ +

The function `tcfdbput' is used in order to store a record into a fixed-length database object.

+ +
+
bool tcfdbput(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDPREV', the number less by one than the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified. If it is `FDBIDNEXT', the number greater by one than the maximum ID number of existing records is specified.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value. If the size of the value is greater than the width tuning parameter of the database, the size is cut down to the width.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcfdbput2' is used in order to store a record with a decimal key into a fixed-length database object.

+ +
+
bool tcfdbput2(TCFDB *fdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "prev", the number less by one than the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified. If it is "next", the number greater by one than the maximum ID number of existing records is specified.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value. If the size of the value is greater than the width tuning parameter of the database, the size is cut down to the width.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcfdbput3' is used in order to store a string record with a decimal key into a fixed-length database object.

+ +
+
bool tcfdbput3(TCFDB *fdb, const char *kstr, const void *vstr);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kstr' specifies the string of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "prev", the number less by one than the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified. If it is "next", the number greater by one than the maximum ID number of existing records is specified.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcfdbputkeep' is used in order to store a new record into a fixed-length database object.

+ +
+
bool tcfdbputkeep(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDPREV', the number less by one than the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified. If it is `FDBIDNEXT', the number greater by one than the maximum ID number of existing records is specified.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value. If the size of the value is greater than the width tuning parameter of the database, the size is cut down to the width.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcfdbputkeep2' is used in order to store a new record with a decimal key into a fixed-length database object.

+ +
+
bool tcfdbputkeep2(TCFDB *fdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "prev", the number less by one than the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified. If it is "next", the number greater by one than the maximum ID number of existing records is specified.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value. If the size of the value is greater than the width tuning parameter of the database, the size is cut down to the width.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcfdbputkeep3' is used in order to store a new string record with a decimal key into a fixed-length database object.

+ +
+
bool tcfdbputkeep3(TCFDB *fdb, const char *kstr, const void *vstr);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kstr' specifies the string of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "prev", the number less by one than the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified. If it is "next", the number greater by one than the maximum ID number of existing records is specified.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcfdbputcat' is used in order to concatenate a value at the end of the existing record in a fixed-length database object.

+ +
+
bool tcfdbputcat(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDPREV', the number less by one than the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified. If it is `FDBIDNEXT', the number greater by one than the maximum ID number of existing records is specified.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value. If the size of the value is greater than the width tuning parameter of the database, the size is cut down to the width.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcfdbputcat2' is used in order to concatenate a value with a decimal key in a fixed-length database object.

+ +
+
bool tcfdbputcat2(TCFDB *fdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "prev", the number less by one than the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified. If it is "next", the number greater by one than the maximum ID number of existing records is specified.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value. If the size of the value is greater than the width tuning parameter of the database, the size is cut down to the width.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcfdbputcat3' is used in order to concatenate a string value with a decimal key in a fixed-length database object.

+ +
+
bool tcfdbputcat3(TCFDB *fdb, const char *kstr, const void *vstr);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kstr' specifies the string of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "prev", the number less by one than the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified. If it is "next", the number greater by one than the maximum ID number of existing records is specified.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcfdbout' is used in order to remove a record of a fixed-length database object.

+ +
+
bool tcfdbout(TCFDB *fdb, int64_t id);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcfdbout2' is used in order to remove a record with a decimal key of a fixed-length database object.

+ +
+
bool tcfdbout2(TCFDB *fdb, const void *kbuf, int ksiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcfdbout3' is used in order to remove a string record with a decimal key of a fixed-length database object.

+ +
+
bool tcfdbout3(TCFDB *fdb, const char *kstr);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kstr' specifies the string of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcfdbget' is used in order to retrieve a record in a fixed-length database object.

+ +
+
void *tcfdbget(TCFDB *fdb, int64_t id, int *sp);
+
`fdb' specifies the fixed-length database object.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcfdbget2' is used in order to retrieve a record with a decimal key in a fixed-length database object.

+ +
+
void *tcfdbget2(TCFDB *fdb, const void *kbuf, int ksiz, int *sp);
+
`fdb' specifies the fixed-length database object.
+
`kbuf' specifies the pointer to the region of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcfdbget3' is used in order to retrieve a string record with a decimal key in a fixed-length database object.

+ +
+
char *tcfdbget3(TCFDB *fdb, const char *kstr);
+
`fdb' specifies the fixed-length database object.
+
`kstr' specifies the string of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcfdbget4' is used in order to retrieve a record in a fixed-length database object and write the value into a buffer.

+ +
+
int tcfdbget4(TCFDB *fdb, int64_t id, void *vbuf, int max);
+
`fdb' specifies the fixed-length database object.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified.
+
`vbuf' specifies the pointer to the buffer into which the value of the corresponding record is written.
+
`max' specifies the size of the buffer.
+
If successful, the return value is the size of the written data, else, it is -1. -1 is returned if no record corresponds to the specified key.
+
Note that an additional zero code is not appended at the end of the region of the writing buffer.
+
+ +

The function `tcfdbvsiz' is used in order to get the size of the value of a record in a fixed-length database object.

+ +
+
int tcfdbvsiz(TCFDB *fdb, int64_t id);
+
`fdb' specifies the fixed-length database object.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcfdbvsiz2' is used in order to get the size of the value with a decimal key in a fixed-length database object.

+ +
+
int tcfdbvsiz2(TCFDB *fdb, const void *kbuf, int ksiz);
+
`fdb' specifies the fixed-length database object.
+
`kbuf' specifies the pointer to the region of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcfdbvsiz3' is used in order to get the size of the string value with a decimal key in a fixed-length database object.

+ +
+
int tcfdbvsiz3(TCFDB *fdb, const char *kstr);
+
`fdb' specifies the fixed-length database object.
+
`kstr' specifies the string of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcfdbiterinit' is used in order to initialize the iterator of a fixed-length database object.

+ +
+
bool tcfdbiterinit(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
If successful, the return value is true, else, it is false.
+
The iterator is used in order to access the key of every record stored in a database.
+
+ +

The function `tcfdbiternext' is used in order to get the next ID number of the iterator of a fixed-length database object.

+ +
+
uint64_t tcfdbiternext(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
If successful, the return value is the next ID number of the iterator, else, it is 0. 0 is returned when no record is to be get out of the iterator.
+
It is possible to access every record by iteration of calling this function. It is allowed to update or remove records whose keys are fetched while the iteration. The order of this traversal access method is ascending of the ID number.
+
+ +

The function `tcfdbiternext2' is used in order to get the next decimay key of the iterator of a fixed-length database object.

+ +
+
void *tcfdbiternext2(TCFDB *fdb, int *sp);
+
`fdb' specifies the fixed-length database object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the next decimal key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. It is allowed to update or remove records whose keys are fetched while the iteration. The order of this traversal access method is ascending of the ID number.
+
+ +

The function `tcfdbiternext3' is used in order to get the next decimay key string of the iterator of a fixed-length database object.

+ +
+
char *tcfdbiternext3(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
If successful, the return value is the string of the next decimal key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. It is allowed to update or remove records whose keys are fetched while the iteration. The order of this traversal access method is ascending of the ID number.
+
+ +

The function `tcfdbrange' is used in order to get range matching ID numbers in a fixed-length database object.

+ +
+
uint64_t *tcfdbrange(TCFDB *fdb, int64_t lower, int64_t upper, int max, int *np);
+
`fdb' specifies the fixed-length database object.
+
`lower' specifies the lower limit of the range. If it is `FDBIDMIN', the minimum ID is specified.
+
`upper' specifies the upper limit of the range. If it is `FDBIDMAX', the maximum ID is specified.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
`np' specifies the pointer to the variable into which the number of elements of the return value is assigned.
+
If successful, the return value is the pointer to an array of ID numbers of the corresponding records. `NULL' is returned on failure. This function does never fail. It returns an empty array even if no key corresponds.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcfdbrange2' is used in order to get range matching decimal keys in a fixed-length database object.

+ +
+
TCLIST *tcfdbrange2(TCFDB *fdb, const void *lbuf, int lsiz, const void *ubuf, int usiz, int max);
+
`fdb' specifies the fixed-length database object.
+
`lbuf' specifies the pointer to the region of the lower key. If it is "min", the minimum ID number of existing records is specified.
+
`lsiz' specifies the size of the region of the lower key.
+
`ubuf' specifies the pointer to the region of the upper key. If it is "max", the maximum ID number of existing records is specified.
+
`usiz' specifies the size of the region of the upper key.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding decimal keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcfdbrange3' is used in order to get range matching decimal keys with strings in a fixed-length database object.

+ +
+
TCLIST *tcfdbrange3(TCFDB *fdb, const char *lstr, const char *ustr, int max);
+
`fdb' specifies the fixed-length database object.
+
`lstr' specifies the string of the lower key. If it is "min", the minimum ID number of existing records is specified.
+
`ustr' specifies the string of the upper key. If it is "max", the maximum ID number of existing records is specified.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding decimal keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcfdbrange4' is used in order to get keys with an interval notation in a fixed-length database object.

+ +
+
TCLIST *tcfdbrange4(TCFDB *fdb, const void *ibuf, int isiz, int max);
+
`fdb' specifies the fixed-length database object.
+
`ibuf' specifies the pointer to the region of the interval notation.
+
`isiz' specifies the size of the region of the interval notation.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding decimal keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcfdbrange5' is used in order to get keys with an interval notation string in a fixed-length database object.

+ +
+
TCLIST *tcfdbrange5(TCFDB *fdb, const void *istr, int max);
+
`fdb' specifies the fixed-length database object.
+
`istr' specifies the pointer to the region of the interval notation string.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding decimal keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcfdbaddint' is used in order to add an integer to a record in a fixed-length database object.

+ +
+
int tcfdbaddint(TCFDB *fdb, int64_t id, int num);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDPREV', the number less by one than the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified. If it is `FDBIDNEXT', the number greater by one than the maximum ID number of existing records is specified.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is `INT_MIN'.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcfdbadddouble' is used in order to add a real number to a record in a fixed-length database object.

+ +
+
double tcfdbadddouble(TCFDB *fdb, int64_t id, double num);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDPREV', the number less by one than the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified. If it is `FDBIDNEXT', the number greater by one than the maximum ID number of existing records is specified.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is Not-a-Number.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcfdbsync' is used in order to synchronize updated contents of a fixed-length database object with the file and the device.

+ +
+
bool tcfdbsync(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
This function is useful when another process connects to the same database file.
+
+ +

The function `tcfdboptimize' is used in order to optimize the file of a fixed-length database object.

+ +
+
bool tcfdboptimize(TCFDB *fdb, int32_t width, int64_t limsiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`width' specifies the width of the value of each record. If it is not more than 0, the current setting is not changed.
+
`limsiz' specifies the limit size of the database file. If it is not more than 0, the current setting is not changed.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcfdbvanish' is used in order to remove all records of a fixed-length database object.

+ +
+
bool tcfdbvanish(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcfdbcopy' is used in order to copy the database file of a fixed-length database object.

+ +
+
bool tcfdbcopy(TCFDB *fdb, const char *path);
+
`fdb' specifies the fixed-length database object.
+
`path' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line.
+
If successful, the return value is true, else, it is false. False is returned if the executed command returns non-zero code.
+
The database file is assured to be kept synchronized and not modified while the copying or executing operation is in progress. So, this function is useful to create a backup file of the database file.
+
+ +

The function `tcfdbtranbegin' is used in order to begin the transaction of a fixed-length database object.

+ +
+
bool tcfdbtranbegin(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
The database is locked by the thread while the transaction so that only one transaction can be activated with a database object at the same time. Thus, the serializable isolation level is assumed if every database operation is performed in the transaction. All updated regions are kept track of by write ahead logging while the transaction. If the database is closed during transaction, the transaction is aborted implicitly.
+
+ +

The function `tcfdbtrancommit' is used in order to commit the transaction of a fixed-length database object.

+ +
+
bool tcfdbtrancommit(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is fixed when it is committed successfully.
+
+ +

The function `tcfdbtranabort' is used in order to abort the transaction of a fixed-length database object.

+ +
+
bool tcfdbtranabort(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is discarded when it is aborted. The state of the database is rollbacked to before transaction.
+
+ +

The function `tcfdbpath' is used in order to get the file path of a fixed-length database object.

+ +
+
const char *tcfdbpath(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
The return value is the path of the database file or `NULL' if the object does not connect to any database file.
+
+ +

The function `tcfdbrnum' is used in order to get the number of records of a fixed-length database object.

+ +
+
uint64_t tcfdbrnum(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
The return value is the number of records or 0 if the object does not connect to any database file.
+
+ +

The function `tcfdbfsiz' is used in order to get the size of the database file of a fixed-length database object.

+ +
+
uint64_t tcfdbfsiz(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
The return value is the size of the database file or 0 if the object does not connect to any database file.
+
+ +

Example Code

+ +

The following code is an example to use a hash database.

+ +
#include <tcutil.h>
+#include <tcfdb.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+int main(int argc, char **argv){
+  TCFDB *fdb;
+  int ecode;
+  char *key, *value;
+
+  /* create the object */
+  fdb = tcfdbnew();
+
+  /* open the database */
+  if(!tcfdbopen(fdb, "casket.tcf", FDBOWRITER | FDBOCREAT)){
+    ecode = tcfdbecode(fdb);
+    fprintf(stderr, "open error: %s\n", tcfdberrmsg(ecode));
+  }
+
+  /* store records */
+  if(!tcfdbput3(fdb, "1", "one") ||
+     !tcfdbput3(fdb, "12", "twelve") ||
+     !tcfdbput3(fdb, "144", "one forty four")){
+    ecode = tcfdbecode(fdb);
+    fprintf(stderr, "put error: %s\n", tcfdberrmsg(ecode));
+  }
+
+  /* retrieve records */
+  value = tcfdbget3(fdb, "1");
+  if(value){
+    printf("%s\n", value);
+    free(value);
+  } else {
+    ecode = tcfdbecode(fdb);
+    fprintf(stderr, "get error: %s\n", tcfdberrmsg(ecode));
+  }
+
+  /* traverse records */
+  tcfdbiterinit(fdb);
+  while((key = tcfdbiternext3(fdb)) != NULL){
+    value = tcfdbget3(fdb, key);
+    if(value){
+      printf("%s:%s\n", key, value);
+      free(value);
+    }
+    free(key);
+  }
+
+  /* close the database */
+  if(!tcfdbclose(fdb)){
+    ecode = tcfdbecode(fdb);
+    fprintf(stderr, "close error: %s\n", tcfdberrmsg(ecode));
+  }
+
+  /* delete the object */
+  tcfdbdel(fdb);
+
+  return 0;
+}
+
+ +

CLI

+ +

To use the fixed-length database API easily, the commands `tcftest', `tcfmttest', and `tcfmgr' are provided.

+ +

The command `tcftest' is a utility for facility test and performance test. This command is used in the following format. `path' specifies the path of a database file. `rnum' specifies the number of iterations. `width' specifies the width of the value of each record. `limsiz' specifies the limit size of the database file.

+ +
+
tcftest write [-mt] [-nl|-nb] [-rnd] path rnum [width [limsiz]]
+
Store records with keys of 8 bytes. They change as `00000001', `00000002'...
+
tcftest read [-mt] [-nl|-nb] [-wb] [-rnd] path
+
Retrieve all records of the database above.
+
tcftest remove [-mt] [-nl|-nb] [-rnd] path
+
Remove all records of the database above.
+
tcftest rcat [-mt] [-nl|-nb] [-pn num] [-dai|-dad|-rl] path rnum [limsiz]]
+
Store records with partway duplicated keys using concatenate mode.
+
tcftest misc [-mt] [-nl|-nb] path rnum
+
Perform miscellaneous test of various operations.
+
tcftest wicked [-mt] [-nl|-nb] path rnum
+
Perform updating operations selected at random.
+
+ +

Options feature the following.

+ +
    +
  • -mt : call the function `tcfdbsetmutex'.
  • +
  • -nl : enable the option `FDBNOLCK'.
  • +
  • -nb : enable the option `FDBLCKNB'.
  • +
  • -rnd : select keys at random.
  • +
  • -wb : use the function `tcfdbget4' instead of `tcfdbget2'.
  • +
  • -pn num : specify the number of patterns.
  • +
  • -dai : use the function `tcfdbaddint' instead of `tcfdbputcat'.
  • +
  • -dad : use the function `tcfdbadddouble' instead of `tcfdbputcat'.
  • +
  • -rl : set the length of values at random.
  • +
+ +

This command returns 0 on success, another on failure.

+ +

The command `tcfmttest' is a utility for facility test under multi-thread situation. This command is used in the following format. `path' specifies the path of a database file. `tnum' specifies the number of running threads. `rnum' specifies the number of iterations. `width' specifies the width of the value of each record. `limsiz' specifies the limit size of the database file.

+ +
+
tcfmttest write [-nl|-nb] [-rnd] path tnum rnum [width [limsiz]]
+
Store records with keys of 8 bytes. They change as `00000001', `00000002'...
+
tcfmttest read [-nl|-nb] [-wb] [-rnd] path tnum
+
Retrieve all records of the database above.
+
tcfmttest remove [-nl|-nb] [-rnd] path tnum
+
Remove all records of the database above.
+
tcfmttest wicked [-nl|-nb] [-nc] path tnum rnum
+
Perform updating operations selected at random.
+
tcfmttest typical [-nl|-nb] [-nc] [-rr num] path tnum rnum [width [limsiz]]
+
Perform typical operations selected at random.
+
+ +

Options feature the following.

+ +
    +
  • -nl : enable the option `FDBNOLCK'.
  • +
  • -nb : enable the option `FDBLCKNB'.
  • +
  • -rnd : select keys at random.
  • +
  • -wb : use the function `tcfdbget4' instead of `tcfdbget2'.
  • +
  • -nc : omit the comparison test.
  • +
  • -rr num : specify the ratio of reading operation by percentage.
  • +
+ +

This command returns 0 on success, another on failure.

+ +

The command `tcfmgr' is a utility for test and debugging of the fixed-length database API and its applications. `path' specifies the path of a database file. `width' specifies the width of the value of each record. `limsiz' specifies the limit size of the database file. `key' specifies the key of a record. `value' specifies the value of a record. `file' specifies the input file.

+ +
+
tcfmgr create path [width [limsiz]]
+
Create a database file.
+
tcfmgr inform [-nl|-nb] path
+
Print miscellaneous information to the standard output.
+
tcfmgr put [-nl|-nb] [-sx] [-dk|-dc|-dai|-dad] path key value
+
Store a record.
+
tcfmgr out [-nl|-nb] [-sx] path key
+
Remove a record.
+
tcfmgr get [-nl|-nb] [-sx] [-px] [-pz] path key
+
Print the value of a record.
+
tcfmgr list [-nl|-nb] [-m num] [-pv] [-px] [-rb lkey ukey] [-ri str] path
+
Print keys of all records, separated by line feeds.
+
tcfmgr optimize [-nl|-nb] path [width [limsiz]]
+
Optimize a database file.
+
tcfmgr importtsv [-nl|-nb] [-sc] path [file]
+
Store records of TSV in each line of a file.
+
tcfmgr version
+
Print the version information of Tokyo Cabinet.
+
+ +

Options feature the following.

+ +
    +
  • -nl : enable the option `FDBNOLCK'.
  • +
  • -nb : enable the option `FDBLCKNB'.
  • +
  • -sx : the input data is evaluated as a hexadecimal data string.
  • +
  • -dk : use the function `tcfdbputkeep' instead of `tcfdbput'.
  • +
  • -dc : use the function `tcfdbputcat' instead of `tcfdbput'.
  • +
  • -dai : use the function `tcfdbaddint' instead of `tcfdbput'.
  • +
  • -dad : use the function `tcfdbadddouble' instead of `tcfdbput'.
  • +
  • -px : the output data is converted into a hexadecimal data string.
  • +
  • -pz : do not append line feed at the end of the output.
  • +
  • -m num : specify the maximum number of the output.
  • +
  • -pv : print values of records also.
  • +
  • -rb lkey ukey : specify the range of keys.
  • +
  • -ri str : specify the interval notation of keys.
  • +
  • -sc : normalize keys as lower cases.
  • +
+ +

This command returns 0 on success, another on failure.

+ +
+ +

The Table Database API

+ +

Table database is a file containing records composed of the primary keys and arbitrary columns and is handled with the table database API. See `tctdb.h' for the entire specification.

+ +

Description

+ +

To use the table database API, include `tcutil.h', `tctdb.h', and related standard header files. Usually, write the following description near the front of a source file.

+ +
+
#include <tcutil.h>
+
#include <tctdb.h>
+
#include <stdlib.h>
+
#include <stdbool.h>
+
#include <stdint.h>
+
+ +

Objects whose type is pointer to `TCTDB' are used to handle table databases. A table database object is created with the function `tctdbnew' and is deleted with the function `tctdbdel'. To avoid memory leak, it is important to delete every object when it is no longer in use.

+ +

Before operations to store or retrieve records, it is necessary to open a database file and connect the table database object to it. The function `tctdbopen' is used to open a database file and the function `tctdbclose' is used to close the database file. To avoid data missing or corruption, it is important to close every database file when it is no longer in use. It is forbidden for multible database objects in a process to open the same database at the same time.

+ +

API

+ +

The function `tctdberrmsg' is used in order to get the message string corresponding to an error code.

+ +
+
const char *tctdberrmsg(int ecode);
+
`ecode' specifies the error code.
+
The return value is the message string of the error code.
+
+ +

The function `tctdbnew' is used in order to create a table database object.

+ +
+
TCTDB *tctdbnew(void);
+
The return value is the new table database object.
+
+ +

The function `tctdbdel' is used in order to delete a table database object.

+ +
+
void tctdbdel(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
If the database is not closed, it is closed implicitly. Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tctdbecode' is used in order to get the last happened error code of a table database object.

+ +
+
int tctdbecode(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
The return value is the last happened error code.
+
The following error codes are defined: `TCESUCCESS' for success, `TCETHREAD' for threading error, `TCEINVALID' for invalid operation, `TCENOFILE' for file not found, `TCENOPERM' for no permission, `TCEMETA' for invalid meta data, `TCERHEAD' for invalid record header, `TCEOPEN' for open error, `TCECLOSE' for close error, `TCETRUNC' for trunc error, `TCESYNC' for sync error, `TCESTAT' for stat error, `TCESEEK' for seek error, `TCEREAD' for read error, `TCEWRITE' for write error, `TCEMMAP' for mmap error, `TCELOCK' for lock error, `TCEUNLINK' for unlink error, `TCERENAME' for rename error, `TCEMKDIR' for mkdir error, `TCERMDIR' for rmdir error, `TCEKEEP' for existing record, `TCENOREC' for no record found, and `TCEMISC' for miscellaneous error.
+
+ +

The function `tctdbsetmutex' is used in order to set mutual exclusion control of a table database object for threading.

+ +
+
bool tctdbsetmutex(TCTDB *tdb);
+
`tdb' specifies the table database object which is not opened.
+
If successful, the return value is true, else, it is false.
+
Note that the mutual exclusion control is needed if the object is shared by plural threads and this function should be called before the database is opened.
+
+ +

The function `tctdbtune' is used in order to set the tuning parameters of a table database object.

+ +
+
bool tctdbtune(TCTDB *tdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
`tdb' specifies the table database object which is not opened.
+
`bnum' specifies the number of elements of the bucket array. If it is not more than 0, the default value is specified. The default value is 131071. Suggested size of the bucket array is about from 0.5 to 4 times of the number of all records to be stored.
+
`apow' specifies the size of record alignment by power of 2. If it is negative, the default value is specified. The default value is 4 standing for 2^4=16.
+
`fpow' specifies the maximum number of elements of the free block pool by power of 2. If it is negative, the default value is specified. The default value is 10 standing for 2^10=1024.
+
`opts' specifies options by bitwise-or: `TDBTLARGE' specifies that the size of the database can be larger than 2GB by using 64-bit bucket array, `TDBTDEFLATE' specifies that each record is compressed with Deflate encoding, `TDBTBZIP' specifies that each record is compressed with BZIP2 encoding, `TDBTTCBS' specifies that each record is compressed with TCBS encoding.
+
If successful, the return value is true, else, it is false.
+
Note that the tuning parameters should be set before the database is opened.
+
+ +

The function `tctdbsetcache' is set the caching parameters of a table database object.

+ +
+
bool tctdbsetcache(TCTDB *tdb, int32_t rcnum, int32_t lcnum, int32_t ncnum);
+
`tdb' specifies the table database object which is not opened.
+
`rcnum' specifies the maximum number of records to be cached. If it is not more than 0, the record cache is disabled. It is disabled by default.
+
`lcnum' specifies the maximum number of leaf nodes to be cached. If it is not more than 0, the default value is specified. The default value is 4096.
+
`ncnum' specifies the maximum number of non-leaf nodes to be cached. If it is not more than 0, the default value is specified. The default value is 512.
+
If successful, the return value is true, else, it is false.
+
Note that the caching parameters should be set before the database is opened. Leaf nodes and non-leaf nodes are used in column indices.
+
+ +

The function `tctdbsetxmsiz' is used in order to set the size of the extra mapped memory of a table database object.

+ +
+
bool tctdbsetxmsiz(TCTDB *tdb, int64_t xmsiz);
+
`tdb' specifies the table database object which is not opened.
+
`xmsiz' specifies the size of the extra mapped memory. If it is not more than 0, the extra mapped memory is disabled. The default size is 67108864.
+
If successful, the return value is true, else, it is false.
+
Note that the mapping parameters should be set before the database is opened.
+
+ +

The function `tctdbsetdfunit' is used in order to set the unit step number of auto defragmentation of a table database object.

+ +
+
bool tctdbsetdfunit(TCTDB *tdb, int32_t dfunit);
+
`tdb' specifies the table database object which is not opened.
+
`dfunit' specifie the unit step number. If it is not more than 0, the auto defragmentation is disabled. It is disabled by default.
+
If successful, the return value is true, else, it is false.
+
Note that the defragmentation parameters should be set before the database is opened.
+
+ +

The function `tctdbopen' is used in order to open a database file and connect a table database object.

+ +
+
bool tctdbopen(TCTDB *tdb, const char *path, int omode);
+
`tdb' specifies the table database object which is not opened.
+
`path' specifies the path of the database file.
+
`omode' specifies the connection mode: `TDBOWRITER' as a writer, `TDBOREADER' as a reader. If the mode is `TDBOWRITER', the following may be added by bitwise-or: `TDBOCREAT', which means it creates a new database if not exist, `TDBOTRUNC', which means it creates a new database regardless if one exists, `TDBOTSYNC', which means every transaction synchronizes updated contents with the device. Both of `TDBOREADER' and `TDBOWRITER' can be added to by bitwise-or: `TDBONOLCK', which means it opens the database file without file locking, or `TDBOLCKNB', which means locking is performed without blocking.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tctdbclose' is used in order to close a table database object.

+ +
+
bool tctdbclose(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
If successful, the return value is true, else, it is false.
+
Update of a database is assured to be written when the database is closed. If a writer opens a database but does not close it appropriately, the database will be broken.
+
+ +

The function `tctdbput' is used in order to store a record into a table database object.

+ +
+
bool tctdbput(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols);
+
`tdb' specifies the table database object connected as a writer.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
`cols' specifies a map object containing columns.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tctdbput2' is used in order to store a string record into a table database object with a zero separated column string.

+ +
+
bool tctdbput2(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz);
+
`tdb' specifies the table database object connected as a writer.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
`cbuf' specifies the pointer to the region of the zero separated column string where the name and the value of each column are situated one after the other.
+
`csiz' specifies the size of the region of the column string.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tctdbput3' is used in order to store a string record into a table database object with a tab separated column string.

+ +
+
bool tctdbput3(TCTDB *tdb, const char *pkstr, const char *cstr);
+
`tdb' specifies the table database object connected as a writer.
+
`pkstr' specifies the string of the primary key.
+
`cstr' specifies the string of the the tab separated column string where the name and the value of each column are situated one after the other.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tctdbputkeep' is used in order to store a new record into a table database object.

+ +
+
bool tctdbputkeep(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols);
+
`tdb' specifies the table database object connected as a writer.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
`cols' specifies a map object containing columns.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tctdbputkeep2' is used in order to store a new string record into a table database object with a zero separated column string.

+ +
+
bool tctdbputkeep2(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz);
+
`tdb' specifies the table database object connected as a writer.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
`cbuf' specifies the pointer to the region of the zero separated column string where the name and the value of each column are situated one after the other.
+
`csiz' specifies the size of the region of the column string.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tctdbputkeep3' is used in order to store a new string record into a table database object with a tab separated column string.

+ +
+
bool tctdbputkeep3(TCTDB *tdb, const char *pkstr, const char *cstr);
+
`tdb' specifies the table database object connected as a writer.
+
`pkstr' specifies the string of the primary key.
+
`cstr' specifies the string of the the tab separated column string where the name and the value of each column are situated one after the other.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tctdbputcat' is used in order to concatenate columns of the existing record in a table database object.

+ +
+
bool tctdbputcat(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols);
+
`tdb' specifies the table database object connected as a writer.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
`cols' specifies a map object containing columns.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tctdbputcat2' is used in order to concatenate columns in a table database object with a zero separated column string.

+ +
+
bool tctdbputcat2(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz);
+
`tdb' specifies the table database object connected as a writer.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
`cbuf' specifies the pointer to the region of the zero separated column string where the name and the value of each column are situated one after the other.
+
`csiz' specifies the size of the region of the column string.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tctdbputcat3' is used in order to concatenate columns in a table database object with with a tab separated column string.

+ +
+
bool tctdbputcat3(TCTDB *tdb, const char *pkstr, const char *cstr);
+
`tdb' specifies the table database object connected as a writer.
+
`pkstr' specifies the string of the primary key.
+
`cstr' specifies the string of the the tab separated column string where the name and the value of each column are situated one after the other.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tctdbout' is used in order to remove a record of a table database object.

+ +
+
bool tctdbout(TCTDB *tdb, const void *pkbuf, int pksiz);
+
`tdb' specifies the table database object connected as a writer.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tctdbout2' is used in order to remove a string record of a table database object.

+ +
+
bool tctdbout2(TCTDB *tdb, const char *pkstr);
+
`tdb' specifies the table database object connected as a writer.
+
`pkstr' specifies the string of the primary key.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tctdbget' is used in order to retrieve a record in a table database object.

+ +
+
TCMAP *tctdbget(TCTDB *tdb, const void *pkbuf, int pksiz);
+
`tdb' specifies the table database object.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
If successful, the return value is a map object of the columns of the corresponding record. `NULL' is returned if no record corresponds.
+
Because the object of the return value is created with the function `tcmapnew', it should be deleted with the function `tcmapdel' when it is no longer in use.
+
+ +

The function `tctdbget2' is used in order to retrieve a record in a table database object as a zero separated column string.

+ +
+
char *tctdbget2(TCTDB *tdb, const void *pkbuf, int pksiz, int *sp);
+
`tdb' specifies the table database object.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the column string of the corresponding record. `NULL' is returned if no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tctdbget3' is used in order to retrieve a string record in a table database object as a tab separated column string.

+ +
+
char *tctdbget3(TCTDB *tdb, const char *pkstr);
+
`tdb' specifies the table database object.
+
`pkstr' specifies the string of the primary key.
+
If successful, the return value is the tab separated column string of the corresponding record. `NULL' is returned if no record corresponds.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tctdbvsiz' is used in order to get the size of the value of a record in a table database object.

+ +
+
int tctdbvsiz(TCTDB *tdb, const void *pkbuf, int pksiz);
+
`tdb' specifies the table database object.
+
`kbuf' specifies the pointer to the region of the primary key.
+
`ksiz' specifies the size of the region of the primary key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tctdbvsiz2' is used in order to get the size of the value of a string record in a table database object.

+ +
+
int tctdbvsiz2(TCTDB *tdb, const char *pkstr);
+
`tdb' specifies the table database object.
+
`kstr' specifies the string of the primary key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tctdbiterinit' is used in order to initialize the iterator of a table database object.

+ +
+
bool tctdbiterinit(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
If successful, the return value is true, else, it is false.
+
The iterator is used in order to access the primary key of every record stored in a database.
+
+ +

The function `tctdbiternext' is used in order to get the next primary key of the iterator of a table database object.

+ +
+
void *tctdbiternext(TCTDB *tdb, int *sp);
+
`tdb' specifies the table database object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the next primary key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. It is allowed to update or remove records whose keys are fetched while the iteration. However, it is not assured if updating the database is occurred while the iteration. Besides, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access.
+
+ +

The function `tctdbiternext2' is used in order to get the next primary key string of the iterator of a table database object.

+ +
+
char *tctdbiternext2(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
If successful, the return value is the string of the next primary key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. However, it is not assured if updating the database is occurred while the iteration. Besides, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access.
+
+ +

The function `tctdbiternext3' is used in order to get the columns of the next record of the iterator of a table database object.

+ +
+
TCMAP *tctdbiternext3(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
If successful, the return value is a map object of the columns of the next record, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator. The primary key is added into the map as a column of an empty string key.
+
Because the object of the return value is created with the function `tcmapnew', it should be deleted with the function `tcmapdel' when it is no longer in use. It is possible to access every record by iteration of calling this function. However, it is not assured if updating the database is occurred while the iteration. Besides, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access.
+
+ +

The function `tctdbfwmkeys' is used in order to get forward matching primary keys in a table database object.

+ +
+
TCLIST *tctdbfwmkeys(TCTDB *tdb, const void *pbuf, int psiz, int max);
+
`tdb' specifies the table database object.
+
`pbuf' specifies the pointer to the region of the prefix.
+
`psiz' specifies the size of the region of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tctdbfwmkeys2' is used in order to get forward matching string primary keys in a table database object.

+ +
+
TCLIST *tctdbfwmkeys2(TCTDB *tdb, const char *pstr, int max);
+
`tdb' specifies the table database object.
+
`pstr' specifies the string of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tctdbaddint' is used in order to add an integer to a column of a record in a table database object.

+ +
+
int tctdbaddint(TCTDB *tdb, const void *pkbuf, int pksiz, int num);
+
`tdb' specifies the table database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the primary key.
+
`ksiz' specifies the size of the region of the primary key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is `INT_MIN'.
+
The additional value is stored as a decimal string value of a column whose name is "_num". If no record corresponds, a new record with the additional value is stored.
+
+ +

The function `tctdbadddouble' is used in order to add a real number to a column of a record in a table database object.

+ +
+
double tctdbadddouble(TCTDB *tdb, const void *pkbuf, int pksiz, double num);
+
`tdb' specifies the table database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the primary key.
+
`ksiz' specifies the size of the region of the primary key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is Not-a-Number.
+
The additional value is stored as a decimal string value of a column whose name is "_num". If no record corresponds, a new record with the additional value is stored.
+
+ +

The function `tctdbsync' is used in order to synchronize updated contents of a table database object with the file and the device.

+ +
+
bool tctdbsync(TCTDB *tdb);
+
`tdb' specifies the table database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
This function is useful when another process connects to the same database file.
+
+ +

The function `tctdboptimize' is used in order to optimize the file of a table database object.

+ +
+
bool tctdboptimize(TCTDB *tdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
`tdb' specifies the table database object connected as a writer.
+
`bnum' specifies the number of elements of the bucket array. If it is not more than 0, the default value is specified. The default value is two times of the number of records.
+
`apow' specifies the size of record alignment by power of 2. If it is negative, the current setting is not changed.
+
`fpow' specifies the maximum number of elements of the free block pool by power of 2. If it is negative, the current setting is not changed.
+
`opts' specifies options by bitwise-or: `TDBTLARGE' specifies that the size of the database can be larger than 2GB by using 64-bit bucket array, `TDBTDEFLATE' specifies that each record is compressed with Deflate encoding, `TDBTBZIP' specifies that each record is compressed with BZIP2 encoding, `TDBTTCBS' specifies that each record is compressed with TCBS encoding. If it is `UINT8_MAX', the current setting is not changed.
+
If successful, the return value is true, else, it is false.
+
This function is useful to reduce the size of the database file with data fragmentation by successive updating.
+
+ +

The function `tctdbvanish' is used in order to remove all records of a table database object.

+ +
+
bool tctdbvanish(TCTDB *tdb);
+
`tdb' specifies the table database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tctdbcopy' is used in order to copy the database file of a table database object.

+ +
+
bool tctdbcopy(TCTDB *tdb, const char *path);
+
`tdb' specifies the table database object.
+
`path' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line.
+
If successful, the return value is true, else, it is false. False is returned if the executed command returns non-zero code.
+
The database file is assured to be kept synchronized and not modified while the copying or executing operation is in progress. So, this function is useful to create a backup file of the database file.
+
+ +

The function `tctdbtranbegin' is used in order to begin the transaction of a table database object.

+ +
+
bool tctdbtranbegin(TCTDB *tdb);
+
`tdb' specifies the table database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
The database is locked by the thread while the transaction so that only one transaction can be activated with a database object at the same time. Thus, the serializable isolation level is assumed if every database operation is performed in the transaction. Because all pages are cached on memory while the transaction, the amount of referred records is limited by the memory capacity. If the database is closed during transaction, the transaction is aborted implicitly.
+
+ +

The function `tctdbtrancommit' is used in order to commit the transaction of a table database object.

+ +
+
bool tctdbtrancommit(TCTDB *tdb);
+
`tdb' specifies the table database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is fixed when it is committed successfully.
+
+ +

The function `tctdbtranabort' is used in order to abort the transaction of a table database object.

+ +
+
bool tctdbtranabort(TCTDB *tdb);
+
`tdb' specifies the table database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is discarded when it is aborted. The state of the database is rollbacked to before transaction.
+
+ +

The function `tctdbpath' is used in order to get the file path of a table database object.

+ +
+
const char *tctdbpath(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
The return value is the path of the database file or `NULL' if the object does not connect to any database file.
+
+ +

The function `tctdbrnum' is used in order to get the number of records ccccof a table database object.

+ +
+
uint64_t tctdbrnum(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
The return value is the number of records or 0 if the object does not connect to any database file.
+
+ +

The function `tctdbfsiz' is used in order to get the size of the database file of a table database object.

+ +
+
uint64_t tctdbfsiz(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
The return value is the size of the database file or 0 if the object does not connect to any database file.
+
+ +

The function `tctdbsetindex' is used in order to set a column index to a table database object.

+ +
+
bool tctdbsetindex(TCTDB *tdb, const char *name, int type);
+
`tdb' specifies the table database object connected as a writer.
+
`name' specifies the name of a column. If the name of an existing index is specified, the index is rebuilt. An empty string means the primary key.
+
`type' specifies the index type: `TDBITLEXICAL' for lexical string, `TDBITDECIMAL' for decimal string, `TDBITTOKEN' for token inverted index, `TDBITQGRAM' for q-gram inverted index. If it is `TDBITOPT', the index is optimized. If it is `TDBITVOID', the index is removed. If `TDBITKEEP' is added by bitwise-or and the index exists, this function merely returns failure.
+
If successful, the return value is true, else, it is false.
+
Note that the setting indices should be set after the database is opened.
+
+ +

The function `tctdbgenuid' is used in order to generate a unique ID number of a table database object.

+ +
+
int64_t tctdbgenuid(TCTDB *tdb);
+
`tdb' specifies the table database object connected as a writer.
+
The return value is the new unique ID number or -1 on failure.
+
+ +

The function `tctdbqrynew' is used in order to create a query object.

+ +
+
TDBQRY *tctdbqrynew(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
The return value is the new query object.
+
+ +

The function `tctdbqrydel' is used in order to delete a query object.

+ +
+
void tctdbqrydel(TDBQRY *qry);
+
`qry' specifies the query object.
+
+ +

The function `tctdbqryaddcond' is used in order to add a narrowing condition to a query object.

+ +
+
void tctdbqryaddcond(TDBQRY *qry, const char *name, int op, const char *expr);
+
`qry' specifies the query object.
+
`name' specifies the name of a column. An empty string means the primary key.
+
`op' specifies an operation type: `TDBQCSTREQ' for string which is equal to the expression, `TDBQCSTRINC' for string which is included in the expression, `TDBQCSTRBW' for string which begins with the expression, `TDBQCSTREW' for string which ends with the expression, `TDBQCSTRAND' for string which includes all tokens in the expression, `TDBQCSTROR' for string which includes at least one token in the expression, `TDBQCSTROREQ' for string which is equal to at least one token in the expression, `TDBQCSTRRX' for string which matches regular expressions of the expression, `TDBQCNUMEQ' for number which is equal to the expression, `TDBQCNUMGT' for number which is greater than the expression, `TDBQCNUMGE' for number which is greater than or equal to the expression, `TDBQCNUMLT' for number which is less than the expression, `TDBQCNUMLE' for number which is less than or equal to the expression, `TDBQCNUMBT' for number which is between two tokens of the expression, `TDBQCNUMOREQ' for number which is equal to at least one token in the expression, `TDBQCFTSPH' for full-text search with the phrase of the expression, `TDBQCFTSAND' for full-text search with all tokens in the expression, `TDBQCFTSOR' for full-text search with at least one token in the expression, `TDBQCFTSEX' for full-text search with the compound expression. All operations can be flagged by bitwise-or: `TDBQCNEGATE' for negation, `TDBQCNOIDX' for using no index.
+
`expr' specifies an operand exression.
+
+ +

The function `tctdbqrysetorder' is used in order to set the order of a query object.

+ +
+
void tctdbqrysetorder(TDBQRY *qry, const char *name, int type);
+
`qry' specifies the query object.
+
`name' specifies the name of a column. An empty string means the primary key.
+
`type' specifies the order type: `TDBQOSTRASC' for string ascending, `TDBQOSTRDESC' for string descending, `TDBQONUMASC' for number ascending, `TDBQONUMDESC' for number descending.
+
+ +

The function `tctdbqrysetlimit' is used in order to set the limit number of records of the result of a query object.

+ +
+
void tctdbqrysetlimit(TDBQRY *qry, int max, int skip);
+
`qry' specifies the query object.
+
`max' specifies the maximum number of records of the result. If it is negative, no limit is specified.
+
`skip' specifies the number of skipped records of the result. If it is not more than 0, no record is skipped.
+
+ +

The function `tctdbqrysearch' is used in order to execute the search of a query object.

+ +
+
TCLIST *tctdbqrysearch(TDBQRY *qry);
+
`qry' specifies the query object.
+
The return value is a list object of the primary keys of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tctdbqrysearchout' is used in order to remove each record corresponding to a query object.

+ +
+
bool tctdbqrysearchout(TDBQRY *qry);
+
`qry' specifies the query object of the database connected as a writer.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tctdbqryproc' is used in order to process each record corresponding to a query object.

+ +
+
bool tctdbqryproc(TDBQRY *qry, TDBQRYPROC proc, void *op);
+
`qry' specifies the query object of the database connected as a writer.
+
`proc' specifies the pointer to the iterator function called for each record. It receives four parameters. The first parameter is the pointer to the region of the primary key. The second parameter is the size of the region of the primary key. The third parameter is a map object containing columns. The fourth parameter is the pointer to the optional opaque object. It returns flags of the post treatment by bitwise-or: `TDBQPPUT' to modify the record, `TDBQPOUT' to remove the record, `TDBQPSTOP' to stop the iteration.
+
`op' specifies an arbitrary pointer to be given as a parameter of the iterator function. If it is not needed, `NULL' can be specified.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tctdbqryhint' is used in order to get the hint string of a query object.

+ +
+
const char *tctdbqryhint(TDBQRY *qry);
+
`qry' specifies the query object.
+
The return value is the hint string.
+
+ +

The function `tctdbmetasearch' is used in order to retrieve records with multiple query objects and get the set of the result.

+ +
+
TCLIST *tctdbmetasearch(TDBQRY **qrys, int num, int type);
+
`qrys' specifies an array of the query objects.
+
`num' specifies the number of elements of the array.
+
`type' specifies a set operation type: `TDBMSUNION' for the union set, `TDBMSISECT' for the intersection set, `TDBMSDIFF' for the difference set.
+
The return value is a list object of the primary keys of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds.
+
If the first query object has the order setting, the result array is sorted by the order. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

Example Code

+ +

The following code is an example to use a table database.

+ +
#include <tcutil.h>
+#include <tctdb.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+int main(int argc, char **argv){
+  TCTDB *tdb;
+  int ecode, pksiz, i, rsiz;
+  char pkbuf[256];
+  const char *rbuf, *name;
+  TCMAP *cols;
+  TDBQRY *qry;
+  TCLIST *res;
+
+  /* create the object */
+  tdb = tctdbnew();
+
+  /* open the database */
+  if(!tctdbopen(tdb, "casket.tct", TDBOWRITER | TDBOCREAT)){
+    ecode = tctdbecode(tdb);
+    fprintf(stderr, "open error: %s\n", tctdberrmsg(ecode));
+  }
+
+  /* store a record */
+  pksiz = sprintf(pkbuf, "%ld", (long)tctdbgenuid(tdb));
+  cols = tcmapnew3("name", "mikio", "age", "30", "lang", "ja,en,c", NULL);
+  if(!tctdbput(tdb, pkbuf, pksiz, cols)){
+    ecode = tctdbecode(tdb);
+    fprintf(stderr, "put error: %s\n", tctdberrmsg(ecode));
+  }
+  tcmapdel(cols);
+
+  /* store a record in a naive way */
+  pksiz = sprintf(pkbuf, "12345");
+  cols = tcmapnew();
+  tcmapput2(cols, "name", "falcon");
+  tcmapput2(cols, "age", "31");
+  tcmapput2(cols, "lang", "ja");
+  if(!tctdbput(tdb, pkbuf, pksiz, cols)){
+    ecode = tctdbecode(tdb);
+    fprintf(stderr, "put error: %s\n", tctdberrmsg(ecode));
+  }
+  tcmapdel(cols);
+
+  /* store a record with a TSV string */
+  if(!tctdbput3(tdb, "abcde", "name\tjoker\tage\t19\tlang\ten,es")){
+    ecode = tctdbecode(tdb);
+    fprintf(stderr, "put error: %s\n", tctdberrmsg(ecode));
+  }
+
+  /* search for records */
+  qry = tctdbqrynew(tdb);
+  tctdbqryaddcond(qry, "age", TDBQCNUMGE, "20");
+  tctdbqryaddcond(qry, "lang", TDBQCSTROR, "ja,en");
+  tctdbqrysetorder(qry, "name", TDBQOSTRASC);
+  tctdbqrysetlimit(qry, 10, 0);
+  res = tctdbqrysearch(qry);
+  for(i = 0; i < tclistnum(res); i++){
+    rbuf = tclistval(res, i, &rsiz);
+    cols = tctdbget(tdb, rbuf, rsiz);
+    if(cols){
+      printf("%s", rbuf);
+      tcmapiterinit(cols);
+      while((name = tcmapiternext2(cols)) != NULL){
+        printf("\t%s\t%s", name, tcmapget2(cols, name));
+      }
+      printf("\n");
+      tcmapdel(cols);
+    }
+  }
+  tclistdel(res);
+  tctdbqrydel(qry);
+
+  /* close the database */
+  if(!tctdbclose(tdb)){
+    ecode = tctdbecode(tdb);
+    fprintf(stderr, "close error: %s\n", tctdberrmsg(ecode));
+  }
+
+  /* delete the object */
+  tctdbdel(tdb);
+
+  return 0;
+}
+
+ +

CLI

+ +

To use the table database API easily, the commands `tcttest', `tctmttest', and `tctmgr' are provided.

+ +

The command `tcttest' is a utility for facility test and performance test. This command is used in the following format. `path' specifies the path of a database file. `rnum' specifies the number of iterations. `bnum' specifies the number of buckets. `apow' specifies the power of the alignment. `fpow' specifies the power of the free block pool.

+ +
+
tcttest write [-mt] [-tl] [-td|-tb|-tt|-tx] [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-ip] [-is] [-in] [-it] [-if] [-ix] [-nl|-nb] [-rnd] path rnum [bnum [apow [fpow]]]
+
Store records with columns "str", "num", "type", and "flag".
+
tcttest read [-mt] [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path
+
Retrieve all records of the database above.
+
tcttest remove [-mt] [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path
+
Remove all records of the database above.
+
tcttest rcat [-mt] [-tl] [-td|-tb|-tt|-tx] [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-ip] [-is] [-in] [-it] [-if] [-ix] [-nl|-nb] [-pn num] [-dai|-dad|-rl|-ru] path rnum [bnum [apow [fpow]]]
+
Store records with partway duplicated keys using concatenate mode.
+
tcttest misc [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum
+
Perform miscellaneous test of various operations.
+
tcttest wicked [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum
+
Perform updating operations selected at random.
+
+ +

Options feature the following.

+ +
    +
  • -mt : call the function `tctdbsetmutex'.
  • +
  • -tl : enable the option `TDBTLARGE'.
  • +
  • -td : enable the option `TDBTDEFLATE'.
  • +
  • -tb : enable the option `TDBTBZIP'.
  • +
  • -tt : enable the option `TDBTTCBS'.
  • +
  • -tx : enable the option `TDBTEXCODEC'.
  • +
  • -rc num : specify the number of cached records.
  • +
  • -lc num : specify the number of cached leaf pages.
  • +
  • -nc num : specify the number of cached non-leaf pages.
  • +
  • -xm num : specify the size of the extra mapped memory.
  • +
  • -df num : specify the unit step number of auto defragmentation.
  • +
  • -ip : create the number index for the primary key.
  • +
  • -is : create the string index for the column "str".
  • +
  • -in : create the number index for the column "num".
  • +
  • -it : create the string index for the column "type".
  • +
  • -if : create the token inverted index for the column "flag".
  • +
  • -ix : create the q-gram inverted index for the column "text".
  • +
  • -nl : enable the option `TDBNOLCK'.
  • +
  • -nb : enable the option `TDBLCKNB'.
  • +
  • -rnd : select keys at random.
  • +
  • -pn num : specify the number of patterns.
  • +
  • -dai : use the function `tctdbaddint' instead of `tctdbputcat'.
  • +
  • -dad : use the function `tctdbadddouble' instead of `tctdbputcat'.
  • +
  • -rl : set the length of values at random.
  • +
  • -ru : select update operations at random.
  • +
+ +

This command returns 0 on success, another on failure.

+ +

The command `tctmttest' is a utility for facility test under multi-thread situation. This command is used in the following format. `path' specifies the path of a database file. `tnum' specifies the number of running threads. `rnum' specifies the number of iterations. `bnum' specifies the number of buckets. `apow' specifies the power of the alignment. `fpow' specifies the power of the free block pool.

+ +
+
tctmttest write [-tl] [-td|-tb|-tt|-tx] [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-ip] [-is] [-in] [-it] [-if] [-ix] [-nl|-nb] [-rnd] path tnum rnum [bnum [apow [fpow]]]
+
Store records with columns "str", "num", "type", and "flag".
+
tctmttest read [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path tnum
+
Retrieve all records of the database above.
+
tctmttest remove [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path tnum
+
Remove all records of the database above.
+
tctmttest wicked [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path tnum rnum
+
Perform updating operations selected at random.
+
tctmttest typical [-tl] [-td|-tb|-tt|-tx] [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-rr num] path tnum rnum [bnum [apow [fpow]]
+
Perform typical operations selected at random.
+
+ +

Options feature the following.

+ +
    +
  • -tl : enable the option `TDBTLARGE'.
  • +
  • -td : enable the option `TDBTDEFLATE'.
  • +
  • -tb : enable the option `TDBTBZIP'.
  • +
  • -tt : enable the option `TDBTTCBS'.
  • +
  • -tx : enable the option `TDBTEXCODEC'.
  • +
  • -rc num : specify the number of cached records.
  • +
  • -lc num : specify the number of cached leaf pages.
  • +
  • -nc num : specify the number of cached non-leaf pages.
  • +
  • -xm num : specify the size of the extra mapped memory.
  • +
  • -df num : specify the unit step number of auto defragmentation.
  • +
  • -ip : create the number index for the primary key.
  • +
  • -is : create the string index for the column "str".
  • +
  • -in : create the number index for the column "num".
  • +
  • -it : create the string index for the column "type".
  • +
  • -if : create the token inverted index for the column "flag".
  • +
  • -ix : create the q-gram inverted index for the column "text".
  • +
  • -nl : enable the option `TDBNOLCK'.
  • +
  • -nb : enable the option `TDBLCKNB'.
  • +
  • -rnd : select keys at random.
  • +
  • -nc : omit the comparison test.
  • +
  • -rr num : specify the ratio of reading operation by percentage.
  • +
+ +

This command returns 0 on success, another on failure.

+ +

The command `tctmgr' is a utility for test and debugging of the table database API and its applications. `path' specifies the path of a database file. `bnum' specifies the number of buckets. `apow' specifies the power of the alignment. `fpow' specifies the power of the free block pool. `pkey' specifies the primary key of a record. `cols' specifies the names and the values of a record alternately. `name' specifies the name of a column. `op' specifies an operator. `expr' specifies the condition expression. `file' specifies the input file.

+ +
+
tctmgr create [-tl] [-td|-tb|-tt|-tx] path [bnum [apow [fpow]]]
+
Create a database file.
+
tctmgr inform [-nl|-nb] path
+
Print miscellaneous information to the standard output.
+
tctmgr put [-nl|-nb] [-sx] [-dk|-dc|-dai|-dad] path pkey [cols ...]
+
Store a record.
+
tctmgr out [-nl|-nb] [-sx] path pkey
+
Remove a record.
+
tctmgr get [-nl|-nb] [-sx] [-px] [-pz] path pkey
+
Print the value of a record.
+
tctmgr list [-nl|-nb] [-m num] [-pv] [-px] [-fm str] path
+
Print the primary keys of all records, separated by line feeds.
+
tctmgr search [-nl|-nb] [-ord name type] [-m num] [-sk num] [-kw] [-pv] [-px] [-ph] [-bt num] [-rm] [-ms type] path [name op expr ...]
+
Print records matching conditions, separated by line feeds.
+
tctmgr optimize [-tl] [-td|-tb|-tt|-tx] [-tz] [-nl|-nb] [-df] path [bnum [apow [fpow]]]
+
Optimize a database file.
+
tctmgr setindex [-nl|-nb] [-it type] path name
+
Set the index of a column.
+
tctmgr importtsv [-nl|-nb] [-sc] path [file]
+
Store records of TSV in each line of a file.
+
tctmgr version
+
Print the version information of Tokyo Cabinet.
+
+ +

Options feature the following.

+ +
    +
  • -tl : enable the option `TDBTLARGE'.
  • +
  • -td : enable the option `TDBTDEFLATE'.
  • +
  • -tb : enable the option `TDBTBZIP'.
  • +
  • -tt : enable the option `TDBTTCBS'.
  • +
  • -tx : enable the option `TDBTEXCODEC'.
  • +
  • -nl : enable the option `TDBNOLCK'.
  • +
  • -nb : enable the option `TDBLCKNB'.
  • +
  • -sx : the input data is evaluated as a hexadecimal data string.
  • +
  • -dk : use the function `tctdbputkeep' instead of `tctdbput'.
  • +
  • -dc : use the function `tctdbputcat' instead of `tctdbput'.
  • +
  • -dai : use the function `tctdbaddint' instead of `tctdbput'.
  • +
  • -dad : use the function `tctdbadddouble' instead of `tctdbput'.
  • +
  • -px : the output data is converted into a hexadecimal data string.
  • +
  • -pz : do not append line feed at the end of the output.
  • +
  • -m num : specify the maximum number of the output.
  • +
  • -pv : print values of records also.
  • +
  • -fm str : specify the prefix of keys.
  • +
  • -ord name type : specify the order of the result.
  • +
  • -sk num : specify the number of skipped records.
  • +
  • -kw : print KWIC string.
  • +
  • -ph : print hint information also.
  • +
  • -bt : specify the number of benchmark tests.
  • +
  • -rm : remove every record in the result.
  • +
  • -ms type : specify the set operation of meta search.
  • +
  • -tz : enable the option `UINT8_MAX'.
  • +
  • -df : perform defragmentation only.
  • +
  • -it type : specify the index type among "lexical", "decimal", "token", "qgram", and "void".
  • +
  • -cd : create the number index instead of the string index.
  • +
  • -cv : remove the existing index.
  • +
  • -sc : normalize keys as lower cases.
  • +
+ +

The operator of the `search' subcommand is one of "STREQ", "STRINC", "STRBW", "STREW", "STRAND", "STROR", "STROREQ", "STRRX", "NUMEQ", "NUMGT", "NUMGE", "NUMLT", "NUMLE", "NUMBT", "NUMOREQ", "FTSPH", "FTSAND", "FTSOR", and "FTSEX". If "~" preposes each operator, the logical meaning is reversed. If "+" preposes each operator, no index is used for the operator. The type of the `-ord' option is one of "STRASC", "STRDESC", "NUMASC", and "NUMDESC". The type of the `-ms' option is one of "UNION", "ISECT", and "DIFF". This command returns 0 on success, another on failure.

+ +
+ +

The Abstract Database API

+ +

Abstract database is a set of interfaces to use on-memory hash database, on-memory tree database, hash database, B+ tree database, fixed-length database, and table database with the same API. See `tcadb.h' for the entire specification.

+ +

Description

+ +

To use the abstract database API, include `tcutil.h', `tcadb.h', and related standard header files. Usually, write the following description near the front of a source file.

+ +
+
#include <tcutil.h>
+
#include <tcadb.h>
+
#include <stdlib.h>
+
#include <stdbool.h>
+
#include <stdint.h>
+
+ +

Objects whose type is pointer to `TCADB' are used to handle abstract databases. An abstract database object is created with the function `tcadbnew' and is deleted with the function `tcadbdel'. To avoid memory leak, it is important to delete every object when it is no longer in use.

+ +

Before operations to store or retrieve records, it is necessary to connect the abstract database object to the concrete one. The function `tcadbopen' is used to open a concrete database and the function `tcadbclose' is used to close the database. To avoid data missing or corruption, it is important to close every database instance when it is no longer in use. It is forbidden for multible database objects in a process to open the same database at the same time.

+ +

API

+ +

The function `tcadbnew' is used in order to create an abstract database object.

+ +
+
TCADB *tcadbnew(void);
+
The return value is the new abstract database object.
+
+ +

The function `tcadbdel' is used in order to delete an abstract database object.

+ +
+
void tcadbdel(TCADB *adb);
+
`adb' specifies the abstract database object.
+
+ +

The function `tcadbopen' is used in order to open an abstract database.

+ +
+
bool tcadbopen(TCADB *adb, const char *name);
+
`adb' specifies the abstract database object.
+
`name' specifies the name of the database. If it is "*", the database will be an on-memory hash database. If it is "+", the database will be an on-memory tree database. If its suffix is ".tch", the database will be a hash database. If its suffix is ".tcb", the database will be a B+ tree database. If its suffix is ".tcf", the database will be a fixed-length database. If its suffix is ".tct", the database will be a table database. Otherwise, this function fails. Tuning parameters can trail the name, separated by "#". Each parameter is composed of the name and the value, separated by "=". On-memory hash database supports "bnum", "capnum", and "capsiz". On-memory tree database supports "capnum" and "capsiz". Hash database supports "mode", "bnum", "apow", "fpow", "opts", "rcnum", "xmsiz", and "dfunit". B+ tree database supports "mode", "lmemb", "nmemb", "bnum", "apow", "fpow", "opts", "lcnum", "ncnum", "xmsiz", and "dfunit". Fixed-length database supports "mode", "width", and "limsiz". Table database supports "mode", "bnum", "apow", "fpow", "opts", "rcnum", "lcnum", "ncnum", "xmsiz", "dfunit", and "idx".
+
If successful, the return value is true, else, it is false.
+
The tuning parameter "capnum" specifies the capacity number of records. "capsiz" specifies the capacity size of using memory. Records spilled the capacity are removed by the storing order. "mode" can contain "w" of writer, "r" of reader, "c" of creating, "t" of truncating, "e" of no locking, and "f" of non-blocking lock. The default mode is relevant to "wc". "opts" can contains "l" of large option, "d" of Deflate option, "b" of BZIP2 option, and "t" of TCBS option. "idx" specifies the column name of an index and its type separated by ":". For example, "casket.tch#bnum=1000000#opts=ld" means that the name of the database file is "casket.tch", and the bucket number is 1000000, and the options are large and Deflate.
+
+ +

The function `tcadbclose' is used in order to close an abstract database object.

+ +
+
bool tcadbclose(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is true, else, it is false.
+
Update of a database is assured to be written when the database is closed. If a writer opens a database but does not close it appropriately, the database will be broken.
+
+ +

The function `tcadbput' is used in order to store a record into an abstract database object.

+ +
+
bool tcadbput(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcadbput2' is used in order to store a string record into an abstract object.

+ +
+
bool tcadbput2(TCADB *adb, const char *kstr, const char *vstr);
+
`adb' specifies the abstract database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcadbputkeep' is used in order to store a new record into an abstract database object.

+ +
+
bool tcadbputkeep(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcadbputkeep2' is used in order to store a new string record into an abstract database object.

+ +
+
bool tcadbputkeep2(TCADB *adb, const char *kstr, const char *vstr);
+
`adb' specifies the abstract database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcadbputcat' is used in order to concatenate a value at the end of the existing record in an abstract database object.

+ +
+
bool tcadbputcat(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcadbputcat2' is used in order to concatenate a string value at the end of the existing record in an abstract database object.

+ +
+
bool tcadbputcat2(TCADB *adb, const char *kstr, const char *vstr);
+
`adb' specifies the abstract database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcadbout' is used in order to remove a record of an abstract database object.

+ +
+
bool tcadbout(TCADB *adb, const void *kbuf, int ksiz);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcadbout2' is used in order to remove a string record of an abstract database object.

+ +
+
bool tcadbout2(TCADB *adb, const char *kstr);
+
`adb' specifies the abstract database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcadbget' is used in order to retrieve a record in an abstract database object.

+ +
+
void *tcadbget(TCADB *adb, const void *kbuf, int ksiz, int *sp);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcadbget2' is used in order to retrieve a string record in an abstract database object.

+ +
+
char *tcadbget2(TCADB *adb, const char *kstr);
+
`adb' specifies the abstract database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcadbvsiz' is used in order to get the size of the value of a record in an abstract database object.

+ +
+
int tcadbvsiz(TCADB *adb, const void *kbuf, int ksiz);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcadbvsiz2' is used in order to get the size of the value of a string record in an abstract database object.

+ +
+
int tcadbvsiz2(TCADB *adb, const char *kstr);
+
`adb' specifies the abstract database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcadbiterinit' is used in order to initialize the iterator of an abstract database object.

+ +
+
bool tcadbiterinit(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is true, else, it is false.
+
The iterator is used in order to access the key of every record stored in a database.
+
+ +

The function `tcadbiternext' is used in order to get the next key of the iterator of an abstract database object.

+ +
+
void *tcadbiternext(TCADB *adb, int *sp);
+
`adb' specifies the abstract database object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. It is allowed to update or remove records whose keys are fetched while the iteration. However, it is not assured if updating the database is occurred while the iteration. Besides, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access.
+
+ +

The function `tcadbiternext2' is used in order to get the next key string of the iterator of an abstract database object.

+ +
+
char *tcadbiternext2(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is the string of the next key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. However, it is not assured if updating the database is occurred while the iteration. Besides, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access.
+
+ +

The function `tcadbfwmkeys' is used in order to get forward matching keys in an abstract database object.

+ +
+
TCLIST *tcadbfwmkeys(TCADB *adb, const void *pbuf, int psiz, int max);
+
`adb' specifies the abstract database object.
+
`pbuf' specifies the pointer to the region of the prefix.
+
`psiz' specifies the size of the region of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcadbfwmkeys2' is used in order to get forward matching string keys in an abstract database object.

+ +
+
TCLIST *tcadbfwmkeys2(TCADB *adb, const char *pstr, int max);
+
`adb' specifies the abstract database object.
+
`pstr' specifies the string of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcadbaddint' is used in order to add an integer to a record in an abstract database object.

+ +
+
int tcadbaddint(TCADB *adb, const void *kbuf, int ksiz, int num);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is `INT_MIN'.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcadbadddouble' is used in order to add a real number to a record in an abstract database object.

+ +
+
double tcadbadddouble(TCADB *adb, const void *kbuf, int ksiz, double num);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is Not-a-Number.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcadbsync' is used in order to synchronize updated contents of an abstract database object with the file and the device.

+ +
+
bool tcadbsync(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcadboptimize' is used in order to optimize the storage of an abstract database object.

+ +
+
bool tcadboptimize(TCADB *adb, const char *params);
+
`adb' specifies the abstract database object.
+
`params' specifies the string of the tuning parameters, which works as with the tuning of parameters the function `tcadbopen'. If it is `NULL', it is not used.
+
If successful, the return value is true, else, it is false.
+
This function is useful to reduce the size of the database storage with data fragmentation by successive updating.
+
+ +

The function `tcadbvanish' is used in order to remove all records of an abstract database object.

+ +
+
bool tcadbvanish(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcadbcopy' is used in order to copy the database file of an abstract database object.

+ +
+
bool tcadbcopy(TCADB *adb, const char *path);
+
`adb' specifies the abstract database object.
+
`path' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line.
+
If successful, the return value is true, else, it is false. False is returned if the executed command returns non-zero code.
+
The database file is assured to be kept synchronized and not modified while the copying or executing operation is in progress. So, this function is useful to create a backup file of the database file.
+
+ +

The function `tcadbtranbegin' is used in order to begin the transaction of an abstract database object.

+ +
+
bool tcadbtranbegin(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is true, else, it is false.
+
The database is locked by the thread while the transaction so that only one transaction can be activated with a database object at the same time. Thus, the serializable isolation level is assumed if every database operation is performed in the transaction. All updated regions are kept track of by write ahead logging while the transaction. If the database is closed during transaction, the transaction is aborted implicitly.
+
+ +

The function `tcadbtrancommit' is used in order to commit the transaction of an abstract database object.

+ +
+
bool tcadbtrancommit(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is fixed when it is committed successfully.
+
+ +

The function `tcadbtranabort' is used in order to abort the transaction of an abstract database object.

+ +
+
bool tcadbtranabort(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is discarded when it is aborted. The state of the database is rollbacked to before transaction.
+
+ +

The function `tcadbpath' is used in order to get the file path of an abstract database object.

+ +
+
const char *tcadbpath(TCADB *adb);
+
`adb' specifies the abstract database object.
+
The return value is the path of the database file or `NULL' if the object does not connect to any database. "*" stands for on-memory hash database. "+" stands for on-memory tree database.
+
+ +

The function `tcadbrnum' is used in order to get the number of records of an abstract database object.

+ +
+
uint64_t tcadbrnum(TCADB *adb);
+
`adb' specifies the abstract database object.
+
The return value is the number of records or 0 if the object does not connect to any database instance.
+
+ +

The function `tcadbsize' is used in order to get the size of the database of an abstract database object.

+ +
+
uint64_t tcadbsize(TCADB *adb);
+
`adb' specifies the abstract database object.
+
The return value is the size of the database or 0 if the object does not connect to any database instance.
+
+ +

The function `tcadbmisc' is used in order to call a versatile function for miscellaneous operations of an abstract database object.

+ +
+
TCLIST *tcadbmisc(TCADB *adb, const char *name, const TCLIST *args);
+
`adb' specifies the abstract database object.
+
`name' specifies the name of the function. All databases support "put", "out", "get", "putlist", "outlist", "getlist", and "getpart". "put" is to store a record. It receives a key and a value, and returns an empty list. "out" is to remove a record. It receives a key, and returns an empty list. "get" is to retrieve a record. It receives a key, and returns a list of the values. "putlist" is to store records. It receives keys and values one after the other, and returns an empty list. "outlist" is to remove records. It receives keys, and returns an empty list. "getlist" is to retrieve records. It receives keys, and returns keys and values of corresponding records one after the other. "getpart" is to retrieve the partial value of a record. It receives a key, the offset of the region, and the length of the region.
+
`args' specifies a list object containing arguments.
+
If successful, the return value is a list object of the result. `NULL' is returned on failure.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

Example Code

+ +

The following code is an example to use an abstract database.

+ +
#include <tcutil.h>
+#include <tcadb.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+int main(int argc, char **argv){
+  TCADB *adb;
+  char *key, *value;
+
+  /* create the object */
+  adb = tcadbnew();
+
+  /* open the database */
+  if(!tcadbopen(adb, "casket.tch")){
+    fprintf(stderr, "open error\n");
+  }
+
+  /* store records */
+  if(!tcadbput2(adb, "foo", "hop") ||
+     !tcadbput2(adb, "bar", "step") ||
+     !tcadbput2(adb, "baz", "jump")){
+    fprintf(stderr, "put error\n");
+  }
+
+  /* retrieve records */
+  value = tcadbget2(adb, "foo");
+  if(value){
+    printf("%s\n", value);
+    free(value);
+  } else {
+    fprintf(stderr, "get error\n");
+  }
+
+  /* traverse records */
+  tcadbiterinit(adb);
+  while((key = tcadbiternext2(adb)) != NULL){
+    value = tcadbget2(adb, key);
+    if(value){
+      printf("%s:%s\n", key, value);
+      free(value);
+    }
+    free(key);
+  }
+
+  /* close the database */
+  if(!tcadbclose(adb)){
+    fprintf(stderr, "close error\n");
+  }
+
+  /* delete the object */
+  tcadbdel(adb);
+
+  return 0;
+}
+
+ +

CLI

+ +

To use the abstract database API easily, the commands `tcatest', `tcamttest' and `tcamgr' are provided.

+ +

The command `tcatest' is a utility for facility test and performance test. This command is used in the following format. `name' specifies the database name. `rnum' specifies the number of iterations. `tnum' specifies the number of transactions.

+ +
+
tcatest write name rnum
+
Store records with keys of 8 bytes. They change as `00000001', `00000002'...
+
tcatest read name
+
Retrieve all records of the database above.
+
tcatest remove name
+
Remove all records of the database above.
+
tcatest rcat name rnum
+
Store records with partway duplicated keys using concatenate mode.
+
tcatest misc name rnum
+
Perform miscellaneous test of various operations.
+
tcatest wicked name rnum
+
Perform updating operations of list and map selected at random.
+
tcatest compare name tnum rnum
+
Perform comparison test of database schema.
+
+ +

This command returns 0 on success, another on failure.

+ +

The command `tcamttest' is a utility for facility test under multi-thread situation. This command is used in the following format. `name' specifies the database name. `tnum' specifies the number of running threads. `rnum' specifies the number of iterations.

+ +
+
tcamttest write name tnum rnum
+
Store records with keys of 8 bytes. They change as `00000001', `00000002'...
+
tcamttest read name tnum
+
Retrieve all records of the database above.
+
tcamttest remove name tnum
+
Remove all records of the database above.
+
+ +

This command returns 0 on success, another on failure.

+ +

The command `tcamgr' is a utility for test and debugging of the abstract database API and its applications. `name' specifies the name of a database. `key' specifies the key of a record. `value' specifies the value of a record. `params' specifies the tuning parameters. `func' specifies the name of a function. `arg' specifies the arguments of the function. `dest' specifies the path of the destination file.

+ +
+
tcamgr create name
+
Create a database file.
+
tcamgr inform name
+
Print miscellaneous information to the standard output.
+
tcamgr put [-sx] [-sep chr] [-dk|-dc|-dai|-dad] name key value
+
Store a record.
+
tcamgr out [-sx] [-sep chr] name key
+
Remove a record.
+
tcamgr get [-sx] [-sep chr] [-px] [-pz] name key
+
Print the value of a record.
+
tcamgr list [-sep chr] [-m num] [-pv] [-px] [-fm str] name
+
Print keys of all records, separated by line feeds.
+
tcamgr optimize name params
+
Optimize a database file.
+
tcamgr misc [-sx] [-sep chr] [-px] name func [arg...]
+
Call a versatile function for miscellaneous operations.
+
tcamgr map [-fm str] name dest
+
Map records into another B+ tree database.
+
tcamgr version
+
Print the version information of Tokyo Cabinet.
+
+ +

Options feature the following.

+ +
    +
  • -sx : the input data is evaluated as a hexadecimal data string.
  • +
  • -sep chr : specify the separator of the input data.
  • +
  • -dk : use the function `tcadbputkeep' instead of `tcadbput'.
  • +
  • -dc : use the function `tcadbputcat' instead of `tcadbput'.
  • +
  • -dai : use the function `tcadbaddint' instead of `tcadbput'.
  • +
  • -dad : use the function `tcadbadddouble' instead of `tcadbput'.
  • +
  • -px : the output data is converted into a hexadecimal data string.
  • +
  • -pz : do not append line feed at the end of the output.
  • +
  • -m num : specify the maximum number of the output.
  • +
  • -pv : print values of records also.
  • +
  • -fm str : specify the prefix of keys.
  • +
+ +

This command returns 0 on success, another on failure.

+ +

CGI

+ +

To use the abstract database API easily, the CGI script `tcawmgr.cgi' is provided.

+ +

The CGI script `tcawmgr.cgi' is a utility to browse and edit an abstract database by Web interface. The database should be placed in the same directory of the CGI script and named as "casket.tch", "casket.tcb", or "casket.tcf". And, its permission should allow reading and writing by the user executing the CGI script. Install the CGI script in a public directory of your Web server then you can start to use the CGI script by accessing the assigned URL.

+ +
+ +

File Format

+ +

This section describes the format of the database files of Tokyo Cabinet.

+ +

File Format of Hash Database

+ +

There are four sections in the file managed by the hash database; the header section, the bucket section, the free block pool section, and the record section. Numeric values in the file are serialized in the little endian order or in the variable length format. The latter format is delta encoding based on the 128-radix numbering.

+ +

The header section is from the top of the file and its length is 256 bytes. There are the following information.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
nameoffsetlengthfeature
magic number032identification of the database. Begins with "ToKyO CaBiNeT"
database type321hash (0x01) / B+ tree (0x02) / fixed-length (0x03) / table (0x04)
additional flags331logical union of open (1<<0) and fatal (1<<1)
alignment power341the alignment size, by power of 2
free block pool power351the number of elements in the free block pool, by power of 2
options361logical union of large (1<<0), Deflate (1<<1), BZIP2 (1<<2), TCBS (1<<3), extra codec (1<<4)
bucket number408the number of elements of the bucket array
record number488the number of records in the database
file size568the file size of the database
first record648the offset of the first record
opaque region128128users can use this region arbitrarily
+ +

The bucket section trails the header section and its size is defined by the bucket number. Each element of the bucket array indicates the offset of the first record of the hash chain. The format of each element is the fixed length number and its size is 4 bytes in the normal mode or 8 bytes in the large mode. The offset is recorded as the quotient by the alignment.

+ +

The free block pool section trails the bucket section and its size is defined by the free block pool number. Each element of the free block pool indicates the offset and the size of each free block. The offset is recorded as the difference of the former free block and as the quotient by the alignment. The offset and the size are serialized in the variable length format.

+ +

The record section trails the free block pool section and occupies the rest region to the end of the file. Each element has the following information. The region of each record begins at the offset of the multiple of the alignment.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
nameoffsetlengthfeature
magic number01identification of record block. always 0xC8
hash value11the hash value to decide the path of the hash chain
left chain24the alignment quotient of the destination of the left chain
right chain64the alignment quotient of the destination of the right chain
padding size102the size of the padding
key size12varythe size of the key
value sizevaryvarythe size of the value
keyvaryvarythe data of the key
valuevaryvarythe data of the value
paddingvaryvaryuseless data
+ +

However, regions of free blocks contain the following information.

+ + + + + + + + + + + + + + + + + + + + +
nameoffsetlengthfeature
magic number01identification of record block. always 0xB0
block size14size of the block
+ +

The transaction log is recorded in the file whose name is composed of the database name and the suffix ".wal". The top eight bytes indicate the file size of the beginning of the transaction. After that, there are the following information.

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
nameoffsetlengthfeature
offset08the offset of the updated region
size84the size of the updated region
data12varythe data before update
+ +

File Format of B+ Tree Database

+ +

All data managed by the B+ tree database are recorded in the hash database. Recorded data are classified into meta data and logical pages. Logical pages are classified into leaf nodes and non-leaf nodes. The formats of the fixed length number and the variable length number are the same as with the hash database.

+ +

Meta data are recorded in the opaque region in the header of the hash database and have the following information.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
nameoffsetlengthfeature
comparison function01tccmplexical (0x00), tccmpdecimal (0x01), tccmpint32 (0x02), tccmpint64 (0x03), other (0xff)
reserved region17not used
record number of leaf node84the maximum number of records in a leaf node
index number of non-leaf node124the maximum number of indices in a leaf node
root node ID168the page ID of the root node of B+ tree
first leaf ID248the page ID of the first leaf node
last leaf ID328the page ID of the last leaf node
leaf number408the number of the leaf nodes
non-leaf number488the number of the non-leaf nodes
record number568the number of records in the database
+ +

Each leaf node contains a list of records. Each non-leaf node contains a list of indices to child nodes. Though each record is a logical unit of user data, records with the same key are integrated into one record physically. Each physical record has the following information.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
nameoffsetlengthfeature
key size0varythe size of the key
value sizevaryvarythe size of the value
duplication numbervaryvarythe number of values with the same key
keyvaryvarythe data of the key
valuevaryvarythe data of the value
duplicated recordsvaryvarya list of value sizes and value data
+ +

Each leaf node is a physical unit of a set of records. Each leaf node is identified by the sequential ID number from 1. Each leaf node is recorded in the hash database. The key is a string in the hexadecimal numbering. The value has the following information. Records are kept in the ascending order of keys.

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
nameoffsetlengthfeature
previous leaf0varythe ID number of the previous leaf node
next leafvaryvarythe ID number of the next leaf node
record listvaryvarythe serialized data of all records in the node
+ +

Each index is a logical unit of pointer to the child node. Each index has the following information.

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
nameoffsetlengthfeature
page ID0varythe ID number of the referred page
key sizevaryvarythe size of the key
keyvaryvarythe data of the key
+ +

Each non-leaf node is a physical unit of a set of indices. Each non-leaf node is identified by the sequential number from 281474976710657. Each non-leaf node is recorded in the hash database. The key is a string begins with "#" and is trailed by the hexadecimal number of the ID number subtracted by 281474976710657. The value has the following information. Indices are kept in the ascending order of keys.

+ + + + + + + + + + + + + + + + + + + + +
nameoffsetlengthfeature
accession ID0varythe ID number of the first child node
index listvaryvarythe serialized data of all indices in the node
+ +

File Format of Fixed-length Database

+ +

There are two sections in the file managed by the fixed-length database; the header section, and the record section. Numeric values in the file are serialized in the little endian order.

+ +

The header section is from the top of the file and its length is 256 bytes. There are the following information.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
nameoffsetlengthfeature
magic number032identification of the database. Begins with "ToKyO CaBiNeT"
database type321always 0x03
additional flags331logical union of open (1<<0) and fatal (1<<1)
record number488the number of records in the database
file size568the file size of the database
record width648the width of each record
limit size728the limit size of the database
least ID808the least ID number of records
greatest ID888the greatest ID number of records
opaque region128128users can use this region arbitrarily
+ +

The record section trails the header section and occupies the rest region to the end of the file. Each element has the following information. The size region takes 1 byte if the record width is less than 256 bytes, or takes 2 bytes if the record width is less than 65536, else takes 4 bytes. The size of each record is the summation of the size of the width region and the record width. So, the region of each record begins at the offset generated by the ID number subtracted by 1 and multiplied by the record width and the added by 256.

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
nameoffsetlengthfeature
value size0varythe size of the value
valuevaryvarythe data of the value
paddingvaryvarypadding. If the size of the value is 0, the first byte indicates whether the record exists or not
+ +

The naming convention and the file format of the transaction log file is the same as the one of the hash database.

+ +

Note

+ +

Because database files are not sparse, you can copy them as with normal files. Moreover, the database formats don't depend on the byte order of the running environment, you can migrate the database files between environments with different byte orders.

+ +

If possible, set the MIME type `application/x-tokyocabinet-hash' when sending files of the hash database. The suffix of the file name should be `.tch'. As for the B+ tree database, `application/x-tokyocabinet-btree' and `.tcb'. As for the fixed-length database, `application/x-tokyocabinet-fixed' and `.tcf'. As for the table database, `application/x-tokyocabinet-btree' and `.tct'.

+ +

To make the `file' command identify the database formats, append the following lines to the `magic' file.

+ +
# Tokyo Cabinet magic data
+0       string    ToKyO\ CaBiNeT\n   Tokyo Cabinet
+>14     string    x                  \b (%s)
+>32     byte      0                  \b, Hash
+!:mime  application/x-tokyocabinet-hash
+>32     byte      1                  \b, B+ tree
+!:mime  application/x-tokyocabinet-btree
+>32     byte      2                  \b, Fixed-length
+!:mime  application/x-tokyocabinet-fixed
+>32     byte      3                  \b, Table
+!:mime  application/x-tokyocabinet-table
+>33     byte      &1                 \b, [open]
+>33     byte      &2                 \b, [fatal]
+>34     byte      x                  \b, apow=%d
+>35     byte      x                  \b, fpow=%d
+>36     byte      &1                 \b, [large]
+>36     byte      &2                 \b, [deflate]
+>36     byte      &4                 \b, [bzip]
+>36     byte      &8                 \b, [tcbs]
+>36     byte      &16                \b, [excodec]
+>40     lequad    x                  \b, bnum=%lld
+>48     lequad    x                  \b, rnum=%lld
+>56     lequad    x                  \b, fsiz=%lld
+
+ +
+ +

License

+ +

Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License or any later version.

+ +

Tokyo Cabinet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

+ +

You should have received a copy of the GNU Lesser General Public License along with Tokyo Cabinet (See the file `COPYING'); if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.

+ +

Tokyo Cabinet was written by FAL Labs. You can contact the author by e-mail to `info@fallabs.com'.

+ +
+ + + + + + diff --git a/tcejdb/doc/spex-ja.html b/tcejdb/doc/spex-ja.html new file mode 100644 index 0000000..e3938d1 --- /dev/null +++ b/tcejdb/doc/spex-ja.html @@ -0,0 +1,7476 @@ + + + + + + + + + + + + + + + + + + +Fundamental Specifications of Tokyo Cabinet Version 1 (Japanese) + + + + +

Tokyo Cabinet第1版基本仕様書

+ +
Copyright (C) 2006-2012 FAL Labs
+
Last Update: Sat, 18 Aug 2012 11:05:00 +0900
+ + +
+ +

目次

+ +
    +
  1. はじめに
  2. +
  3. 特徴
  4. +
  5. インストール
  6. +
  7. ユーティリティAPI
  8. +
  9. ハッシュデータベースAPI
  10. +
  11. B+木データベースAPI
  12. +
  13. 固定長データベースAPI
  14. +
  15. テーブルデータベースAPI
  16. +
  17. 抽象データベースAPI
  18. +
  19. ちょっとしたコツ
  20. +
  21. ファイルフォーマット
  22. +
  23. よく聞かれる質問
  24. +
  25. ライセンス
  26. +
+ +
+ +

はじめに

+ +

Tokyo Cabinetはデータベースを扱うルーチン群のライブラリです。データベースといっても単純なもので、キーと値のペアからなるレコード群を格納したデータファイルです。キーと値は任意の長さを持つ一連のバイト列であり、文字列でもバイナリでも扱うことができます。テーブルやデータ型の概念はありません。レコードはハッシュ表かB+木か固定長配列で編成されます。

+ +

ハッシュ表のデータベースでは、キーはデータベース内で一意であり、キーが重複する複数のレコードを格納することはできません。このデータベースに対しては、キーと値を指定してレコードを格納したり、キーを指定して対応するレコードを削除したり、キーを指定して対応するレコードを検索したりすることができます。また、データベースに格納してある全てのキーを順不同に一つずつ取り出すこともできます。このような操作は、UNIX標準で定義されているDBMライブラリおよびその追従であるNDBMやGDBMに類するものです。Tokyo CabinetはDBMのより良い代替として利用することができます。

+ +

B+木のデータベースでは、キーが重複する複数のレコードを格納することができます。このデータベースに対しては、ハッシュ表のデータベースと同様に、キーを指定してレコードを格納したり取り出したり削除したりすることができます。レコードはユーザが指示した比較関数に基づいて整列されて格納されます。カーソルを用いて各レコードを昇順または降順で参照することができます。この機構によって、文字列の前方一致検索や数値の範囲検索が可能になります。

+ +

固定長配列のデータベースでは、一意な自然数をキーとしてレコードが格納されます。キーが重複する複数のレコードを格納することはできません。また、各レコードの値の長さは一定以下に制限されます。提供される操作はハッシュデータベースとほぼ同様です。

+ +

ハッシュ表のデータベース変種として、テーブルのデータベースも提供されます。各レコードは主キーで識別されるとともに、名前付きコラムの集合を値として持ちます。データスキーマの概念はありませんが、任意のコラムに張られたインデックスを用いることで複雑な条件に基づくレコードの検索を効率化することができます。

+ +

Tokyo CabinetはC言語で記述され、CとPerlとRubyとJavaとLuaのAPIとして提供されます。Tokyo CabinetはC99およびPOSIX準拠のAPIを備えるプラットフォームで利用できます。Tokyo CabinetはGNU Lesser General Public Licenseに基づくフリーソフトウェアです。

+ +
+ +

特徴

+ +

Tokyo CabinetはQDBMの後継であり、空間効率と時間効率と使いやすさを向上させた製品です。この節ではTokyo Cabinetの特徴について説明します。

+ +

DBM一族の最右翼

+ +

Tokyo CabinetはGDBMやQDBMの後継として次の点を目標として開発されました。これらの目標は達成されており、Tokyo Cabinetは従来のDBMを置き換える製品だと言えます。

+ +
    +
  • 空間効率の向上 : データベースファイルがより小さい
  • +
  • 時間効率の向上 : 処理がより高速である
  • +
  • 並列性の向上 : マルチスレッド環境での同時実行性能の向上
  • +
  • 利便性の向上 : APIがより単純である
  • +
  • 堅牢性の向上 : 不慮の事態でもデータベースファイルが壊れにくい
  • +
  • 64ビット対応 : 巨大なメモリ空間とデータベースファイルを扱える
  • +
+ +

Tokyo CabinetはQDBMと同様に、伝統的なDBMが抱える三つの制限事項を回避しています。すなわち、プロセス内で複数のデータベースを扱うことができ、キーと値のサイズに制限がなく、データベースファイルがスパースではありません。さらに、QDBMが抱える三つの制限事項を回避しています。すなわち、2GB以上のデータベースファイルを扱うことができ、バイトオーダの異なる環境間でデータベースファイルを共有することができ、複数のスレッドが同時にデータベースの探索を行うことができます。

+ +

Tokyo Cabinetは高速に動作します。例えば100万件のレコードの登録にかかる時間は、ハッシュデータベースで0.7秒ほど、B+木データベースで1.6秒ほどです。そしてTokyo Cabinetのデータベースは小さいです。例えば1レコードあたりのオーバーヘッドは、ハッシュデータベースで16バイトほど、B+木データベースで5バイトほどです。さらにTokyo Cabinetで扱えるデータの規模は莫大です。最大8EB(9.22e18バイト)までのデータベースファイルを扱うことができます。

+ +

効率的なハッシュデータベースの実装

+ +

Tokyo Cabinetはレコードの探索にハッシュアルゴリズムを用います。バケット配列に十分な要素数があれば、レコードの探索にかかる時間計算量は O(1) です。すなわち、レコードの探索に必要な時間はデータベースの規模に関わらず一定です。追加や削除に関しても同様です。ハッシュ値の衝突はセパレートチェーン法で管理します。チェーンのデータ構造は二分探索木です。したがって、バケット配列の要素数が著しく少ない場合でも、探索等の時間計算量は O(log n) に抑えられます。

+ +

Tokyo Cabinetはバケット配列を全てRAM上に保持することによって、処理の高速化を図ります。バケット配列がRAM上にあれば、ほぼ1パスのファイル操作でレコードに該当するファイル上の領域を参照することができます。ファイルに記録されたバケット配列は `read' コールでRAM上に読み込むのではなく、`mmap' コールでRAMに直接マッピングされます。したがって、データベースに接続する際の準備時間が極めて短く、また、複数のプロセスでメモリマップを共有することができます。

+ +

バケット配列の要素数が格納するレコード数の半分ほどであれば、データの性質によって多少前後しますが、ハッシュ値の衝突率は56.7%ほどです(等倍だと36.8%、2倍だと21.3%、4倍だと11.5%、8倍だと6.0%ほど)。そのような場合、平均2パス以下のファイル操作でレコードを探索することができます。これを性能指標とするならば、例えば100万個のレコードを格納するためには50万要素のバケット配列が求められます。バケット配列の各要素は4バイトです。すなわち、2MバイトのRAMが利用できれば100万レコードのデータベースが構築できます。

+ +

伝統的なDBMにはレコードの追加操作に関して「挿入」モードと「置換」モードがあります。前者では、キーが既存のレコードと重複する際に既存の値を残します。後者では、キーが既存のレコードと重複した際に新しい値に置き換えます。Tokyo Cabinetはその2つに加えて「連結」モードがあります。既存の値の末尾に指定された値を連結して格納する操作です。レコードの値を配列として扱う場合、要素を追加するには連結モードが役に立ちます。

+ +

一般的に、データベースの更新処理を続けるとファイル内の利用可能領域の断片化(フラグメンテーション)が起き、ファイルのサイズが肥大化してしまいます。Tokyo Cabinetは隣接する不要領域を連結して再利用することによってこの問題に対処します。既存のレコードの値をより大きなサイズの値に上書きする場合、そのレコードの領域をファイル中の別の位置に移動させる必要があります。この処理の時間計算量はレコードのサイズに依存するので、値を拡張していく場合には効率が悪くなります。しかし、Tokyo Cabinetはアラインメントによってこの問題に対処します。増分がパディングに収まれば領域を移動させる必要はありません。

+ +

不要領域を効率的に再利用するための「フリーブロックプール」も実装されています。これは不要になった領域をリストに記憶しておき、新しい領域が要求された際ににリストの中から最も小さい不要領域(ベストフィット)を選択して再利用するものです。それでも断片化は避けられないので、レコードの領域を詰め直して最適化(デフラグ)する二種類の機能も実装されています。一つめは静的な最適化で、全てのレコードを別ファイルに配置しなおしてから一気に書き戻すものです。二つめは動的な最適化で、レコードと不要領域の位置を入れ替える操作を少しずつ行って不要領域を集結させていくものです。

+ +

便利なB+木データベースの実装

+ +

B+木データベースはハッシュデータベースより遅いのですが、ユーザが定義した順序に基づいて各レコードを参照できることが特長です。B+木は複数のレコードを整列させた状態で論理的なページにまとめて管理します。各ページに対してはB木すなわち多進平衡木によって階層化された疎インデックスが維持されます。したがって、各レコードの探索等にかかる時間計算量は O(log n) です。各レコードを順番に参照するためにカーソルが提供されます。カーソルの場所はキーを指定して飛ばすことができ、また現在の場所から次のレコードに進めたり前のレコードに戻したりすることができます。各ページは双方向リンクリストで編成されるので、カーソルを前後に移動させる操作の時間計算量は O(1) です。

+ +

B+木データベースは上述のハッシュデータベースを基盤として実装されます。B+木の各ページはハッシュデータベースのレコードとして記録されるので、ハッシュデータベースの記憶管理の効率性を継承しています。B+木では各レコードのヘッダが小さく、アラインメントはページの単位でとられるので、ほとんどの場合、ハッシュデータベースに較べてデータベースファイルのサイズが半減します。B+木を更新する際には多くのページを操作する必要がありますが、Tokyo Cabinetはページをキャッシュすることによってファイル操作を減らして処理を効率化します。ほとんどの場合、疎インデックス全体がメモリ上にキャッシュされるので、各レコードを参照するのに必要なファイル操作は平均1パス以下です。

+ +

各ページを圧縮して保存する機能も提供されます。圧縮方式はZLIBのDeflateとBZIP2のブロックソーティングの2種類をサポートしています。同一ページ内の各レコードは似たようなパターンを持つため、Lempel-ZivやBWTなどのアルゴリズムを適用すると高い圧縮効率が期待できます。テキストデータを扱う場合、データベースのサイズが元の25%程度になります。データベースの規模が大きくディスクI/Oがボトルネックとなる場合は、圧縮機能を有効化すると処理速度が大幅に改善されます。

+ +

素朴な固定長データベースの実装

+ +

固定長データベースは、キーが自然数でなくてはならず、また値のサイズが制限されますが、その条件を受諾できる場合には最も効率的です。レコード群は固定長の要素の配列として保持され、各レコードはキーの倍数から算出されるオフセットの位置に格納されます。したがって、各レコードの探索等にかかる時間計算量は O(1) です。提供される操作群はハッシュデータベースとほぼ同じです。

+ +

データベース全体を `mmap' コールでメモリ上にマッピングして多次元配列として参照するので、ファイルI/Oにかかるオーバーヘッドは極小化されます。構造が単純なおかげで、固定長データベースはハッシュデータベースよりもさらに高速に動作するとともに、マルチスレッド環境での並列実行性能も傑出しています。

+ +

データベースのサイズは、キーの変域と値の制限長に比例します。すなわち、キーの変域が小さく、値のサイズが小さいほど、空間効率は向上します。例えば、キーの最大値が100万で、値の制限長が100バイトの場合、データベースのサイズは100MBほどになります。RAM上に読み込まれるのは実際に参照されたレコードの周辺の領域のみなので、データベースのサイズは仮想メモリのサイズまで大きくすることができます。

+ +

柔軟なテーブルデータベースの実装

+ +

テーブルデータベースは、単純なキーと値の構造ではなく、リレーショナルデータベースの表のような構造を表現します。各レコードは主キーで識別されるとともに、任意の文字列で名前を付けられたコラムの集合を値として持ちます。例えば、社員番号を主キーにして、名前や部署や給与などのコラムを構造化して格納することができます。リレーショナルデータベースと違ってデータスキーマを事前に定義する必要はなく、レコード毎に異なる種類のコラムを持たせることができます。

+ +

テーブルデータベースに対しては、主キー以外の条件でも問い合わせを行うことができます。条件はコラムの名前と条件式で構成されます。条件式の演算子としては、文字列型に関しては完全一致や前方一致や正規表現などが提供され、数値型に関しては完全一致や範囲一致が提供されます。タグ検索や全文検索の演算子も提供されます。クエリに複数の条件式を持たせることで論理積条件を指定できます。複数のクエリを使って検索を行うことで論理和条件を指定できます。検索結果の順序は文字列または数値の昇順または降順を指定することができます。

+ +

コラムを使った検索やソートを高速化するために、コラム毎のインデックスを作成することができます。コラムには型の概念はありませんが、インデックスには文字列型もしくは数値型の区別があります。空白区切りトークンと文字N-gramトークンの転置インデックスもサポートされます。クエリオプティマイザは検索条件やソート条件に応じた最適な順序でインデックスを利用します。インデックスはB+木データベースの外部ファイルとして実装されます。

+ +

実用的な機能性

+ +

ファイルシステム上のデータベースはトランザクション機構を提供します。トランザクションを開始してから終了するまでの一連の操作を一括してデータベースにコミットしたり、一連の更新操作を破棄してデータベースの状態をトランザクションの開始前の状態にロールバックしたりすることができます。トランザクションの分離レベルは2種類あります。データベースに対する全ての操作をトランザクション内で行うと直列化可能(serializable)トランザクションとなり、トランザクション外の操作を同時に行うと非コミット読み取り(read uncommitted)トランザクションとなります。耐久性はログ先行書き込みとシャドウページングによって担保されます。

+ +

Tokyo Cabinetにはデータベースに接続するモードとして、「リーダ」と「ライタ」の二種類があります。リーダは読み込み専用で、ライタは読み書き両用です。データベースにはファイルロックによってプロセス間での排他制御が行われます。ライタが接続している間は、他のプロセスはリーダとしてもライタとしても接続できません。リーダが接続している間は、他のプロセスのリーダは接続できるが、ライタは接続できません。この機構によって、マルチタスク環境での同時接続に伴うデータの整合性が保証されます。

+ +

Tokyo CabinetのAPIの各関数はリエントラントであり、マルチスレッド環境で安全に利用することができます。別個のデータベースオブジェクトに対しては全ての操作を完全に並列に行うことができます。同一のデータベースオブジェクトに対しては、リードライトロックで排他制御を行います。すなわち、読み込みを行うスレッド同士は並列に実行でき、書き込みを行うスレッドは他の読み込みや書き込みをブロックします。ロックの粒度は、ハッシュデータベースと固定長データベースではレコード単位、それ以外のデータベースではファイル単位です。

+ +

単純だが多様なインタフェース群

+ +

Tokyo Cabinetはオブジェクト指向に基づいた簡潔なAPIを提供します。データベースに対する全ての操作はデータベースオブジェクトにカプセル化され、開く(open)、閉じる(close)、挿入する(put)、削除する(out)、取得する(get)といった関数(メソッド)を呼ぶことでプログラミングを進めていけます。ハッシュデータベースとB+木データベースと固定長データベースのAPIは互いに酷似しているので、アプリケーションを一方から他方に移植することも簡単です。さらに、それらのAPI群を全く同じインターフェイスで操作するための抽象APIも提供されます。抽象APIを用いると実行時にデータベースの種類を決定することができます。

+ +

メモリ上でレコードを簡単に扱うために、ユーティリティAPIが提供されます。リストやマップといった基本的なデータ構造をはじめ、メモリプールや文字列処理や符号処理など、プログラミングで良く使う機能を詰め込んでいます。

+ +

C言語のAPIには、ユーティリティAPI、ハッシュデータベースAPI、B+木データベースAPI、固定長データベースAPI、テーブルデータベースAPI、抽象データベースAPIの6種類があります。各APIに対応したコマンドラインインタフェースも用意されています。それらはプロトタイピングやテストやデバッグなどで活躍するでしょう。Tokyo CabinetはC言語の他にも、PerlとRubyとJavaとLuaのAPIを提供します。その他の言語のインターフェイスも第三者によって提供されるでしょう。

+ +

複数のプロセスが同時にデータベースを操作したい場合やリモートホストにあるデータベースを操作したい場合には、リモートサービスを使うと便利です。リモートサービスはデータベースサーバとそのアクセスライブラリからなり、アプリケーションはリモートデータベースAPIを介してデータベースサーバを操作することができます。HTTPやmemcachedプロトコルもサポートするので、ほぼ全てのプラットフォームからデータベースサーバを簡単に操作することができます。

+ +
+ +

インストール

+ +

Tokyo Cabinetのソースパッケージからのインストール方法を説明します。バイナリパッケージのインストール方法についてはそれぞれのパッケージの説明書をご覧ください。

+ +

前提

+ +

Tokyo Cabinetの現在バージョンは、UNIX系のOSで利用することができます。少なくとも、以下の環境では動作するはずです。

+ +
    +
  • Linux 2.4以降 (x86-32/x86-64/PowerPC/Alpha/SPARC)
  • +
  • Mac OS X 10.3以降 (x86-32/x86-64/PowerPC)
  • +
+ +

ソースパッケージを用いてTokyo Cabinetをインストールするには、gccのバージョン3.1以降とmakeが必要です。それらはLinuxやFreeBSDなどには標準的にインストールされています。

+ +

Tokyo Cabinetは、以下のライブラリを利用しています。予めインストールしておいてください。

+ +
    +
  • zlib : 可逆データ圧縮。バージョン1.2.3以降推奨。
  • +
  • bzip2 : 可逆データ圧縮。バージョン1.0.5以降推奨。
  • +
+ +

ビルドとインストール

+ +

Tokyo Cabinetの配布用アーカイブファイルを展開したら、作成されたディレクトリに入ってインストール作業を行います。

+ +

configureスクリプトを実行して、ビルド環境を設定します。

+ +
./configure
+
+ +

プログラムをビルドします。

+ +
make
+
+ +

プログラムの自己診断テストを行います。

+ +
make check
+
+ +

プログラムをインストールします。作業はrootユーザで行います。

+ +
make install
+
+ +

結果

+ +

一連の作業が終ると、以下のファイルがインストールされます。

+ +
/usr/local/include/tcutil.h
+/usr/local/include/tchdb.h
+/usr/local/include/tcbdb.h
+/usr/local/include/tcfdb.h
+/usr/local/include/tctdb.h
+/usr/local/include/tcadb.h
+/usr/local/lib/libtokyocabinet.a
+/usr/local/lib/libtokyocabinet.so.x.y.z
+/usr/local/lib/libtokyocabinet.so.x
+/usr/local/lib/libtokyocabinet.so
+/usr/local/lib/pkgconfig/tokyocabinet.pc
+/usr/local/bin/tcutest
+/usr/local/bin/tcumttest
+/usr/local/bin/tcucodec
+/usr/local/bin/tchtest
+/usr/local/bin/tchmttest
+/usr/local/bin/tchmgr
+/usr/local/bin/tcbmgr
+/usr/local/bin/tcbtest
+/usr/local/bin/tcbmttest
+/usr/local/bin/tcftest
+/usr/local/bin/tcfmttest
+/usr/local/bin/tcfmgr
+/usr/local/bin/tcttest
+/usr/local/bin/tctmttest
+/usr/local/bin/tctmgr
+/usr/local/bin/tcatest
+/usr/local/bin/tcamttest
+/usr/local/bin/tcamgr
+/usr/local/libexec/tcawmgr.cgi
+/usr/local/share/tokyocabinet/...
+/usr/local/man/man1/...
+/usr/local/man/man3/...
+
+ +

configureのオプション

+ +

「./configure」を実行する際には、以下のオプションを指定することができます。

+ +
    +
  • --enable-debug : デバッグ用にビルドする。デバッグシンボルを有効化し、最適化を行わず、静的にリンクする。
  • +
  • --enable-devel : 開発用にビルドする。デバッグシンボルを有効化し、最適化を行い、動的にリンクする。
  • +
  • --enable-profile : プロファイル用にビルドする。プロファイルオプションを有効化し、最適化を行い、動的にリンクする。
  • +
  • --enable-static : 静的にリンクする。
  • +
  • --enable-fastest : 最高速になるように最適化を行う。
  • +
  • --enable-off64 : 32ビット環境でも64ビットのファイルオフセットを用いる。
  • +
  • --enable-swab : バイトオーダの変換を強制する。
  • +
  • --enable-uyield : レースコンディションの検出用にビルドする。
  • +
  • --disable-zlib : ZLIBによるレコード圧縮を無効にする。
  • +
  • --disable-bzip : BZIP2によるレコード圧縮を無効にする。
  • +
  • --disable-pthread : POSIXスレッドのサポートを無効にする。
  • +
  • --disable-shared : 共有ライブラリのビルドを行わない。
  • +
+ +

`--prefix' などのオプションも一般的なUNIXソフトウェアのパッケージと同様に利用可能です。`/usr/local' 以下ではなく '/usr' 以下にインストールしたい場合は `--prefix=/usr' を指定してください。なお、ライブラリ検索パスに `/usr/local/lib' が入っていない環境では、Tokyo Cabinetのアプリケーションを実行する際に環境変数 `LD_LIBRARY_PATH' の値に `/usr/local/lib' を含めておくようにしてください。

+ +

ライブラリの使い方

+ +

Tokyo CabinetはC言語のAPIを提供し、それはC89標準(ANSI C)またはC99標準に準拠したプログラムから利用することができます。Tokyo Cabinetヘッダは `tcutil.h'、`tchdb.h'、`tcbdb.h'、`tcadb.h' として提供されますので、適宜それらをアプリケーションのソースコード中でインクルードした上で、APIの各種機能を利用してください。ライブラリは `libtokyocabinet.a' および `libtokyocabinet.so' として提供され、それらは `libz.so'、`libbz2.so', `librt.so', `libpthread.so'、`libm.so'、`libc.so' に依存しますので、アプリケーションプログラムをビルドする際にはそれらに対応するリンカオプションをつけてください。最も典型的なビルド手順は以下のようになります。

+ +
gcc -I/usr/local/include tc_example.c -o tc_example \
+  -L/usr/local/lib -ltokyocabinet -lz -lbz2 -lrt -lpthread -lm -lc
+
+ +

Tokyo CabinetはC++言語のプログラムからも利用することができます。各ヘッダは暗黙的にCリンケージ(「extern "C"」ブロック)で包まれているので、単にインクルードするだけで利用することができます。

+ +
+ +

ユーティリティAPI

+ +

ユーティリティAPIは、メモリ上で簡単にレコードを扱うためのルーチン集です。特に拡張可能文字列と配列リストがハッシュマップと順序木が便利です。`tcutil.h' にAPIの仕様の完全な記述があります。

+ +

概要

+ +

ユーティリティAPIを使うためには、`tcutil.h' および関連する標準ヘッダファイルをインクルードしてください。通常、ソースファイルの冒頭付近で以下の記述を行います。

+ +
+
#include <tcutil.h>
+
#include <stdlib.h>
+
#include <stdbool.h>
+
#include <stdint.h>
+
+ +

拡張可能文字列を扱う際には、`TCXSTR' 型へのポインタをオブジェクトとして用います。拡張可能文字列オブジェクトは、関数 `tcxstrnew' で作成し、関数 `tcxstrdel' で破棄します。配列リストを扱う際には、`TCLIST' 型へのポインタをオブジェクトとして用います。リストオブジェクトは、関数 `tclistnew' で作成し、関数 `tclistdel' で破棄します。ハッシュマップを扱う際には、`TCMAP' 型へのポインタをオブジェクトとして用います。マップオブジェクトは、関数 `tcmapopen' で作成し、関数 `tcmapdel' で破棄します。順序木を扱う際には、`TCTREE' 型へのポインタをオブジェクトとして用います。ツリーオブジェクトは、関数 `tctreeopen' で作成し、関数 `tctreedel' で破棄します。作成したオブジェクトを使い終わったら必ず破棄してください。そうしないとメモリリークが発生します。

+ +

基礎的なユーティリティのAPI(英語御免)

+ +

The constant `tcversion' is the string containing the version information.

+ +
+
extern const char *tcversion;
+
+ +

The variable `tcfatalfunc' is the pointer to the call back function for handling a fatal error.

+ +
+
extern void (*tcfatalfunc)(const char *);
+
The argument specifies the error message.
+
The initial value of this variable is `NULL'. If the value is `NULL', the default function is called when a fatal error occurs. A fatal error occurs when memory allocation is failed.
+
+ +

The function `tcmalloc' is used in order to allocate a region on memory.

+ +
+
void *tcmalloc(size_t size);
+
`size' specifies the size of the region.
+
The return value is the pointer to the allocated region.
+
This function handles failure of memory allocation implicitly. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tccalloc' is used in order to allocate a nullified region on memory.

+ +
+
void *tccalloc(size_t nmemb, size_t size);
+
`nmemb' specifies the number of elements.
+
`size' specifies the size of each element.
+
The return value is the pointer to the allocated nullified region.
+
This function handles failure of memory allocation implicitly. Because the region of the return value is allocated with the `calloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcrealloc' is used in order to re-allocate a region on memory.

+ +
+
void *tcrealloc(void *ptr, size_t size);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
The return value is the pointer to the re-allocated region.
+
This function handles failure of memory allocation implicitly. Because the region of the return value is allocated with the `realloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmemdup' is used in order to duplicate a region on memory.

+ +
+
void *tcmemdup(const void *ptr, size_t size);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
The return value is the pointer to the allocated region of the duplicate.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcstrdup' is used in order to duplicate a string on memory.

+ +
+
char *tcstrdup(const void *str);
+
`str' specifies the string.
+
The return value is the allocated string equivalent to the specified string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcfree' is used in order to free a region on memory.

+ +
+
void tcfree(void *ptr);
+
`ptr' specifies the pointer to the region. If it is `NULL', this function has no effect.
+
Although this function is just a wrapper of `free' call, this is useful in applications using another package of the `malloc' series.
+
+ +

拡張可能文字列のAPI(英語御免)

+ +

The function `tcxstrnew' is used in order to create an extensible string object.

+ +
+
TCXSTR *tcxstrnew(void);
+
The return value is the new extensible string object.
+
+ +

The function `tcxstrnew2' is used in order to create an extensible string object from a character string.

+ +
+
TCXSTR *tcxstrnew2(const char *str);
+
`str' specifies the string of the initial content.
+
The return value is the new extensible string object containing the specified string.
+
+ +

The function `tcxstrnew3' is used in order to create an extensible string object with the initial allocation size.

+ +
+
TCXSTR *tcxstrnew3(int asiz);
+
`asiz' specifies the initial allocation size.
+
The return value is the new extensible string object.
+
+ +

The function `tcxstrdup' is used in order to copy an extensible string object.

+ +
+
TCXSTR *tcxstrdup(const TCXSTR *xstr);
+
`xstr' specifies the extensible string object.
+
The return value is the new extensible string object equivalent to the specified object.
+
+ +

The function `tcxstrdel' is used in order to delete an extensible string object.

+ +
+
void tcxstrdel(TCXSTR *xstr);
+
`xstr' specifies the extensible string object.
+
Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tcxstrcat' is used in order to concatenate a region to the end of an extensible string object.

+ +
+
void tcxstrcat(TCXSTR *xstr, const void *ptr, int size);
+
`xstr' specifies the extensible string object.
+
`ptr' specifies the pointer to the region to be appended.
+
`size' specifies the size of the region.
+
+ +

The function `tcxstrcat2' is used in order to concatenate a character string to the end of an extensible string object.

+ +
+
void tcxstrcat2(TCXSTR *xstr, const char *str);
+
`xstr' specifies the extensible string object.
+
`str' specifies the string to be appended.
+
+ +

The function `tcxstrptr' is used in order to get the pointer of the region of an extensible string object.

+ +
+
const void *tcxstrptr(const TCXSTR *xstr);
+
`xstr' specifies the extensible string object.
+
The return value is the pointer of the region of the object.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string.
+
+ +

The function `tcxstrsize' is used in order to get the size of the region of an extensible string object.

+ +
+
int tcxstrsize(const TCXSTR *xstr);
+
`xstr' specifies the extensible string object.
+
The return value is the size of the region of the object.
+
+ +

The function `tcxstrclear' is used in order to clear an extensible string object.

+ +
+
void tcxstrclear(TCXSTR *xstr);
+
`xstr' specifies the extensible string object.
+
The internal buffer of the object is cleared and the size is set zero.
+
+ +

The function `tcxstrprintf' is used in order to perform formatted output into an extensible string object.

+ +
+
void tcxstrprintf(TCXSTR *xstr, const char *format, ...);
+
`xstr' specifies the extensible string object.
+
`format' specifies the printf-like format string. The conversion character `%' can be used with such flag characters as `s', `d', `o', `u', `x', `X', `c', `e', `E', `f', `g', `G', `@', `?', `b', and `%'. `@' works as with `s' but escapes meta characters of XML. `?' works as with `s' but escapes meta characters of URL. `b' converts an integer to the string as binary numbers. The other conversion character work as with each original.
+
The other arguments are used according to the format string.
+
+ +

The function `tcsprintf' is used in order to allocate a formatted string on memory.

+ +
+
char *tcsprintf(const char *format, ...);
+
`format' specifies the printf-like format string. The conversion character `%' can be used with such flag characters as `s', `d', `o', `u', `x', `X', `c', `e', `E', `f', `g', `G', `@', `?', `b', and `%'. `@' works as with `s' but escapes meta characters of XML. `?' works as with `s' but escapes meta characters of URL. `b' converts an integer to the string as binary numbers. The other conversion character work as with each original.
+
The other arguments are used according to the format string.
+
The return value is the pointer to the region of the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

配列リストのAPI(英語御免)

+ +

The function `tclistnew' is used in order to create a list object.

+ +
+
TCLIST *tclistnew(void);
+
The return value is the new list object.
+
+ +

The function `tclistnew2' is used in order to create a list object with expecting the number of elements.

+ +
+
TCLIST *tclistnew2(int anum);
+
`anum' specifies the number of elements expected to be stored in the list.
+
The return value is the new list object.
+
+ +

The function `tclistnew3' is used in order to create a list object with initial string elements.

+ +
+
TCLIST *tclistnew3(const char *str, ...);
+
`str' specifies the string of the first element.
+
The other arguments are other elements. They should be trailed by a `NULL' argument.
+
The return value is the new list object.
+
+ +

The function `tclistdup' is used in order to copy a list object.

+ +
+
TCLIST *tclistdup(const TCLIST *list);
+
`list' specifies the list object.
+
The return value is the new list object equivalent to the specified object.
+
+ +

The function `tclistdel' is used in order to delete a list object.

+ +
+
void tclistdel(TCLIST *list);
+
`list' specifies the list object.
+
Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tclistnum' is used in order to get the number of elements of a list object.

+ +
+
int tclistnum(const TCLIST *list);
+
`list' specifies the list object.
+
The return value is the number of elements of the list.
+
+ +

The function `tclistval' is used in order to get the pointer to the region of an element of a list object.

+ +
+
const void *tclistval(const TCLIST *list, int index, int *sp);
+
`list' specifies the list object.
+
`index' specifies the index of the element.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the value.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. If `index' is equal to or more than the number of elements, the return value is `NULL'.
+
+ +

The function `tclistval2' is used in order to get the string of an element of a list object.

+ +
+
const char *tclistval2(const TCLIST *list, int index);
+
`list' specifies the list object.
+
`index' specifies the index of the element.
+
The return value is the string of the value.
+
If `index' is equal to or more than the number of elements, the return value is `NULL'.
+
+ +

The function `tclistpush' is used in order to add an element at the end of a list object.

+ +
+
void tclistpush(TCLIST *list, const void *ptr, int size);
+
`list' specifies the list object.
+
`ptr' specifies the pointer to the region of the new element.
+
`size' specifies the size of the region.
+
+ +

The function `tclistpush2' is used in order to add a string element at the end of a list object.

+ +
+
void tclistpush2(TCLIST *list, const char *str);
+
`list' specifies the list object.
+
`str' specifies the string of the new element.
+
+ +

The function `tclistpop' is used in order to remove an element of the end of a list object.

+ +
+
void *tclistpop(TCLIST *list, int *sp);
+
`list' specifies the list object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the removed element.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. If the list is empty, the return value is `NULL'.
+
+ +

The function `tclistpop2' is used in order to remove a string element of the end of a list object.

+ +
+
char *tclistpop2(TCLIST *list);
+
`list' specifies the list object.
+
The return value is the string of the removed element.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. If the list is empty, the return value is `NULL'.
+
+ +

The function `tclistunshift' is used in order to add an element at the top of a list object.

+ +
+
void tclistunshift(TCLIST *list, const void *ptr, int size);
+
`list' specifies the list object.
+
`ptr' specifies the pointer to the region of the new element.
+
`size' specifies the size of the region.
+
+ +

The function `tclistunshift2' is used in order to add a string element at the top of a list object.

+ +
+
void tclistunshift2(TCLIST *list, const char *str);
+
`list' specifies the list object.
+
`str' specifies the string of the new element.
+
+ +

The function `tclistshift' is used in order to remove an element of the top of a list object.

+ +
+
void *tclistshift(TCLIST *list, int *sp);
+
`list' specifies the list object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the removed element.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. If the list is empty, the return value is `NULL'.
+
+ +

The function `tclistshift2' is used in order to remove a string element of the top of a list object.

+ +
+
char *tclistshift2(TCLIST *list);
+
`list' specifies the list object.
+
The return value is the string of the removed element.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. If the list is empty, the return value is `NULL'.
+
+ +

The function `tclistinsert' is used in order to add an element at the specified location of a list object.

+ +
+
void tclistinsert(TCLIST *list, int index, const void *ptr, int size);
+
`list' specifies the list object.
+
`index' specifies the index of the new element.
+
`ptr' specifies the pointer to the region of the new element.
+
`size' specifies the size of the region.
+
If `index' is equal to or more than the number of elements, this function has no effect.
+
+ +

The function `tclistinsert2' is used in order to add a string element at the specified location of a list object.

+ +
+
void tclistinsert2(TCLIST *list, int index, const char *str);
+
`list' specifies the list object.
+
`index' specifies the index of the new element.
+
`str' specifies the string of the new element.
+
If `index' is equal to or more than the number of elements, this function has no effect.
+
+ +

The function `tclistremove' is used in order to remove an element at the specified location of a list object.

+ +
+
void *tclistremove(TCLIST *list, int index, int *sp);
+
`list' specifies the list object.
+
`index' specifies the index of the element to be removed.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the removed element.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. If `index' is equal to or more than the number of elements, no element is removed and the return value is `NULL'.
+
+ +

The function `tclistremove2' is used in order to remove a string element at the specified location of a list object.

+ +
+
char *tclistremove2(TCLIST *list, int index);
+
`list' specifies the list object.
+
`index' specifies the index of the element to be removed.
+
The return value is the string of the removed element.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. If `index' is equal to or more than the number of elements, no element is removed and the return value is `NULL'.
+
+ +

The function `tclistover' is used in order to overwrite an element at the specified location of a list object.

+ +
+
void tclistover(TCLIST *list, int index, const void *ptr, int size);
+
`list' specifies the list object.
+
`index' specifies the index of the element to be overwritten.
+
`ptr' specifies the pointer to the region of the new content.
+
`size' specifies the size of the new content.
+
If `index' is equal to or more than the number of elements, this function has no effect.
+
+ +

The function `tclistover2' is used in order to overwrite a string element at the specified location of a list object.

+ +
+
void tclistover2(TCLIST *list, int index, const char *str);
+
`list' specifies the list object.
+
`index' specifies the index of the element to be overwritten.
+
`str' specifies the string of the new content.
+
If `index' is equal to or more than the number of elements, this function has no effect.
+
+ +

The function `tclistsort' is used in order to sort elements of a list object in lexical order.

+ +
+
void tclistsort(TCLIST *list);
+
`list' specifies the list object.
+
+ +

The function `tclistlsearch' is used in order to search a list object for an element using liner search.

+ +
+
int tclistlsearch(const TCLIST *list, const void *ptr, int size);
+
`list' specifies the list object.
+
`ptr' specifies the pointer to the region of the key.
+
`size' specifies the size of the region.
+
The return value is the index of a corresponding element or -1 if there is no corresponding element.
+
If two or more elements correspond, the former returns.
+
+ +

The function `tclistbsearch' is used in order to search a list object for an element using binary search.

+ +
+
int tclistbsearch(const TCLIST *list, const void *ptr, int size);
+
`list' specifies the list object. It should be sorted in lexical order.
+
`ptr' specifies the pointer to the region of the key.
+
`size' specifies the size of the region.
+
The return value is the index of a corresponding element or -1 if there is no corresponding element.
+
If two or more elements correspond, which returns is not defined.
+
+ +

The function `tclistclear' is used in order to clear a list object.

+ +
+
void tclistclear(TCLIST *list);
+
`list' specifies the list object.
+
All elements are removed.
+
+ +

The function `tclistdump' is used in order to serialize a list object into a byte array.

+ +
+
void *tclistdump(const TCLIST *list, int *sp);
+
`list' specifies the list object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the result serial region.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tclistload' is used in order to create a list object from a serialized byte array.

+ +
+
TCLIST *tclistload(const void *ptr, int size);
+
`ptr' specifies the pointer to the region of serialized byte array.
+
`size' specifies the size of the region.
+
The return value is a new list object.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

ハッシュマップのAPI(英語御免)

+ +

The function `tcmapnew' is used in order to create a map object.

+ +
+
TCMAP *tcmapnew(void);
+
The return value is the new map object.
+
+ +

The function `tcmapnew2' is used in order to create a map object with specifying the number of the buckets.

+ +
+
TCMAP *tcmapnew2(uint32_t bnum);
+
`bnum' specifies the number of the buckets.
+
The return value is the new map object.
+
+ +

The function `tcmapnew3' is used in order to create a map object with initial string elements.

+ +
+
TCMAP *tcmapnew3(const char *str, ...);
+
`str' specifies the string of the first element.
+
The other arguments are other elements. They should be trailed by a `NULL' argument.
+
The return value is the new map object.
+
The key and the value of each record are situated one after the other.
+
+ +

The function `tcmapdup' is used in order to copy a map object.

+ +
+
TCMAP *tcmapdup(const TCMAP *map);
+
`map' specifies the map object.
+
The return value is the new map object equivalent to the specified object.
+
+ +

The function `tcmapdel' is used in order to delete a map object.

+ +
+
void tcmapdel(TCMAP *map);
+
`map' specifies the map object.
+
Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tcmapput' is used in order to store a record into a map object.

+ +
+
void tcmapput(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If a record with the same key exists in the map, it is overwritten.
+
+ +

The function `tcmapput2' is used in order to store a string record into a map object.

+ +
+
void tcmapput2(TCMAP *map, const char *kstr, const char *vstr);
+
`map' specifies the map object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If a record with the same key exists in the map, it is overwritten.
+
+ +

The function `tcmapputkeep' is used in order to store a new record into a map object.

+ +
+
bool tcmapputkeep(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the map, this function has no effect.
+
+ +

The function `tcmapputkeep2' is used in order to store a new string record into a map object.

+ +
+
bool tcmapputkeep2(TCMAP *map, const char *kstr, const char *vstr);
+
`map' specifies the map object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the map, this function has no effect.
+
+ +

The function `tcmapputcat' is used in order to concatenate a value at the end of the value of the existing record in a map object.

+ +
+
void tcmapputcat(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcmapputcat2' is used in order to concatenate a string value at the end of the value of the existing record in a map object.

+ +
+
void tcmapputcat2(TCMAP *map, const char *kstr, const char *vstr);
+
`map' specifies the map object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcmapout' is used in order to remove a record of a map object.

+ +
+
bool tcmapout(TCMAP *map, const void *kbuf, int ksiz);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcmapout2' is used in order to remove a string record of a map object.

+ +
+
bool tcmapout2(TCMAP *map, const char *kstr);
+
`map' specifies the map object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcmapget' is used in order to retrieve a record in a map object.

+ +
+
const void *tcmapget(const TCMAP *map, const void *kbuf, int ksiz, int *sp);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string.
+
+ +

The function `tcmapget2' is used in order to retrieve a string record in a map object.

+ +
+
const char *tcmapget2(const TCMAP *map, const char *kstr);
+
`map' specifies the map object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
+ +

The function `tcmapmove' is used in order to move a record to the edge of a map object.

+ +
+
bool tcmapmove(TCMAP *map, const void *kbuf, int ksiz, bool head);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of a key.
+
`ksiz' specifies the size of the region of the key.
+
`head' specifies the destination which is the head if it is true or the tail if else.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcmapmove2' is used in order to move a string record to the edge of a map object.

+ +
+
bool tcmapmove2(TCMAP *map, const char *kstr, bool head);
+
`map' specifies the map object.
+
`kstr' specifies the string of a key.
+
`head' specifies the destination which is the head if it is true or the tail if else.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcmapiterinit' is used in order to initialize the iterator of a map object.

+ +
+
void tcmapiterinit(TCMAP *map);
+
`map' specifies the map object.
+
The iterator is used in order to access the key of every record stored in the map object.
+
+ +

The function `tcmapiternext' is used in order to get the next key of the iterator of a map object.

+ +
+
const void *tcmapiternext(TCMAP *map, int *sp);
+
`map' specifies the map object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. The order of iteration is assured to be the same as the stored order.
+
+ +

The function `tcmapiternext2' is used in order to get the next key string of the iterator of a map object.

+ +
+
const char *tcmapiternext2(TCMAP *map);
+
`map' specifies the map object.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
The order of iteration is assured to be the same as the stored order.
+
+ +

The function `tcmaprnum' is used in order to get the number of records stored in a map object.

+ +
+
uint64_t tcmaprnum(const TCMAP *map);
+
`map' specifies the map object.
+
The return value is the number of the records stored in the map object.
+
+ +

The function `tcmapmsiz' is used in order to get the total size of memory used in a map object.

+ +
+
uint64_t tcmapmsiz(const TCMAP *map);
+
`map' specifies the map object.
+
The return value is the total size of memory used in a map object.
+
+ +

The function `tcmapkeys' is used in order to create a list object containing all keys in a map object.

+ +
+
TCLIST *tcmapkeys(const TCMAP *map);
+
`map' specifies the map object.
+
The return value is the new list object containing all keys in the map object.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcmapvals' is used in order to create a list object containing all values in a map object.

+ +
+
TCLIST *tcmapvals(const TCMAP *map);
+
`map' specifies the map object.
+
The return value is the new list object containing all values in the map object.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcmapaddint' is used in order to add an integer to a record in a map object.

+ +
+
int tcmapaddint(TCMAP *map, const void *kbuf, int ksiz, int num);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcmapadddouble' is used in order to add a real number to a record in a map object.

+ +
+
double tcmapadddouble(TCMAP *map, const void *kbuf, int ksiz, double num);
+
`map' specifies the map object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcmapclear' is used in order to clear a map object.

+ +
+
void tcmapclear(TCMAP *map);
+
`map' specifies the map object.
+
All records are removed.
+
+ +

The function `tcmapcutfront' is used in order to remove front records of a map object.

+ +
+
void tcmapcutfront(TCMAP *map, int num);
+
`map' specifies the map object.
+
`num' specifies the number of records to be removed.
+
+ +

The function `tcmapdump' is used in order to serialize a map object into a byte array.

+ +
+
void *tcmapdump(const TCMAP *map, int *sp);
+
`map' specifies the map object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the result serial region.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmapload' is used in order to create a map object from a serialized byte array.

+ +
+
TCMAP *tcmapload(const void *ptr, int size);
+
`ptr' specifies the pointer to the region of serialized byte array.
+
`size' specifies the size of the region.
+
The return value is a new map object.
+
Because the object of the return value is created with the function `tcmapnew', it should be deleted with the function `tcmapdel' when it is no longer in use.
+
+ +

順序木のAPI(英語御免)

+ +

The function `tctreenew' is used in order to create a tree object.

+ +
+
TCTREE *tctreenew(void);
+
The return value is the new tree object.
+
+ +

The function `tctreenew2' is used in order to create a tree object with specifying the custom comparison function.

+ +
+
TCTREE *tctreenew2(TCCMP cmp, void *cmpop);
+
`cmp' specifies the pointer to the custom comparison function. It receives five parameters. The first parameter is the pointer to the region of one key. The second parameter is the size of the region of one key. The third parameter is the pointer to the region of the other key. The fourth parameter is the size of the region of the other key. The fifth parameter is the pointer to the optional opaque object. It returns positive if the former is big, negative if the latter is big, 0 if both are equivalent.
+
`cmpop' specifies an arbitrary pointer to be given as a parameter of the comparison function. If it is not needed, `NULL' can be specified.
+
The return value is the new tree object.
+
The default comparison function compares keys of two records by lexical order. The functions `tccmplexical' (dafault), `tccmpdecimal', `tccmpint32', and `tccmpint64' are built-in.
+
+ +

The function `tctreedup' is used in order to copy a tree object.

+ +
+
TCTREE *tctreedup(const TCTREE *tree);
+
`tree' specifies the tree object.
+
The return value is the new tree object equivalent to the specified object.
+
+ +

The function `tctreedel' is used in order to delete a tree object.

+ +
+
void tctreedel(TCTREE *tree);
+
`tree' specifies the tree object.
+
Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tctreeput' is used in order to store a record into a tree object.

+ +
+
void tctreeput(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`tree' specifies the tree object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If a record with the same key exists in the tree, it is overwritten.
+
+ +

The function `tctreeput2' is used in order to store a string record into a tree object.

+ +
+
void tctreeput2(TCTREE *tree, const char *kstr, const char *vstr);
+
`tree' specifies the tree object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If a record with the same key exists in the tree, it is overwritten.
+
+ +

The function `tctreeputkeep' is used in order to store a new record into a tree object.

+ +
+
bool tctreeputkeep(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`tree' specifies the tree object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the tree, this function has no effect.
+
+ +

The function `tctreeputkeep2' is used in order to store a new string record into a tree object.

+ +
+
bool tctreeputkeep2(TCTREE *tree, const char *kstr, const char *vstr);
+
`tree' specifies the tree object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the tree, this function has no effect.
+
+ +

The function `tctreeputcat' is used in order to concatenate a value at the end of the value of the existing record in a tree object.

+ +
+
void tctreeputcat(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`tree' specifies the tree object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tctreeputcat2' is used in order to concatenate a string value at the end of the value of the existing record in a tree object.

+ +
+
void tctreeputcat2(TCTREE *tree, const char *kstr, const char *vstr);
+
`tree' specifies the tree object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tctreeout' is used in order to remove a record of a tree object.

+ +
+
bool tctreeout(TCTREE *tree, const void *kbuf, int ksiz);
+
`tree' specifies the tree object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tctreeout2' is used in order to remove a string record of a tree object.

+ +
+
bool tctreeout2(TCTREE *tree, const char *kstr);
+
`tree' specifies the tree object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tctreeget' is used in order to retrieve a record in a tree object.

+ +
+
const void *tctreeget(TCTREE *tree, const void *kbuf, int ksiz, int *sp);
+
`tree' specifies the tree object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string.
+
+ +

The function `tctreeget2' is used in order to retrieve a string record in a tree object.

+ +
+
const char *tctreeget2(TCTREE *tree, const char *kstr);
+
`tree' specifies the tree object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
+ +

The function `tctreeiterinit' is used in order to initialize the iterator of a tree object.

+ +
+
void tctreeiterinit(TCTREE *tree);
+
`tree' specifies the tree object.
+
The iterator is used in order to access the key of every record stored in the tree object.
+
+ +

The function `tctreeiternext' is used in order to get the next key of the iterator of a tree object.

+ +
+
const void *tctreeiternext(TCTREE *tree, int *sp);
+
`tree' specifies the tree object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. The order of iteration is assured to be ascending of the keys.
+
+ +

The function `tctreeiternext2' is used in order to get the next key string of the iterator of a tree object.

+ +
+
const char *tctreeiternext2(TCTREE *tree);
+
`tree' specifies the tree object.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
The order of iteration is assured to be ascending of the keys.
+
+ +

The function `tctreernum' is used in order to get the number of records stored in a tree object.

+ +
+
uint64_t tctreernum(const TCTREE *tree);
+
`tree' specifies the tree object.
+
The return value is the number of the records stored in the tree object.
+
+ +

The function `tctreemsiz' is used in order to get the total size of memory used in a tree object.

+ +
+
uint64_t tctreemsiz(const TCTREE *tree);
+
`tree' specifies the tree object.
+
The return value is the total size of memory used in a tree object.
+
+ +

The function `tctreekeys' is used in order to create a list object containing all keys in a tree object.

+ +
+
TCLIST *tctreekeys(const TCTREE *tree);
+
`tree' specifies the tree object.
+
The return value is the new list object containing all keys in the tree object.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tctreevals' is used in order to create a list object containing all values in a tree object.

+ +
+
TCLIST *tctreevals(const TCTREE *tree);
+
`tree' specifies the tree object.
+
The return value is the new list object containing all values in the tree object.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tctreeaddint' is used in order to add an integer to a record in a tree object.

+ +
+
int tctreeaddint(TCTREE *tree, const void *kbuf, int ksiz, int num);
+
`tree' specifies the tree object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tctreeadddouble' is used in order to add a real number to a record in a tree object.

+ +
+
double tctreeadddouble(TCTREE *tree, const void *kbuf, int ksiz, double num);
+
`tree' specifies the tree object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tctreeclear' is used in order to clear a tree object.

+ +
+
void tctreeclear(TCTREE *tree);
+
`tree' specifies the tree object.
+
All records are removed.
+
+ +

The function `tctreecutfringe' is used in order to remove fringe records of a tree object.

+ +
+
void tctreecutfringe(TCTREE *tree, int num);
+
`tree' specifies the tree object.
+
`num' specifies the number of records to be removed.
+
+ +

The function `tctreedump' is used in order to serialize a tree object into a byte array.

+ +
+
void *tctreedump(const TCTREE *tree, int *sp);
+
`tree' specifies the tree object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the result serial region.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tctreeload' is used in order to create a tree object from a serialized byte array.

+ +
+
TCTREE *tctreeload(const void *ptr, int size, TCCMP cmp, void *cmpop);
+
`ptr' specifies the pointer to the region of serialized byte array.
+
`size' specifies the size of the region.
+
`cmp' specifies the pointer to the custom comparison function.
+
`cmpop' specifies an arbitrary pointer to be given as a parameter of the comparison function.
+
If it is not needed, `NULL' can be specified.
+
The return value is a new tree object.
+
Because the object of the return value is created with the function `tctreenew', it should be deleted with the function `tctreedel' when it is no longer in use.
+
+ +

オンメモリハッシュデータベースのAPI(英語御免)

+ +

The function `tcmdbnew' is used in order to create an on-memory hash database object.

+ +
+
TCMDB *tcmdbnew(void);
+
The return value is the new on-memory hash database object.
+
The object can be shared by plural threads because of the internal mutex.
+
+ +

The function `tcmdbnew2' is used in order to create an on-memory hash database object with specifying the number of the buckets.

+ +
+
TCMDB *tcmdbnew2(uint32_t bnum);
+
`bnum' specifies the number of the buckets.
+
The return value is the new on-memory hash database object.
+
The object can be shared by plural threads because of the internal mutex.
+
+ +

The function `tcmdbdel' is used in order to delete an on-memory hash database object.

+ +
+
void tcmdbdel(TCMDB *mdb);
+
`mdb' specifies the on-memory hash database object.
+
+ +

The function `tcmdbput' is used in order to store a record into an on-memory hash database object.

+ +
+
void tcmdbput(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcmdbput2' is used in order to store a string record into an on-memory hash database object.

+ +
+
void tcmdbput2(TCMDB *mdb, const char *kstr, const char *vstr);
+
`mdb' specifies the on-memory hash database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcmdbputkeep' is used in order to store a new record into an on-memory hash database object.

+ +
+
bool tcmdbputkeep(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcmdbputkeep2' is used in order to store a new string record into an on-memory hash database object.

+ +
+
bool tcmdbputkeep2(TCMDB *mdb, const char *kstr, const char *vstr);
+
`mdb' specifies the on-memory hash database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcmdbputcat' is used in order to concatenate a value at the end of the existing record in an on-memory hash database.

+ +
+
void tcmdbputcat(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcmdbputcat2' is used in order to concatenate a string at the end of the existing record in an on-memory hash database.

+ +
+
void tcmdbputcat2(TCMDB *mdb, const char *kstr, const char *vstr);
+
`mdb' specifies the on-memory hash database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcmdbout' is used in order to remove a record of an on-memory hash database object.

+ +
+
bool tcmdbout(TCMDB *mdb, const void *kbuf, int ksiz);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcmdbout2' is used in order to remove a string record of an on-memory hash database object.

+ +
+
bool tcmdbout2(TCMDB *mdb, const char *kstr);
+
`mdb' specifies the on-memory hash database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcmdbget' is used in order to retrieve a record in an on-memory hash database object.

+ +
+
void *tcmdbget(TCMDB *mdb, const void *kbuf, int ksiz, int *sp);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmdbget2' is used in order to retrieve a string record in an on-memory hash database object.

+ +
+
char *tcmdbget2(TCMDB *mdb, const char *kstr);
+
`mdb' specifies the on-memory hash database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmdbvsiz' is used in order to get the size of the value of a record in an on-memory hash database object.

+ +
+
int tcmdbvsiz(TCMDB *mdb, const void *kbuf, int ksiz);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcmdbvsiz2' is used in order to get the size of the value of a string record in an on-memory hash database object.

+ +
+
int tcmdbvsiz2(TCMDB *mdb, const char *kstr);
+
`mdb' specifies the on-memory hash database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcmdbiterinit' is used in order to initialize the iterator of an on-memory hash database object.

+ +
+
void tcmdbiterinit(TCMDB *mdb);
+
`mdb' specifies the on-memory hash database object.
+
The iterator is used in order to access the key of every record stored in the on-memory hash database.
+
+ +

The function `tcmdbiternext' is used in order to get the next key of the iterator of an on-memory hash database object.

+ +
+
void *tcmdbiternext(TCMDB *mdb, int *sp);
+
`mdb' specifies the on-memory hash database object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return
+
value is assigned.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. The order of iteration is assured to be the same as the stored order.
+
+ +

The function `tcmdbiternext2' is used in order to get the next key string of the iterator of an on-memory hash database object.

+ +
+
char *tcmdbiternext2(TCMDB *mdb);
+
`mdb' specifies the on-memory hash database object.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. The order of iteration is assured to be the same as the stored order.
+
+ +

The function `tcmdbfwmkeys' is used in order to get forward matching keys in an on-memory hash database object.

+ +
+
TCLIST *tcmdbfwmkeys(TCMDB *mdb, const void *pbuf, int psiz, int max);
+
`mdb' specifies the on-memory hash database object.
+
`pbuf' specifies the pointer to the region of the prefix.
+
`psiz' specifies the size of the region of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcmdbfwmkeys2' is used in order to get forward matching string keys in an on-memory hash database object.

+ +
+
TCLIST *tcmdbfwmkeys2(TCMDB *mdb, const char *pstr, int max);
+
`mdb' specifies the on-memory hash database object.
+
`pstr' specifies the string of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcmdbrnum' is used in order to get the number of records stored in an on-memory hash database object.

+ +
+
uint64_t tcmdbrnum(TCMDB *mdb);
+
`mdb' specifies the on-memory hash database object.
+
The return value is the number of the records stored in the database.
+
+ +

The function `tcmdbmsiz' is used in order to get the total size of memory used in an on-memory hash database object.

+ +
+
uint64_t tcmdbmsiz(TCMDB *mdb);
+
`mdb' specifies the on-memory hash database object.
+
The return value is the total size of memory used in the database.
+
+ +

The function `tcmdbaddint' is used in order to add an integer to a record in an on-memory hash database object.

+ +
+
int tcmdbaddint(TCMDB *mdb, const void *kbuf, int ksiz, int num);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcmdbadddouble' is used in order to add a real number to a record in an on-memory hash database object.

+ +
+
double tcmdbadddouble(TCMDB *mdb, const void *kbuf, int ksiz, double num);
+
`mdb' specifies the on-memory hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcmdbvanish' is used in order to clear an on-memory hash database object.

+ +
+
void tcmdbvanish(TCMDB *mdb);
+
`mdb' specifies the on-memory hash database object.
+
All records are removed.
+
+ +

The function `tcmdbcutfront' is used in order to remove front records of an on-memory hash database object.

+ +
+
void tcmdbcutfront(TCMDB *mdb, int num);
+
`mdb' specifies the on-memory hash database object.
+
`num' specifies the number of records to be removed.
+
+ +

オンメモリツリーデータベースのAPI(英語御免)

+ +

The function `tcndbnew' is used in order to create an on-memory tree database object.

+ +
+
TCNDB *tcndbnew(void);
+
The return value is the new on-memory tree database object.
+
The object can be shared by plural threads because of the internal mutex.
+
+ +

The function `tcndbnew2' is used in order to create an on-memory tree database object with specifying the custom comparison function.

+ +
+
TCNDB *tcndbnew2(TCCMP cmp, void *cmpop);
+
`cmp' specifies the pointer to the custom comparison function.
+
`cmpop' specifies an arbitrary pointer to be given as a parameter of the comparison function. If it is not needed, `NULL' can be specified.
+
The return value is the new on-memory tree database object.
+
The default comparison function compares keys of two records by lexical order. The functions `tccmplexical' (dafault), `tccmpdecimal', `tccmpint32', and `tccmpint64' are built-in. The object can be shared by plural threads because of the internal mutex.
+
+ +

The function `tcndbdel' is used in order to delete an on-memory tree database object.

+ +
+
void tcndbdel(TCNDB *ndb);
+
`ndb' specifies the on-memory tree database object.
+
+ +

The function `tcndbput' is used in order to store a record into an on-memory tree database object.

+ +
+
void tcndbput(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcndbput2' is used in order to store a string record into an on-memory tree database object.

+ +
+
void tcndbput2(TCNDB *ndb, const char *kstr, const char *vstr);
+
`ndb' specifies the on-memory tree database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcndbputkeep' is used in order to store a new record into an on-memory tree database object.

+ +
+
bool tcndbputkeep(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcndbputkeep2' is used in order to store a new string record into an on-memory tree database object.

+ +
+
bool tcndbputkeep2(TCNDB *ndb, const char *kstr, const char *vstr);
+
`ndb' specifies the on-memory tree database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcndbputcat' is used in order to concatenate a value at the end of the existing record in an on-memory tree database.

+ +
+
void tcndbputcat(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcndbputcat2' is used in order to concatenate a string at the end of the existing record in an on-memory tree database.

+ +
+
void tcndbputcat2(TCNDB *ndb, const char *kstr, const char *vstr);
+
`ndb' specifies the on-memory tree database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcndbout' is used in order to remove a record of an on-memory tree database object.

+ +
+
bool tcndbout(TCNDB *ndb, const void *kbuf, int ksiz);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcndbout2' is used in order to remove a string record of an on-memory tree database object.

+ +
+
bool tcndbout2(TCNDB *ndb, const char *kstr);
+
`ndb' specifies the on-memory tree database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true. False is returned when no record corresponds to the specified key.
+
+ +

The function `tcndbget' is used in order to retrieve a record in an on-memory tree database object.

+ +
+
void *tcndbget(TCNDB *ndb, const void *kbuf, int ksiz, int *sp);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcndbget2' is used in order to retrieve a string record in an on-memory tree database object.

+ +
+
char *tcndbget2(TCNDB *ndb, const char *kstr);
+
`ndb' specifies the on-memory tree database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned when no record corresponds.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcndbvsiz' is used in order to get the size of the value of a record in an on-memory tree database object.

+ +
+
int tcndbvsiz(TCNDB *ndb, const void *kbuf, int ksiz);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcndbvsiz2' is used in order to get the size of the value of a string record in an on-memory tree database object.

+ +
+
int tcndbvsiz2(TCNDB *ndb, const char *kstr);
+
`ndb' specifies the on-memory tree database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcndbiterinit' is used in order to initialize the iterator of an on-memory tree database object.

+ +
+
void tcndbiterinit(TCNDB *ndb);
+
`ndb' specifies the on-memory tree database object.
+
The iterator is used in order to access the key of every record stored in the on-memory database.
+
+ +

The function `tcndbiternext' is used in order to get the next key of the iterator of an on-memory tree database object.

+ +
+
void *tcndbiternext(TCNDB *ndb, int *sp);
+
`ndb' specifies the on-memory tree database object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. The order of iteration is assured to be the same as the stored order.
+
+ +

The function `tcndbiternext2' is used in order to get the next key string of the iterator of an on-memory tree database object.

+ +
+
char *tcndbiternext2(TCNDB *ndb);
+
`ndb' specifies the on-memory tree database object.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record can be fetched from the iterator.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. The order of iteration is assured to be the same as the stored order.
+
+ +

The function `tcndbfwmkeys' is used in order to get forward matching keys in an on-memory tree database object.

+ +
+
TCLIST *tcndbfwmkeys(TCNDB *ndb, const void *pbuf, int psiz, int max);
+
`ndb' specifies the on-memory tree database object.
+
`pbuf' specifies the pointer to the region of the prefix.
+
`psiz' specifies the size of the region of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcndbfwmkeys2' is used in order to get forward matching string keys in an on-memory tree database object.

+ +
+
TCLIST *tcndbfwmkeys2(TCNDB *ndb, const char *pstr, int max);
+
`ndb' specifies the on-memory tree database object.
+
`pstr' specifies the string of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcndbrnum' is used in order to get the number of records stored in an on-memory tree database object.

+ +
+
uint64_t tcndbrnum(TCNDB *ndb);
+
`ndb' specifies the on-memory tree database object.
+
The return value is the number of the records stored in the database.
+
+ +

The function `tcndbmsiz' is used in order to get the total size of memory used in an on-memory tree database object.

+ +
+
uint64_t tcndbmsiz(TCNDB *ndb);
+
`ndb' specifies the on-memory tree database object.
+
The return value is the total size of memory used in the database.
+
+ +

The function `tcndbaddint' is used in order to add an integer to a record in an on-memory tree database object.

+ +
+
int tcndbaddint(TCNDB *ndb, const void *kbuf, int ksiz, int num);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcndbadddouble' is used in order to add a real number to a record in an on-memory tree database object.

+ +
+
double tcndbadddouble(TCNDB *ndb, const void *kbuf, int ksiz, double num);
+
`ndb' specifies the on-memory tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
The return value is the summation value.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcndbvanish' is used in order to clear an on-memory tree database object.

+ +
+
void tcndbvanish(TCNDB *ndb);
+
`ndb' specifies the on-memory tree database object.
+
All records are removed.
+
+ +

The function `tcndbcutfringe' is used in order to remove fringe records of an on-memory tree database object.

+ +
+
void tcndbcutfringe(TCNDB *ndb, int num);
+
`ndb' specifies the on-memory tree database object.
+
`num' specifies the number of records to be removed.
+
+ +

メモリプールのAPI(英語御免)

+ +

The function `tcmpoolnew' is used in order to create a memory pool object.

+ +
+
TCMPOOL *tcmpoolnew(void);
+
The return value is the new memory pool object.
+
+ +

The function `tcmpooldel' is used in order to delete a memory pool object.

+ +
+
void tcmpooldel(TCMPOOL *mpool);
+
`mpool' specifies the memory pool object.
+
Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tcmpoolpush' is used in order to relegate an arbitrary object to a memory pool object.

+ +
+
void *tcmpoolpush(TCMPOOL *mpool, void *ptr, void (*del)(void *));
+
`mpool' specifies the memory pool object.
+
`ptr' specifies the pointer to the object to be relegated. If it is `NULL', this function has no effect.
+
`del' specifies the pointer to the function to delete the object.
+
The return value is the pointer to the given object.
+
This function assures that the specified object is deleted when the memory pool object is deleted.
+
+ +

The function `tcmpoolpushptr' is used in order to relegate an allocated region to a memory pool object.

+ +
+
void *tcmpoolpushptr(TCMPOOL *mpool, void *ptr);
+
`mpool' specifies the memory pool object.
+
`ptr' specifies the pointer to the region to be relegated. If it is `NULL', this function has no effect.
+
The return value is the pointer to the given object.
+
This function assures that the specified region is released when the memory pool object is deleted.
+
+ +

The function `tcmpoolpushxstr' is used in order to relegate an extensible string object to a memory pool object.

+ +
+
TCXSTR *tcmpoolpushxstr(TCMPOOL *mpool, TCXSTR *xstr);
+
`mpool' specifies the memory pool object.
+
`xstr' specifies the extensible string object. If it is `NULL', this function has no effect.
+
The return value is the pointer to the given object.
+
This function assures that the specified object is deleted when the memory pool object is deleted.
+
+ +

The function `tcmpoolpushlist' is used in order to relegate a list object to a memory pool object.

+ +
+
TCLIST *tcmpoolpushlist(TCMPOOL *mpool, TCLIST *list);
+
`mpool' specifies the memory pool object.
+
`list' specifies the list object. If it is `NULL', this function has no effect.
+
The return value is the pointer to the given object.
+
This function assures that the specified object is deleted when the memory pool object is deleted.
+
+ +

The function `tcmpoolpushmap' is used in order to relegate a map object to a memory pool object.

+ +
+
TCMAP *tcmpoolpushmap(TCMPOOL *mpool, TCMAP *map);
+
`mpool' specifies the memory pool object.
+
`map' specifies the map object. If it is `NULL', this function has no effect.
+
The return value is the pointer to the given object.
+
This function assures that the specified object is deleted when the memory pool object is deleted.
+
+ +

The function `tcmpoolpushtree' is used in order to relegate a tree object to a memory pool object.

+ +
+
TCTREE *tcmpoolpushtree(TCMPOOL *mpool, TCTREE *tree);
+
`mpool' specifies the memory pool object.
+
`tree' specifies the tree object. If it is `NULL', this function has no effect.
+
The return value is the pointer to the given object.
+
This function assures that the specified object is deleted when the memory pool object is deleted.
+
+ +

The function `tcmpoolmalloc' is used in order to allocate a region relegated to a memory pool object.

+ +
+
void *tcmpoolmalloc(TCMPOOL *mpool, size_t size);
+
`mpool' specifies the memory pool object.
+
The return value is the pointer to the allocated region under the memory pool.
+
+ +

The function `tcmpoolxstrnew' is used in order to create an extensible string object relegated to a memory pool object.

+ +
+
TCXSTR *tcmpoolxstrnew(TCMPOOL *mpool);
+
The return value is the new extensible string object under the memory pool.
+
+ +

The function `tcmpoollistnew' is used in order to create a list object relegated to a memory pool object.

+ +
+
TCLIST *tcmpoollistnew(TCMPOOL *mpool);
+
The return value is the new list object under the memory pool.
+
+ +

The function `tcmpoolmapnew' is used in order to create a map object relegated to a memory pool object.

+ +
+
TCMAP *tcmpoolmapnew(TCMPOOL *mpool);
+
The return value is the new map object under the memory pool.
+
+ +

The function `tcmpooltreenew' is used in order to create a tree object relegated to a memory pool object.

+ +
+
TCTREE *tcmpooltreenew(TCMPOOL *mpool);
+
The return value is the new tree object under the memory pool.
+
+ +

The function `tcmpoolpop' is used in order to remove the most recently installed cleanup handler of a memory pool object.

+ +
+
void tcmpoolpop(TCMPOOL *mpool, bool exe);
+
`mpool' specifies the memory pool object.
+
`exe' specifies whether to execute the destructor of the removed handler.
+
+ +

The function `tcmpoolclear' is used in order to remove all cleanup handler of a memory pool object.

+ +
+
void tcmpoolclear(TCMPOOL *mpool, bool exe);
+
`mpool' specifies the memory pool object.
+
`exe' specifies whether to execute the destructors of the removed handlers.
+
+ +

The function `tcmpoolglobal' is used in order to get the global memory pool object.

+ +
+
TCMPOOL *tcmpoolglobal(void);
+
The return value is the global memory pool object.
+
The global memory pool object is a singleton and assured to be deleted when the process is terminating normally.
+
+ +

雑多なユーティリティのAPI(英語御免)

+ +

The function `tclmax' is used in order to get the larger value of two integers.

+ +
+
long tclmax(long a, long b);
+
`a' specifies an integer.
+
`b' specifies the other integer.
+
The return value is the larger value of the two.
+
+ +

The function `tclmin' is used in order to get the lesser value of two integers.

+ +
+
long tclmin(long a, long b);
+
`a' specifies an integer.
+
`b' specifies the other integer.
+
The return value is the lesser value of the two.
+
+ +

The function `tclrand' is used in order to get a random number as long integer based on uniform distribution.

+ +
+
unsigned long tclrand(void);
+
The return value is the random number between 0 and `ULONG_MAX'.
+
This function uses the random number source device and generates a real random number if possible.
+
+ +

The function `tcdrand' is used in order to get a random number as double decimal based on uniform distribution.

+ +
+
double tcdrand(void);
+
The return value is the random number equal to or greater than 0, and less than 1.0.
+
This function uses the random number source device and generates a real random number if possible.
+
+ +

The function `tcdrandnd' is used in order to get a random number as double decimal based on normal distribution.

+ +
+
double tcdrandnd(double avg, double sd);
+
`avg' specifies the average.
+
`sd' specifies the standard deviation.
+
The return value is the random number.
+
This function uses the random number source device and generates a real random number if possible.
+
+ +

The function `tcstricmp' is used in order to compare two strings with case insensitive evaluation.

+ +
+
int tcstricmp(const char *astr, const char *bstr);
+
`astr' specifies a string.
+
`bstr' specifies of the other string.
+
The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent.
+
+ +

The function `tcstrfwm' is used in order to check whether a string begins with a key.

+ +
+
bool tcstrfwm(const char *str, const char *key);
+
`str' specifies the target string.
+
`key' specifies the forward matching key string.
+
The return value is true if the target string begins with the key, else, it is false.
+
+ +

The function `tcstrifwm' is used in order to check whether a string begins with a key with case insensitive evaluation.

+ +
+
bool tcstrifwm(const char *str, const char *key);
+
`str' specifies the target string.
+
`key' specifies the forward matching key string.
+
The return value is true if the target string begins with the key, else, it is false.
+
+ +

The function `tcstrbwm' is used in order to check whether a string ends with a key.

+ +
+
bool tcstrbwm(const char *str, const char *key);
+
`str' specifies the target string.
+
`key' specifies the backward matching key string.
+
The return value is true if the target string ends with the key, else, it is false.
+
+ +

The function `tcstribwm' is used in order to check whether a string ends with a key with case insensitive evaluation.

+ +
+
bool tcstribwm(const char *str, const char *key);
+
`str' specifies the target string.
+
`key' specifies the backward matching key string.
+
The return value is true if the target string ends with the key, else, it is false.
+
+ +

The function `tcstrdist' is used in order to calculate the edit distance of two strings.

+ +
+
int tcstrdist(const char *astr, const char *bstr);
+
`astr' specifies a string.
+
`bstr' specifies of the other string.
+
The return value is the edit distance which is known as the Levenshtein distance. The cost is calculated by byte.
+
+ +

The function `tcstrdistutf' is used in order to calculate the edit distance of two UTF-8 strings.

+ +
+
int tcstrdistutf(const char *astr, const char *bstr);
+
`astr' specifies a string.
+
`bstr' specifies of the other string.
+
The return value is the edit distance which is known as the Levenshtein distance. The cost is calculated by Unicode character.
+
+ +

The function `tcstrtoupper' is used in order to convert the letters of a string into upper case.

+ +
+
char *tcstrtoupper(char *str);
+
`str' specifies the string to be converted.
+
The return value is the string itself.
+
+ +

The function `tcstrtolower' is used in order to convert the letters of a string into lower case.

+ +
+
char *tcstrtolower(char *str);
+
`str' specifies the string to be converted.
+
The return value is the string itself.
+
+ +

The function `tcstrtrim' is used in order to cut space characters at head or tail of a string.

+ +
+
char *tcstrtrim(char *str);
+
`str' specifies the string to be converted.
+
The return value is the string itself.
+
+ +

The function `tcstrsqzspc' is used in order to squeeze space characters in a string and trim it.

+ +
+
char *tcstrsqzspc(char *str);
+
`str' specifies the string to be converted.
+
The return value is the string itself.
+
+ +

The function `tcstrsubchr' is used in order to substitute characters in a string.

+ +
+
char *tcstrsubchr(char *str, const char *rstr, const char *sstr);
+
`str' specifies the string to be converted.
+
`rstr' specifies the string containing characters to be replaced.
+
`sstr' specifies the string containing characters to be substituted.
+
If the substitute string is shorter then the replacement string, corresponding characters are removed.
+
+ +

The function `tcstrcntutf' is used in order to count the number of characters in a string of UTF-8.

+ +
+
int tcstrcntutf(const char *str);
+
`str' specifies the string of UTF-8.
+
The return value is the number of characters in the string.
+
+ +

The function `tcstrcututf' is used in order to cut a string of UTF-8 at the specified number of characters.

+ +
+
char *tcstrcututf(char *str, int num);
+
`str' specifies the string of UTF-8.
+
`num' specifies the number of characters to be kept.
+
The return value is the string itself.
+
+ +

The function `tcstrutftoucs' is used in order to convert a UTF-8 string into a UCS-2 array.

+ +
+
void tcstrutftoucs(const char *str, uint16_t *ary, int *np);
+
`str' specifies the UTF-8 string.
+
`ary' specifies the pointer to the region into which the result UCS-2 codes are written. The size of the buffer should be sufficient.
+
`np' specifies the pointer to a variable into which the number of elements of the result array is assigned.
+
+ +

The function `tcstrucstoutf' is used in order to convert a UCS-2 array into a UTF-8 string.

+ +
+
int tcstrucstoutf(const uint16_t *ary, int num, char *str);
+
`ary' specifies the array of UCS-2 codes.
+
`num' specifies the number of the array.
+
`str' specifies the pointer to the region into which the result UTF-8 string is written. The size of the buffer should be sufficient.
+
The return value is the length of the result string.
+
+ +

The function `tcstrsplit' is used in order to create a list object by splitting a string.

+ +
+
TCLIST *tcstrsplit(const char *str, const char *delims);
+
`str' specifies the source string.
+
`delims' specifies a string containing delimiting characters.
+
The return value is a list object of the split elements.
+
If two delimiters are successive, it is assumed that an empty element is between the two. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcstrjoin' is used in order to create a string by joining all elements of a list object.

+ +
+
char *tcstrjoin(const TCLIST *list, char delim);
+
`list' specifies a list object.
+
`delim' specifies a delimiting character.
+
The return value is the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcatoi' is used in order to convert a string to an integer.

+ +
+
int64_t tcatoi(const char *str);
+
`str' specifies the string.
+
The return value is the integer. If the string does not contain numeric expression, 0 is returned.
+
This function is equivalent to `atoll' except that it does not depend on the locale.
+
+ +

The function `tcatoix' is used in order to convert a string with a metric prefix to an integer.

+ +
+
int64_t tcatoix(const char *str);
+
`str' specifies the string, which can be trailed by a binary metric prefix. "K", "M", "G", "T", "P", and "E" are supported. They are case-insensitive.
+
The return value is the integer. If the string does not contain numeric expression, 0 is returned. If the integer overflows the domain, `INT64_MAX' or `INT64_MIN' is returned according to the sign.
+
+ +

The function `tcatof' is used in order to convert a string to a real number.

+ +
+
double tcatof(const char *str);
+
`str' specifies the string.
+
The return value is the real number. If the string does not contain numeric expression, 0.0 is returned.
+
This function is equivalent to `atof' except that it does not depend on the locale.
+
+ +

The function `tcregexmatch' is used in order to check whether a string matches a regular expression.

+ +
+
bool tcregexmatch(const char *str, const char *regex);
+
`str' specifies the target string.
+
`regex' specifies the regular expression string. If it begins with `*', the trailing substring is used as a case-insensitive regular expression.
+
The return value is true if matching is success, else, it is false.
+
+ +

The function `tcregexreplace' is used in order to replace each substring matching a regular expression string.

+ +
+
char *tcregexreplace(const char *str, const char *regex, const char *alt);
+
`str' specifies the target string.
+
`regex' specifies the regular expression string for substrings. If it begins with `*', the trailing substring is used as a case-insensitive regular expression.
+
`alt' specifies the alternative string with which each substrings is replaced. Each `&' in the string is replaced with the matched substring. Each `\' in the string escapes the following character. Special escapes "\1" through "\9" referring to the corresponding matching sub-expressions in the regular expression string are supported.
+
The return value is a new converted string. Even if the regular expression is invalid, a copy of the original string is returned.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmd5hash' is used in order to get the MD5 hash value of a serial object.

+ +
+
void tcmd5hash(const void *ptr, int size, char *buf);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`buf' specifies the pointer to the region into which the result string is written. The size of the buffer should be equal to or more than 48 bytes.
+
+ +

The function `tcarccipher' is used in order to cipher or decipher a serial object with the Arcfour stream cipher.

+ +
+
void tcarccipher(const void *ptr, int size, const void *kbuf, int ksiz, void *obuf);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`kbuf' specifies the pointer to the region of the cipher key.
+
`ksiz' specifies the size of the region of the cipher key.
+
`obuf' specifies the pointer to the region into which the result data is written. The size of the buffer should be equal to or more than the input region.
+
+ +

The function `tctime' is used in order to get the time of day in seconds.

+ +
+
double tctime(void);
+
The return value is the time of day in seconds. The accuracy is in microseconds.
+
+ +

The function `tccalendar' is used in order to get the Gregorian calendar of a time.

+ +
+
void tccalendar(int64_t t, int jl, int *yearp, int *monp, int *dayp, int *hourp, int *minp, int *secp);
+
`t' specifies the source time in seconds from the epoch. If it is `INT64_MAX', the current time is specified.
+
`jl' specifies the jet lag of a location in seconds. If it is `INT_MAX', the local jet lag is specified.
+
`yearp' specifies the pointer to a variable to which the year is assigned. If it is `NULL', it is not used.
+
`monp' specifies the pointer to a variable to which the month is assigned. If it is `NULL', it is not used. 1 means January and 12 means December.
+
`dayp' specifies the pointer to a variable to which the day of the month is assigned. If it is `NULL', it is not used.
+
`hourp' specifies the pointer to a variable to which the hours is assigned. If it is `NULL', it is not used.
+
`minp' specifies the pointer to a variable to which the minutes is assigned. If it is `NULL', it is not used.
+
`secp' specifies the pointer to a variable to which the seconds is assigned. If it is `NULL', it is not used.
+
+ +

The function `tcdatestrwww' is used in order to format a date as a string in W3CDTF.

+ +
+
void tcdatestrwww(int64_t t, int jl, char *buf);
+
`t' specifies the source time in seconds from the epoch. If it is `INT64_MAX', the current time is specified.
+
`jl' specifies the jet lag of a location in seconds. If it is `INT_MAX', the local jet lag is specified.
+
`buf' specifies the pointer to the region into which the result string is written. The size of the buffer should be equal to or more than 48 bytes.
+
W3CDTF represents a date as "YYYY-MM-DDThh:mm:ddTZD".
+
+ +

The function `tcdatestrhttp' is used in order to format a date as a string in RFC 1123 format.

+ +
+
void tcdatestrhttp(int64_t t, int jl, char *buf);
+
`t' specifies the source time in seconds from the epoch. If it is `INT64_MAX', the current time is specified.
+
`jl' specifies the jet lag of a location in seconds. If it is `INT_MAX', the local jet lag is specified.
+
`buf' specifies the pointer to the region into which the result string is written. The size of the buffer should be equal to or more than 48 bytes.
+
RFC 1123 format represents a date as "Wdy, DD-Mon-YYYY hh:mm:dd TZD".
+
+ +

The function `tcstrmktime' is used in order to get the time value of a date string.

+ +
+
int64_t tcstrmktime(const char *str);
+
`str' specifies the date string in decimal, hexadecimal, W3CDTF, or RFC 822 (1123). Decimal can be trailed by "s" for in seconds, "m" for in minutes, "h" for in hours, and "d" for in days.
+
The return value is the time value of the date or `INT64_MIN' if the format is invalid.
+
+ +

The function `tcjetlag' is used in order to get the jet lag of the local time.

+ +
+
int tcjetlag(void);
+
The return value is the jet lag of the local time in seconds.
+
+ +

The function `tcdayofweek' is used in order to get the day of week of a date.

+ +
+
int tcdayofweek(int year, int mon, int day);
+
`year' specifies the year of a date.
+
`mon' specifies the month of the date.
+
`day' specifies the day of the date.
+
The return value is the day of week of the date. 0 means Sunday and 6 means Saturday.
+
+ +

ファイルシステム関連ユーティリティのAPI(英語御免)

+ +

The function `tcrealpath' is used in order to get the canonicalized absolute path of a file.

+ +
+
char *tcrealpath(const char *path);
+
`path' specifies the path of the file.
+
The return value is the canonicalized absolute path of a file, or `NULL' if the path is invalid.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcstatfile' is used in order to get the status information of a file.

+ +
+
bool tcstatfile(const char *path, bool *isdirp, int64_t *sizep, int64_t *mtimep);
+
`path' specifies the path of the file.
+
`isdirp' specifies the pointer to a variable into which whether the file is a directory is assigned. If it is `NULL', it is ignored.
+
`sizep' specifies the pointer to a variable into which the size of the file is assigned. If it is `NULL', it is ignored.
+
`ntimep' specifies the pointer to a variable into which the size of the file is assigned. If it is `NULL', it is ignored.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcreadfile' is used in order to read whole data of a file.

+ +
+
void *tcreadfile(const char *path, int limit, int *sp);
+
`path' specifies the path of the file. If it is `NULL', the standard input is specified.
+
`limit' specifies the limiting size of reading data. If it is not more than 0, the limitation is not specified.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned. If it is `NULL', it is not used.
+
The return value is the pointer to the allocated region of the read data, or `NULL' if the file could not be opened.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when when is no longer in use.
+
+ +

The function `tcreadfilelines' is used in order to read every line of a file.

+ +
+
TCLIST *tcreadfilelines(const char *path);
+
`path' specifies the path of the file. If it is `NULL', the standard input is specified.
+
The return value is a list object of every lines if successful, else it is `NULL'.
+
Line separators are cut out. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcwritefile' is used in order to write data into a file.

+ +
+
bool tcwritefile(const char *path, const void *ptr, int size);
+
`path' specifies the path of the file. If it is `NULL', the standard output is specified.
+
`ptr' specifies the pointer to the data region.
+
`size' specifies the size of the region.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tccopyfile' is used in order to copy a file.

+ +
+
bool tccopyfile(const char *src, const char *dest);
+
`src' specifies the path of the source file.
+
`dest' specifies the path of the destination file.
+
The return value is true if successful, else, it is false.
+
If the destination file exists, it is overwritten.
+
+ +

The function `tcreaddir' is used in order to read names of files in a directory.

+ +
+
TCLIST *tcreaddir(const char *path);
+
`path' specifies the path of the directory.
+
The return value is a list object of names if successful, else it is `NULL'.
+
Links to the directory itself and to the parent directory are ignored.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcglobpat' is used in order to expand a pattern into a list of matched paths.

+ +
+
TCLIST *tcglobpat(const char *pattern);
+
`pattern' specifies the matching pattern.
+
The return value is a list object of matched paths. If no path is matched, an empty list is returned.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcremovelink' is used in order to remove a file or a directory and its sub ones recursively.

+ +
+
bool tcremovelink(const char *path);
+
`path' specifies the path of the link.
+
If successful, the return value is true, else, it is false. False is returned when the link does not exist or the permission is denied.
+
+ +

The function `tcwrite' is used in order to write data into a file.

+ +
+
bool tcwrite(int fd, const void *buf, size_t size);
+
`fd' specifies the file descriptor.
+
`buf' specifies the buffer to be written.
+
`size' specifies the size of the buffer.
+
The return value is true if successful, else, it is false.
+
+ +

The function `tcread' is used in order to read data from a file.

+ +
+
bool tcread(int fd, void *buf, size_t size);
+
`fd' specifies the file descriptor.
+
`buf' specifies the buffer to store into.
+
`size' specifies the size of the buffer.
+
The return value is true if successful, else, it is false.
+
+ +

The function `tclock' is used in order to lock a file.

+ +
+
bool tclock(int fd, bool ex, bool nb);
+
`fd' specifies the file descriptor.
+
`ex' specifies whether an exclusive lock or a shared lock is performed.
+
`nb' specifies whether to request with non-blocking.
+
The return value is true if successful, else, it is false.
+
+ +

The function `tcunlock' is used in order to unlock a file.

+ +
+
bool tcunlock(int fd);
+
`fd' specifies the file descriptor.
+
The return value is true if successful, else, it is false.
+
+ +

The function `tcsystem' is used in order to execute a shell command.

+ +
+
int tcsystem(const char **args, int anum);
+
`args' specifies an array of the command name and its arguments.
+
`anum' specifies the number of elements of the array.
+
The return value is the exit code of the command or `INT_MAX' on failure.
+
The command name and the arguments are quoted and meta characters are escaped.
+
+ +

エンコーディング関連ユーティリティののAPI(英語御免)

+ +

The function `tcurlencode' is used in order to encode a serial object with URL encoding.

+ +
+
char *tcurlencode(const char *ptr, int size);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
The return value is the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if when is no longer in use.
+
+ +

The function `tcurldecode' is used in order to decode a string encoded with URL encoding.

+ +
+
char *tcurldecode(const char *str, int *sp);
+
`str' specifies the encoded string.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the result.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcurlbreak' is used in order to break up a URL into elements.

+ +
+
TCMAP *tcurlbreak(const char *str);
+
`str' specifies the URL string.
+
The return value is the map object whose keys are the name of elements. The key "self" indicates the URL itself. The key "scheme" indicates the scheme. The key "host" indicates the host of the server. The key "port" indicates the port number of the server. The key "authority" indicates the authority information. The key "path" indicates the path of the resource. The key "file" indicates the file name without the directory section. The key "query" indicates the query string. The key "fragment" indicates the fragment string.
+
Supported schema are HTTP, HTTPS, FTP, and FILE. Absolute URL and relative URL are supported. Because the object of the return value is created with the function `tcmapnew', it should be deleted with the function `tcmapdel' when it is no longer in use.
+
+ +

The function `tcurlresolve' is used in order to resolve a relative URL with an absolute URL.

+ +
+
char *tcurlresolve(const char *base, const char *target);
+
`base' specifies the absolute URL of the base location.
+
`target' specifies the URL to be resolved.
+
The return value is the resolved URL. If the target URL is relative, a new URL of relative location from the base location is returned. Else, a copy of the target URL is returned.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbaseencode' is used in order to encode a serial object with Base64 encoding.

+ +
+
char *tcbaseencode(const char *ptr, int size);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
The return value is the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if when is no longer in use.
+
+ +

The function `tcbasedecode' is used in order to decode a string encoded with Base64 encoding.

+ +
+
char *tcbasedecode(const char *str, int *sp);
+
`str' specifies the encoded string.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the result.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcquoteencode' is used in order to encode a serial object with Quoted-printable encoding.

+ +
+
char *tcquoteencode(const char *ptr, int size);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
The return value is the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if when is no longer in use.
+
+ +

The function `tcquotedecode' is used in order to decode a string encoded with Quoted-printable encoding.

+ +
+
char *tcquotedecode(const char *str, int *sp);
+
`str' specifies the encoded string.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the result.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmimeencode' is used in order to encode a string with MIME encoding.

+ +
+
char *tcmimeencode(const char *str, const char *encname, bool base);
+
`str' specifies the string.
+
`encname' specifies the string of the name of the character encoding.
+
`base' specifies whether to use Base64 encoding. If it is false, Quoted-printable is used.
+
The return value is the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmimedecode' is used in order to decode a string encoded with MIME encoding.

+ +
+
char *tcmimedecode(const char *str, char *enp);
+
`str' specifies the encoded string.
+
`enp' specifies the pointer to the region into which the name of encoding is written. If it is `NULL', it is not used. The size of the buffer should be equal to or more than 32 bytes.
+
The return value is the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmimebreak' is used in order to split a string of MIME into headers and the body.

+ +
+
char *tcmimebreak(const char *ptr, int size, TCMAP *headers, int *sp);
+
`ptr' specifies the pointer to the region of MIME data.
+
`size' specifies the size of the region.
+
`headers' specifies a map object to store headers. If it is `NULL', it is not used. Each key of the map is an uncapitalized header name.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the body data.
+
If the content type is defined, the header map has the key "TYPE" specifying the type. If the character encoding is defined, the key "CHARSET" indicates the encoding name. If the boundary string of multipart is defined, the key "BOUNDARY" indicates the string. If the content disposition is defined, the key "DISPOSITION" indicates the direction. If the file name is defined, the key "FILENAME" indicates the name. If the attribute name is defined, the key "NAME" indicates the name. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcmimeparts' is used in order to split multipart data of MIME into its parts.

+ +
+
TCLIST *tcmimeparts(const char *ptr, int size, const char *boundary);
+
`ptr' specifies the pointer to the region of multipart data of MIME.
+
`size' specifies the size of the region.
+
`boundary' specifies the boundary string.
+
The return value is a list object. Each element of the list is the data of a part.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tchexencode' is used in order to encode a serial object with hexadecimal encoding.

+ +
+
char *tchexencode(const char *ptr, int size);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
The return value is the result string.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if when is no longer in use.
+
+ +

The function `tchexdecode' is used in order to decode a string encoded with hexadecimal encoding.

+ +
+
char *tchexdecode(const char *str, int *sp);
+
`str' specifies the encoded string.
+
`sp' specifies the pointer to a variable into which the size of the region of the return
+
value is assigned.
+
The return value is the pointer to the region of the result.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcpackencode' is used in order to compress a serial object with Packbits encoding.

+ +
+
char *tcpackencode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcpackdecode' is used in order to decompress a serial object compressed with Packbits encoding.

+ +
+
char *tcpackdecode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbsencode' is used in order to compress a serial object with TCBS encoding.

+ +
+
char *tcbsencode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbsdecode' is used in order to decompress a serial object compressed with TCBS encoding.

+ +
+
char *tcbsdecode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcdeflate' is used in order to compress a serial object with Deflate encoding.

+ +
+
char *tcdeflate(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcinflate' is used in order to decompress a serial object compressed with Deflate encoding.

+ +
+
char *tcinflate(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcgzipencode' is used in order to compress a serial object with GZIP encoding.

+ +
+
char *tcgzipencode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcgzipdecode' is used in order to decompress a serial object compressed with GZIP encoding.

+ +
+
char *tcgzipdecode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcgetcrc' is used in order to get the CRC32 checksum of a serial object.

+ +
+
unsigned int tcgetcrc(const char *ptr, int size);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
The return value is the CRC32 checksum of the object.
+
+ +

The function `tcbzipencode' is used in order to compress a serial object with BZIP2 encoding.

+ +
+
char *tcbzipencode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbzipdecode' is used in order to decompress a serial object compressed with BZIP2 encoding.

+ +
+
char *tcbzipdecode(const char *ptr, int size, int *sp);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the result object, else, it is `NULL'.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcberencode' is used in order to encode an array of nonnegative integers with BER encoding.

+ +
+
char *tcberencode(const unsigned int *ary, int anum, int *sp);
+
`ary' specifies the pointer to the array of nonnegative integers.
+
`anum' specifies the size of the array.
+
`sp' specifies the pointer to a variable into which the size of the region of the return value is assigned.
+
The return value is the pointer to the region of the result.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if when is no longer in use.
+
+ +

The function `tcberdecode' is used in order to decode a serial object encoded with BER encoding.

+ +
+
unsigned int *tcberdecode(const char *ptr, int size, int *np);
+
`ptr' specifies the pointer to the region.
+
`size' specifies the size of the region.
+
`np' specifies the pointer to a variable into which the number of elements of the return value is assigned.
+
The return value is the pointer to the array of the result.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call if when is no longer in use.
+
+ +

The function `tcxmlescape' is used in order to escape meta characters in a string with the entity references of XML.

+ +
+
char *tcxmlescape(const char *str);
+
`str' specifies the string.
+
The return value is the pointer to the escaped string.
+
This function escapes only `&', `<', `>', and `"'. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcxmlunescape' is used in order to unescape entity references in a string of XML.

+ +
+
char *tcxmlunescape(const char *str);
+
`str' specifies the string.
+
The return value is the unescaped string.
+
This function restores only `&amp;', `&lt;', `&gt;', and `&quot;'. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

コード例

+ +

拡張可能文字列と配列リストとハッシュマップを使ったコード例を以下に示します。

+ +
#include <tcutil.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+int main(int argc, char **argv){
+
+  { /* 拡張可能文字列オブジェクトの使用例 */
+    TCXSTR *xstr;
+    /* オブジェクトを作成する */
+    xstr = tcxstrnew();
+    /* 文字列を連結する */
+    tcxstrcat2(xstr, "hop");
+    tcxstrcat2(xstr, "step");
+    tcxstrcat2(xstr, "jump");
+    /* サイズと内容を印字する */
+    printf("%d:%s\n", tcxstrsize(xstr), (char *)tcxstrptr(xstr));
+    /* オブジェクトを破棄する */
+    tcxstrdel(xstr);
+  }
+
+  { /* リストオブジェクトの使用例 */
+    TCLIST *list;
+    int i;
+    /* オブジェクトを作成する */
+    list = tclistnew();
+    /* 末尾に文字列を追加する */
+    tclistpush2(list, "hop");
+    tclistpush2(list, "step");
+    tclistpush2(list, "jump");
+    /* 全ての要素を印字する */
+    for(i = 0; i < tclistnum(list); i++){
+      printf("%d:%s\n", i, tclistval2(list, i));
+    }
+    /* オブジェクトを破棄する */
+    tclistdel(list);
+  }
+
+  { /* マップオブジェクトの使用例 */
+    TCMAP *map;
+    const char *key;
+    /* オブジェクトを作成する */
+    map = tcmapnew();
+    /* レコードを追加する */
+    tcmapput2(map, "foo", "hop");
+    tcmapput2(map, "bar", "step");
+    tcmapput2(map, "baz", "jump");
+    /* 全てのレコードを印字する */
+    tcmapiterinit(map);
+    while((key = tcmapiternext2(map)) != NULL){
+      printf("%s:%s\n", key, tcmapget2(map, key));
+    }
+    /* オブジェクトを破棄する */
+    tcmapdel(map);
+  }
+
+  { /* マップオブジェクトの使用例 */
+    TCTREE *tree;
+    const char *key;
+    /* オブジェクトを作成する */
+    tree = tctreenew();
+    /* レコードを追加する */
+    tctreeput2(tree, "foo", "hop");
+    tctreeput2(tree, "bar", "step");
+    tctreeput2(tree, "baz", "jump");
+    /* 全てのレコードを印字する */
+    tctreeiterinit(tree);
+    while((key = tctreeiternext2(tree)) != NULL){
+      printf("%s:%s\n", key, tctreeget2(tree, key));
+    }
+    /* オブジェクトを破棄する */
+    tctreedel(tree);
+  }
+
+  return 0;
+}
+
+ +

CLI

+ +

ユーティリティAPIを簡単に利用するために、コマンドラインインターフェイスとして `tcutest' と `tcumttest' と `tcucodec' が提供されます。

+ +

コマンド `tcutest' は、ユーティリティAPIの機能テストや性能テストに用いるツールです。以下の書式で用います。`rnum' は試行回数を指定し、`anum' は配列の初期容量を指定し、`bnum' はバケット数を指定します。

+ +
+
tcutest xstr rnum
+
拡張可能文字列に文字列を連結するテストを行います。
+
tcutest list [-rd] rnum [anum]
+
配列リストに要素を追加するテストを行います。
+
tcutest map [-rd] [-tr] [-rnd] [-dk|-dc|-dai|-dad|-dpr] rnum [bnum]
+
ハッシュマップにレコードを追加するテストを行います。
+
tcutest tree [-rd] [-tr] [-rnd] [-dk|-dc|-dai|-dad|-dpr] rnum
+
順序木にレコードを追加するテストを行います。
+
tcutest mdb [-rd] [-tr] [-rnd] [-dk|-dc|-dai|-dad|-dpr] rnum [bnum]
+
オンメモリハッシュデータベースにレコードを追加するテストを行います。
+
tcutest ndb [-rd] [-tr] [-rnd] [-dk|-dc|-dai|-dad|-dpr] rnum
+
オンメモリツリーデータベースにレコードを追加するテストを行います。
+
tcutest misc rnum
+
その他の雑多なテストを行います。
+
tcutest wicked rnum
+
配列リストとハッシュマップの各種更新操作を無作為に選択して実行するテストを行います。
+
+ +

各オプションは以下の機能を持ちます。

+ +
    +
  • -rd : 取得のテストも行う。
  • +
  • -tr : イテレータのテストも行う。
  • +
  • -rnd : キーを無作為に選択する。
  • +
  • -dk : 関数 `tcxxxput' の代わりに関数 `tcxxxputkeep' を用いる。
  • +
  • -dc : 関数 `tcxxxput' の代わりに関数 `tcxxxputcat' を用いる。
  • +
  • -dai : 関数 `tcxxxput' の代わりに関数 `tcxxxaddint' を用いる。
  • +
  • -dad : 関数 `tcxxxput' の代わりに関数 `tcxxxadddouble' を用いる。
  • +
  • -dpr : 関数 `tcxxxput' の代わりに関数 `tcxxxputproc' を用いる。
  • +
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +

コマンド `tcumttest' は、オンメモリハッシュデータベースAPIとオンメモリツリーデータベースAPIの機能テストをマルチスレッドで行うツールです。以下の書式で用います。`tnum' はスレッド数を指定し、`rnum' は試行回数を指定し、`bnum' はバケット数を指定します。

+ +
+
tcumttest combo [-rnd] tnum rnum [bnum]
+
レコードの格納と検索と削除を順に実行する。
+
tcumttest typical [-nc] [-rr num] tnum rnum [bnum]
+
典型的な操作を無作為に選択して実行する。
+
+ +

各オプションは以下の機能を持ちます

+ +
    +
  • -rnd : キーを無作為に選択する。
  • +
  • -nc : 比較テストを行わない。
  • +
  • -rr num : 読み込み操作の割合を百分率で指定する。
  • +
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +

コマンド `tcucodec' は、ユーティリティAPIが提供するエンコードおよびデコードの機能を利用するツールです。以下の書式で用います。`file' は入力ファイルを指定しますが、省略されれば標準入力を読み込みます。

+ +
+
tcucodec url [-d] [-br] [-rs base] [file]
+
URLエンコードとそのデコードを行う。
+
tcucodec base [-d] [file]
+
Base64エンコードとそのデコードを行う。
+
tcucodec quote [-d] [file]
+
Quoted-printableエンコードとそのデコードを行う。
+
tcucodec mime [-d] [-en name] [-q] [-on] [-hd] [-bd] [-part num] [file]
+
MIMEエンコードとそのデコードを行う。
+
tcucodec hex [-d] [file]
+
16進数エンコードとそのデコードを行う。
+
tcucodec pack [-d] [-bwt] [file]
+
Packbitsの圧縮とその伸長を行う。
+
tcucodec tcbs [-d] [file]
+
TCBSの圧縮とその伸長を行う。
+
tcucodec zlib [-d] [-gz] [file]
+
ZLIBの圧縮とその伸長を行う。
+
tcucodec bzip [-d] [file]
+
BZIP2の圧縮とその伸長を行う。
+
tcucodec xml [-d] [-br] [file]
+
XMLの処理を行う。デフォルトではメタ文字のエスケープを行う。
+
tcucodec cstr [-d] [-js] [file]
+
C文字列のエスケープとそのアンエスケープを行う。
+
tcucodec ucs [-d] [-un] [-kw str] [file]
+
UTF-8の文字列をUCS-2の配列に変換する。
+
tcucodec hash [-crc] [-ch num] [file]
+
ハッシュ値を算出する。デフォルトではMD5関数を用いる。
+
tcucodec cipher [-key str] [file]
+
ストリーム暗号化とその復号を行う。
+
tcucodec date [-ds str] [-jl num] [-wf] [-rf]
+
時刻の書式変換を行う。デフォルトでは現在のUNIX時間を出力する。
+
tcucodec tmpl [-var name val] [file]
+
テンプレートの直列化を行う。
+
tcucodec conf [-v|-i|-l|-p]
+
各種の設定情報を出力する。
+
+ +

各オプションは以下の機能を持ちます。

+ +
    +
  • -d : エンコード(エスケープ)ではなく、デコード(アンエスケープ)を行う。
  • +
  • -br : URLやXMLを構成要素に分解する。
  • +
  • -rs base : ベースURLを指定して、相対URLを解決する。
  • +
  • -en name : 入力の文字コードを指定する。デフォルトはUTF-8である。
  • +
  • -q : Quoted-printableエンコードを用いる。デフォルトはBase64である。
  • +
  • -on : デコード時に結果でなく文字コード名を出力する。
  • +
  • -bd : MIME解析を行ってボディを出力する。
  • +
  • -hd : MIME解析を行ってヘッダを出力する。
  • +
  • -part num : MIME解析を行ってマルチパートの指定されたパートを出力する。
  • +
  • -bwt : 前処理としてBWTを用いる。
  • +
  • -gz : GZIP形式を用いる。
  • +
  • -crc : CRC32関数を用いる。
  • +
  • -js : JSON互換形式を用いる。
  • +
  • -un : UCSの正規化を行う。
  • +
  • -kw str : KWIC文字列を生成する。
  • +
  • -ch num : コンシステントハッシュ関数を用いる。
  • +
  • -key str : 暗号鍵を指定する。
  • +
  • -ds str : 時刻を指定する。
  • +
  • -jl num : 時差を指定する。
  • +
  • -wf : 出力をW3CDTF形式にする。
  • +
  • -rf : 出力をRFC 1123形式にする。
  • +
  • -var name value : テンプレート変数を指定する。
  • +
  • -v : Tokyo Cabinetのバージョン番号を表示する。
  • +
  • -i : Tokyo Cabinetのヘッダのインクルードオプションを表示する。
  • +
  • -l : Tokyo Cabinetのライブラリのリンクオプションを表示する。
  • +
  • -p : Tokyo Cabinetのコマンドのあるディレクトリを表示する。
  • +
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +
+ +

ハッシュデータベースAPI

+ +

ハッシュデータベースは、ハッシュ表を単一のファイルに記録したデータベースです。それを扱うのがハッシュデータベースAPIです。`tchdb.h' にAPIの仕様の完全な記述があります。

+ +

概要

+ +

ハッシュデータベースAPIを使うためには、`tcutil.h'、`tchdb.h' および関連する標準ヘッダファイルをインクルードしてください。通常、ソースファイルの冒頭付近で以下の記述を行います。

+ +
+
#include <tcutil.h>
+
#include <tchdb.h>
+
#include <stdlib.h>
+
#include <stdbool.h>
+
#include <stdint.h>
+
+ +

ハッシュデータベースを扱う際には、`TCHDB' 型へのポインタをオブジェクトとして用います。ハッシュデータベースオブジェクトは、関数 `tchdbnew' で作成し、関数 `tchdbdel' で破棄します。作成したオブジェクトを使い終わったら必ず破棄してください。そうしないとメモリリークが発生します。

+ +

レコードの格納や探索を行う前提として、ハッシューデータベースオブジェクトをデータベースファイルと接続させる必要があります。データベースファイルを開いて接続するには関数 `tchdbopen' を用い、接続の解除してファイルを閉じるには関数 `tchdbclose' を用います。開いたデータベースファイルは必ず閉じてください。そうしないとデータベースファイルが壊れたり格納したデータが失われたりする可能性があります。単一のプロセス内で複数のデータベースオブジェクトが同じデータベースファイルを同時に開くことはできません。

+ +

API(英語ゴメン)

+ +

The function `tchdberrmsg' is used in order to get the message string corresponding to an error code.

+ +
+
const char *tchdberrmsg(int ecode);
+
`ecode' specifies the error code.
+
The return value is the message string of the error code.
+
+ +

The function `tchdbnew' is used in order to create a hash database object.

+ +
+
TCHDB *tchdbnew(void);
+
The return value is the new hash database object.
+
+ +

The function `tchdbdel' is used in order to delete a hash database object.

+ +
+
void tchdbdel(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
If the database is not closed, it is closed implicitly. Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tchdbecode' is used in order to get the last happened error code of a hash database object.

+ +
+
int tchdbecode(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
The return value is the last happened error code.
+
The following error codes are defined: `TCESUCCESS' for success, `TCETHREAD' for threading error, `TCEINVALID' for invalid operation, `TCENOFILE' for file not found, `TCENOPERM' for no permission, `TCEMETA' for invalid meta data, `TCERHEAD' for invalid record header, `TCEOPEN' for open error, `TCECLOSE' for close error, `TCETRUNC' for trunc error, `TCESYNC' for sync error, `TCESTAT' for stat error, `TCESEEK' for seek error, `TCEREAD' for read error, `TCEWRITE' for write error, `TCEMMAP' for mmap error, `TCELOCK' for lock error, `TCEUNLINK' for unlink error, `TCERENAME' for rename error, `TCEMKDIR' for mkdir error, `TCERMDIR' for rmdir error, `TCEKEEP' for existing record, `TCENOREC' for no record found, and `TCEMISC' for miscellaneous error.
+
+ +

The function `tchdbsetmutex' is used in order to set mutual exclusion control of a hash database object for threading.

+ +
+
bool tchdbsetmutex(TCHDB *hdb);
+
`hdb' specifies the hash database object which is not opened.
+
If successful, the return value is true, else, it is false.
+
Note that the mutual exclusion control is needed if the object is shared by plural threads and this function should be called before the database is opened.
+
+ +

The function `tchdbtune' is used in order to set the tuning parameters of a hash database object.

+ +
+
bool tchdbtune(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
`hdb' specifies the hash database object which is not opened.
+
`bnum' specifies the number of elements of the bucket array. If it is not more than 0, the default value is specified. The default value is 131071. Suggested size of the bucket array is about from 0.5 to 4 times of the number of all records to be stored.
+
`apow' specifies the size of record alignment by power of 2. If it is negative, the default value is specified. The default value is 4 standing for 2^4=16.
+
`fpow' specifies the maximum number of elements of the free block pool by power of 2. If it is negative, the default value is specified. The default value is 10 standing for 2^10=1024.
+
`opts' specifies options by bitwise-or: `HDBTLARGE' specifies that the size of the database can be larger than 2GB by using 64-bit bucket array, `HDBTDEFLATE' specifies that each record is compressed with Deflate encoding, `HDBTBZIP' specifies that each record is compressed with BZIP2 encoding, `HDBTTCBS' specifies that each record is compressed with TCBS encoding.
+
If successful, the return value is true, else, it is false.
+
Note that the tuning parameters should be set before the database is opened.
+
+ +

The function `tchdbsetcache' is used in order to set the caching parameters of a hash database object.

+ +
+
bool tchdbsetcache(TCHDB *hdb, int32_t rcnum);
+
`hdb' specifies the hash database object which is not opened.
+
`rcnum' specifies the maximum number of records to be cached. If it is not more than 0, the record cache is disabled. It is disabled by default.
+
If successful, the return value is true, else, it is false.
+
Note that the caching parameters should be set before the database is opened.
+
+ +

The function `tchdbsetxmsiz' is used in order to set the size of the extra mapped memory of a hash database object.

+ +
+
bool tchdbsetxmsiz(TCHDB *hdb, int64_t xmsiz);
+
`hdb' specifies the hash database object which is not opened.
+
`xmsiz' specifies the size of the extra mapped memory. If it is not more than 0, the extra mapped memory is disabled. The default size is 67108864.
+
If successful, the return value is true, else, it is false.
+
Note that the mapping parameters should be set before the database is opened.
+
+ +

The function `tchdbsetdfunit' is used in order to set the unit step number of auto defragmentation of a hash database object.

+ +
+
bool tchdbsetdfunit(TCHDB *hdb, int32_t dfunit);
+
`hdb' specifies the hash database object which is not opened.
+
`dfunit' specifie the unit step number. If it is not more than 0, the auto defragmentation is disabled. It is disabled by default.
+
If successful, the return value is true, else, it is false.
+
Note that the defragmentation parameters should be set before the database is opened.
+
+ +

The function `tchdbopen' is used in order to open a database file and connect a hash database object.

+ +
+
bool tchdbopen(TCHDB *hdb, const char *path, int omode);
+
`hdb' specifies the hash database object which is not opened.
+
`path' specifies the path of the database file.
+
`omode' specifies the connection mode: `HDBOWRITER' as a writer, `HDBOREADER' as a reader. If the mode is `HDBOWRITER', the following may be added by bitwise-or: `HDBOCREAT', which means it creates a new database if not exist, `HDBOTRUNC', which means it creates a new database regardless if one exists, `HDBOTSYNC', which means every transaction synchronizes updated contents with the device. Both of `HDBOREADER' and `HDBOWRITER' can be added to by bitwise-or: `HDBONOLCK', which means it opens the database file without file locking, or `HDBOLCKNB', which means locking is performed without blocking.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tchdbclose' is used in order to close a hash database object.

+ +
+
bool tchdbclose(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
If successful, the return value is true, else, it is false.
+
Update of a database is assured to be written when the database is closed. If a writer opens a database but does not close it appropriately, the database will be broken.
+
+ +

The function `tchdbput' is used in order to store a record into a hash database object.

+ +
+
bool tchdbput(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`hdb' specifies the hash database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tchdbput2' is used in order to store a string record into a hash database object.

+ +
+
bool tchdbput2(TCHDB *hdb, const char *kstr, const char *vstr);
+
`hdb' specifies the hash database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tchdbputkeep' is used in order to store a new record into a hash database object.

+ +
+
bool tchdbputkeep(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`hdb' specifies the hash database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tchdbputkeep2' is used in order to store a new string record into a hash database object.

+ +
+
bool tchdbputkeep2(TCHDB *hdb, const char *kstr, const char *vstr);
+
`hdb' specifies the hash database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tchdbputcat' is used in order to concatenate a value at the end of the existing record in a hash database object.

+ +
+
bool tchdbputcat(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`hdb' specifies the hash database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tchdbputcat2' is used in order to concatenate a string value at the end of the existing record in a hash database object.

+ +
+
bool tchdbputcat2(TCHDB *hdb, const char *kstr, const char *vstr);
+
`hdb' specifies the hash database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tchdbputasync' is used in order to store a record into a hash database object in asynchronous fashion.

+ +
+
bool tchdbputasync(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`hdb' specifies the hash database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten. Records passed to this function are accumulated into the inner buffer and wrote into the file at a blast.
+
+ +

The function `tchdbputasync2' is used in order to store a string record into a hash database object in asynchronous fashion.

+ +
+
bool tchdbputasync2(TCHDB *hdb, const char *kstr, const char *vstr);
+
`hdb' specifies the hash database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten. Records passed to this function are accumulated into the inner buffer and wrote into the file at a blast.
+
+ +

The function `tchdbout' is used in order to remove a record of a hash database object.

+ +
+
bool tchdbout(TCHDB *hdb, const void *kbuf, int ksiz);
+
`hdb' specifies the hash database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tchdbout2' is used in order to remove a string record of a hash database object.

+ +
+
bool tchdbout2(TCHDB *hdb, const char *kstr);
+
`hdb' specifies the hash database object connected as a writer.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tchdbget' is used in order to retrieve a record in a hash database object.

+ +
+
void *tchdbget(TCHDB *hdb, const void *kbuf, int ksiz, int *sp);
+
`hdb' specifies the hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tchdbget2' is used in order to retrieve a string record in a hash database object.

+ +
+
char *tchdbget2(TCHDB *hdb, const char *kstr);
+
`hdb' specifies the hash database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tchdbget3' is used in order to retrieve a record in a hash database object and write the value into a buffer.

+ +
+
int tchdbget3(TCHDB *hdb, const void *kbuf, int ksiz, void *vbuf, int max);
+
`hdb' specifies the hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the buffer into which the value of the corresponding record is written.
+
`max' specifies the size of the buffer.
+
If successful, the return value is the size of the written data, else, it is -1. -1 is returned if no record corresponds to the specified key.
+
Note that an additional zero code is not appended at the end of the region of the writing buffer.
+
+ +

The function `tchdbvsiz' is used in order to get the size of the value of a record in a hash database object.

+ +
+
int tchdbvsiz(TCHDB *hdb, const void *kbuf, int ksiz);
+
`hdb' specifies the hash database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tchdbvsiz2' is used in order to get the size of the value of a string record in a hash database object.

+ +
+
int tchdbvsiz2(TCHDB *hdb, const char *kstr);
+
`hdb' specifies the hash database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tchdbiterinit' is used in order to initialize the iterator of a hash database object.

+ +
+
bool tchdbiterinit(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
If successful, the return value is true, else, it is false.
+
The iterator is used in order to access the key of every record stored in a database.
+
+ +

The function `tchdbiternext' is used in order to get the next key of the iterator of a hash database object.

+ +
+
void *tchdbiternext(TCHDB *hdb, int *sp);
+
`hdb' specifies the hash database object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. It is allowed to update or remove records whose keys are fetched while the iteration. However, it is not assured if updating the database is occurred while the iteration. Besides, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access.
+
+ +

The function `tchdbiternext2' is used in order to get the next key string of the iterator of a hash database object.

+ +
+
char *tchdbiternext2(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
If successful, the return value is the string of the next key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. However, it is not assured if updating the database is occurred while the iteration. Besides, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access.
+
+ +

The function `tchdbiternext3' is used in order to get the next extensible objects of the iterator of a hash database object.

+ +
+
bool tchdbiternext3(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr);
+
`hdb' specifies the hash database object.
+
`kxstr' specifies the object into which the next key is wrote down.
+
`vxstr' specifies the object into which the next value is wrote down.
+
If successful, the return value is true, else, it is false. False is returned when no record is to be get out of the iterator.
+
+ +

The function `tchdbfwmkeys' is used in order to get forward matching keys in a hash database object.

+ +
+
TCLIST *tchdbfwmkeys(TCHDB *hdb, const void *pbuf, int psiz, int max);
+
`hdb' specifies the hash database object.
+
`pbuf' specifies the pointer to the region of the prefix.
+
`psiz' specifies the size of the region of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tchdbfwmkeys2' is used in order to get forward matching string keys in a hash database object.

+ +
+
TCLIST *tchdbfwmkeys2(TCHDB *hdb, const char *pstr, int max);
+
`hdb' specifies the hash database object.
+
`pstr' specifies the string of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tchdbaddint' is used in order to add an integer to a record in a hash database object.

+ +
+
int tchdbaddint(TCHDB *hdb, const void *kbuf, int ksiz, int num);
+
`hdb' specifies the hash database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is `INT_MIN'.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tchdbdbadddouble' is used in order to add a real number to a record in a hash database object.

+ +
+
double tchdbadddouble(TCHDB *hdb, const void *kbuf, int ksiz, double num);
+
`hdb' specifies the hash database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is Not-a-Number.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tchdbsync' is used in order to synchronize updated contents of a hash database object with the file and the device.

+ +
+
bool tchdbsync(TCHDB *hdb);
+
`hdb' specifies the hash database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
This function is useful when another process connects to the same database file.
+
+ +

The function `tchdboptimize' is used in order to optimize the file of a hash database object.

+ +
+
bool tchdboptimize(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
`hdb' specifies the hash database object connected as a writer.
+
`bnum' specifies the number of elements of the bucket array. If it is not more than 0, the default value is specified. The default value is two times of the number of records.
+
`apow' specifies the size of record alignment by power of 2. If it is negative, the current setting is not changed.
+
`fpow' specifies the maximum number of elements of the free block pool by power of 2. If it is negative, the current setting is not changed.
+
`opts' specifies options by bitwise-or: `HDBTLARGE' specifies that the size of the database can be larger than 2GB by using 64-bit bucket array, `HDBTDEFLATE' specifies that each record is compressed with Deflate encoding, `HDBTTCBS' specifies that each record is compressed with TCBS encoding. If it is `UINT8_MAX', the current setting is not changed.
+
If successful, the return value is true, else, it is false.
+
This function is useful to reduce the size of the database file with data fragmentation by successive updating.
+
+ +

The function `tchdbvanish' is used in order to remove all records of a hash database object.

+ +
+
bool tchdbvanish(TCHDB *hdb);
+
`hdb' specifies the hash database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tchdbcopy' is used in order to copy the database file of a hash database object.

+ +
+
bool tchdbcopy(TCHDB *hdb, const char *path);
+
`hdb' specifies the hash database object.
+
`path' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line.
+
If successful, the return value is true, else, it is false. False is returned if the executed command returns non-zero code.
+
The database file is assured to be kept synchronized and not modified while the copying or executing operation is in progress. So, this function is useful to create a backup file of the database file.
+
+ +

The function `tchdbtranbegin' is used in order to begin the transaction of a hash database object.

+ +
+
bool tchdbtranbegin(TCHDB *hdb);
+
`hdb' specifies the hash database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
The database is locked by the thread while the transaction so that only one transaction can be activated with a database object at the same time. Thus, the serializable isolation level is assumed if every database operation is performed in the transaction. All updated regions are kept track of by write ahead logging while the transaction. If the database is closed during transaction, the transaction is aborted implicitly.
+
+ +

The function `tchdbtrancommit' is used in order to commit the transaction of a hash database object.

+ +
+
bool tchdbtrancommit(TCHDB *hdb);
+
`hdb' specifies the hash database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is fixed when it is committed successfully.
+
+ +

The function `tchdbtranabort' is used in order to abort the transaction of a hash database object.

+ +
+
bool tchdbtranabort(TCHDB *hdb);
+
`hdb' specifies the hash database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is discarded when it is aborted. The state of the database is rollbacked to before transaction.
+
+ +

The function `tchdbpath' is used in order to get the file path of a hash database object.

+ +
+
const char *tchdbpath(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
The return value is the path of the database file or `NULL' if the object does not connect to any database file.
+
+ +

The function `tchdbrnum' is used in order to get the number of records of a hash database object.

+ +
+
uint64_t tchdbrnum(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
The return value is the number of records or 0 if the object does not connect to any database file.
+
+ +

The function `tchdbfsiz' is used in order to get the size of the database file of a hash database object.

+ +
+
uint64_t tchdbfsiz(TCHDB *hdb);
+
`hdb' specifies the hash database object.
+
The return value is the size of the database file or 0 if the object does not connect to any database file.
+
+ +

コード例

+ +

ハッシュデータベースを使ったコード例を以下に示します。

+ +
#include <tcutil.h>
+#include <tchdb.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+int main(int argc, char **argv){
+  TCHDB *hdb;
+  int ecode;
+  char *key, *value;
+
+  /* オブジェクトを作成する */
+  hdb = tchdbnew();
+
+  /* データベースを開く */
+  if(!tchdbopen(hdb, "casket.tch", HDBOWRITER | HDBOCREAT)){
+    ecode = tchdbecode(hdb);
+    fprintf(stderr, "open error: %s\n", tchdberrmsg(ecode));
+  }
+
+  /* レコードを格納する */
+  if(!tchdbput2(hdb, "foo", "hop") ||
+     !tchdbput2(hdb, "bar", "step") ||
+     !tchdbput2(hdb, "baz", "jump")){
+    ecode = tchdbecode(hdb);
+    fprintf(stderr, "put error: %s\n", tchdberrmsg(ecode));
+  }
+
+  /* レコードを取得する */
+  value = tchdbget2(hdb, "foo");
+  if(value){
+    printf("%s\n", value);
+    free(value);
+  } else {
+    ecode = tchdbecode(hdb);
+    fprintf(stderr, "get error: %s\n", tchdberrmsg(ecode));
+  }
+
+  /* 横断的にレコードを参照する */
+  tchdbiterinit(hdb);
+  while((key = tchdbiternext2(hdb)) != NULL){
+    value = tchdbget2(hdb, key);
+    if(value){
+      printf("%s:%s\n", key, value);
+      free(value);
+    }
+    free(key);
+  }
+
+  /* データベースを閉じる */
+  if(!tchdbclose(hdb)){
+    ecode = tchdbecode(hdb);
+    fprintf(stderr, "close error: %s\n", tchdberrmsg(ecode));
+  }
+
+  /* オブジェクトを破棄する */
+  tchdbdel(hdb);
+
+  return 0;
+}
+
+ +

CLI

+ +

ハッシュデータベースAPIを簡単に利用するために、コマンドラインインターフェイスとして `tchtest' と `tchmttest' と `tchmgr' が提供されます。

+ +

コマンド `tchtest' は、ハッシュデータベースAPIの機能テストや性能テストに用いるツールです。以下の書式で用います。`path' はデータベースファイルのパスを指定し、`rnum' は試行回数を指定し、`bnum' はバケット数を指定し、`apow' はアラインメント力を指定し、`fpow' はフリーブロックプール力を指定します。

+ +
+
tchtest write [-mt] [-tl] [-td|-tb|-tt|-tx] [-rc num] [-xm num] [-df num] [-nl|-nb] [-as] [-rnd] path rnum [bnum [apow [fpow]]]
+
`00000001'、`00000002' のように変化する8バイトのキーと値を連続してデータベースに追加する。
+
tchtest read [-mt] [-rc num] [-xm num] [-df num] [-nl|-nb] [-wb] [-rnd] path
+
上記で生成したデータベースの全レコードを検索する。
+
tchtest remove [-mt] [-rc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path
+
上記で生成したデータベースの全レコードを削除する。
+
tchtest rcat [-mt] [-tl] [-td|-tb|-tt|-tx] [-rc num] [-xm num] [-df num] [-nl|-nb] [-pn num] [-dai|-dad|-rl|-ru] path rnum [bnum [apow [fpow]]]
+
キーがある程度重複するようにレコードの追加を行い、連結モードで処理する。
+
tchtest misc [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum
+
各種操作の組み合わせテストを行う。
+
tchtest wicked [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum
+
各種更新操作を無作為に選択して実行する。
+
+ +

各オプションは以下の機能を持ちます

+ +
    +
  • -mt : 関数 `tchdbsetmutex' を呼び出す。
  • +
  • -tl : オプション `HDBTLARGE' を有効にする。
  • +
  • -td : オプション `HDBTDEFLATE' を有効にする。
  • +
  • -tb : オプション `HDBTBZIP' を有効にする。
  • +
  • -tt : オプション `HDBTTCBS' を有効にする。
  • +
  • -tx : オプション `HDBTEXCODEC' を有効にする。
  • +
  • -rc num : レコード用キャッシュの最大数を指定する。
  • +
  • -xm num : 拡張マップメモリのサイズを指定する。
  • +
  • -df num : 自動デフラグの単位ステップ数を指定する。
  • +
  • -nl : オプション `HDBNOLCK' を有効にする。
  • +
  • -nb : オプション `HDBLCKNB' を有効にする。
  • +
  • -as : 関数 `tchdbput' の代わりに関数 `tchdbputasync' を用いる。
  • +
  • -rnd : キーを無作為に選択する。
  • +
  • -wb : 関数 `tchdbget' の代わりに関数 `tchdbget3' を用いる。
  • +
  • -pn num : パターン数を指定する。
  • +
  • -dai : 関数 `tchdbputcat' の代わりに関数 `tchdbaddint' を用いる。
  • +
  • -dad : 関数 `tchdbputcat' の代わりに関数 `tchdbadddouble' を用いる。
  • +
  • -rl : 値を無作為な長さにする。
  • +
  • -ru : 更新操作を無作為に選択する。
  • +
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +

コマンド `tchmttest' は、ハッシュデータベースAPIの機能テストをマルチスレッドで行うツールです。以下の書式で用います。`path' はデータベースファイルのパスを指定し、`tnum' はスレッド数を指定し、`rnum' は試行回数を指定し、`bnum' はバケット数を指定し、`apow' はアラインメント力を指定し、`fpow' はフリーブロックプール力を指定します。

+ +
+
tchmttest write [-tl] [-td|-tb|-tt|-tx] [-rc num] [-xm num] [-df num] [-nl|-nb] [-as] [-rnd] path tnum rnum [bnum [apow [fpow]]]
+
`00000001'、`00000002' のように変化する8バイトのキーと値を連続してデータベースに追加する。
+
tchmttest read [-rc num] [-xm num] [-df num] [-nl|-nb] [-wb] [-rnd] path tnum
+
上記で生成したデータベースの全レコードを検索する。
+
tchmttest remove [-rc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path tnum
+
上記で生成したデータベースの全レコードを削除する。
+
tchmttest wicked [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] [-nc] path tnum rnum
+
各種更新操作を無作為に選択して実行する。
+
tchmttest typical [-tl] [-td|-tb|-tt|-tx] [-rc num] [-xm num] [-df num] [-nl|-nb] [-nc] [-rr num] path tnum rnum [bnum [apow [fpow]]
+
典型的な操作を無作為に選択して実行する。
+
tchmttest race [-tl] [-td|-tb|-tt|-tx] [-xm num] [-df num] [-nl|-nb] path tnum rnum [bnum [apow [fpow]]
+
レースコンディション検出のテストを行う。
+
+ +

各オプションは以下の機能を持ちます

+ +
    +
  • -tl : オプション `HDBTLARGE' を有効にする。
  • +
  • -td : オプション `HDBTDEFLATE' を有効にする。
  • +
  • -tb : オプション `HDBTBZIP' を有効にする。
  • +
  • -tt : オプション `HDBTTCBS' を有効にする。
  • +
  • -tx : オプション `HDBTEXCODEC' を有効にする。
  • +
  • -rc num : レコード用キャッシュの最大数を指定する。
  • +
  • -xm num : 拡張マップメモリのサイズを指定する。
  • +
  • -df num : 自動デフラグの単位ステップ数を指定する。
  • +
  • -nl : オプション `HDBNOLCK' を有効にする。
  • +
  • -nb : オプション `HDBLCKNB' を有効にする。
  • +
  • -as : 関数 `tchdbput' の代わりに関数 `tchdbputasync' を用いる。
  • +
  • -rnd : キーを無作為に選択する。
  • +
  • -wb : 関数 `tchdbget' の代わりに関数 `tchdbget3' を用いる。
  • +
  • -nc : 比較テストを行わない。
  • +
  • -rr num : 読み込み操作の割合を百分率で指定する。
  • +
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +

コマンド `tchmgr' は、ハッシュデータベースAPIやそのアプリケーションのテストやデバッグに役立つツールです。以下の書式で用います。`path' はデータベースファイルのパスを指定し、`bnum' はバケット数を指定し、`apow' はアラインメント力を指定し、`fpow' はフリーブロックプール力を指定し、`key' はレコードのキーを指定し、`value' はレコードの値を指定し、`file' は入力ファイルを指定します。

+ +
+
tchmgr create [-tl] [-td|-tb|-tt|-tx] path [bnum [apow [fpow]]]
+
データベースファイルを作成する。
+
tchmgr inform [-nl|-nb] path
+
データベースの雑多な情報を出力する。
+
tchmgr put [-nl|-nb] [-sx] [-dk|-dc|-dai|-dad] path key value
+
レコードを追加する。
+
tchmgr out [-nl|-nb] [-sx] path key
+
レコードを削除する。
+
tchmgr get [-nl|-nb] [-sx] [-px] [-pz] path key
+
レコードの値を取得して標準出力する。
+
tchmgr list [-nl|-nb] [-m num] [-pv] [-px] [-fm str] path
+
全てのレコードのキーを改行で区切って標準出力する。
+
tchmgr optimize [-tl] [-td|-tb|-tt|-tx] [-tz] [-nl|-nb] [-df] path [bnum [apow [fpow]]]
+
データベースを最適化する。
+
tchmgr importtsv [-nl|-nb] [-sc] path [file]
+
TSVファイルの各行をキーと値とみなしてレコードを登録する。
+
tchmgr version
+
Tokyo Cabinetのバージョン情報を標準出力する。
+
+ +

各オプションは以下の機能を持ちます

+ +
    +
  • -tl : オプション `HDBTLARGE' を有効にする。
  • +
  • -td : オプション `HDBTDEFLATE' を有効にする。
  • +
  • -tb : オプション `HDBTBZIP' を有効にする。
  • +
  • -tt : オプション `HDBTTCBS' を有効にする。
  • +
  • -tx : オプション `HDBTEXCODEC' を有効にする。
  • +
  • -nl : オプション `HDBNOLCK' を有効にする。
  • +
  • -nb : オプション `HDBLCKNB' を有効にする。
  • +
  • -sx : 入力を16進数の文字列で行う。
  • +
  • -dk : 関数 `tchdbput' の代わりに関数 `tchdbputkeep' を用いる。
  • +
  • -dc : 関数 `tchdbput' の代わりに関数 `tchdbputcat' を用いる。
  • +
  • -dai : 関数 `tchdbput' の代わりに関数 `tchdbaddint' を用いる。
  • +
  • -dad : 関数 `tchdbput' の代わりに関数 `tchdbadddouble' を用いる。
  • +
  • -px : 出力を16進数の文字列で行う。
  • +
  • -pz : 出力の末尾に改行を付加しない。
  • +
  • -m num : 出力の最大数を指定する。
  • +
  • -pv : レコードの値も出力する。
  • +
  • -fm str : キーの接頭辞を指定する。
  • +
  • -tz : オプション `UINT8_MAX' を有効にする。
  • +
  • -df : デフラグのみを行う。
  • +
  • -sc : キーを小文字に正規化する。
  • +
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +
+ +

B+木データベースAPI

+ +

B+木データベースは、B+木を単一のファイルに記録したデータベースです。それを扱うのがB+木データベースAPIです。`tcbdb.h' にAPIの仕様の完全な記述があります。

+ +

概要

+ +

ハッシュデータベースAPIを使うためには、`tcutil.h'、`tcbdb.h' および関連する標準ヘッダファイルをインクルードしてください。通常、ソースファイルの冒頭付近で以下の記述を行います。

+ +
+
#include <tcutil.h>
+
#include <tcbdb.h>
+
#include <stdlib.h>
+
#include <stdbool.h>
+
#include <stdint.h>
+
+ +

B+木データベースを扱う際には、`TCBDB' 型へのポインタをオブジェクトとして用います。B+木データベースオブジェクトは、関数 `tcbdbnew' で作成し、関数 `tcbdbdel' で破棄します。作成したオブジェクトを使い終わったら必ず破棄してください。そうしないとメモリリークが発生します。

+ +

レコードの格納や探索を行う前提として、B+木ーデータベースオブジェクトをデータベースファイルと接続させる必要があります。データベースファイルを開いて接続するには関数 `tcbdbopen' を用い、接続の解除してファイルを閉じるには関数 `tcbdbclose' を用います。開いたデータベースファイルは必ず閉じてください。そうしないとデータベースファイルが壊れたり格納したデータが失われたりする可能性があります。単一のプロセス内で複数のデータベースオブジェクトが同じデータベースファイルを同時に開くことはできません。

+ +

API(英語ゴメソ)

+ +

The function `tcbdberrmsg' is used in order to get the message string corresponding to an error code.

+ +
+
const char *tcbdberrmsg(int ecode);
+
`ecode' specifies the error code.
+
The return value is the message string of the error code.
+
+ +

The function `tcbdbnew' is used in order to create a B+ tree database object.

+ +
+
TCBDB *tcbdbnew(void);
+
The return value is the new B+ tree database object.
+
+ +

The function `tcbdbdel' is used in order to delete a B+ tree database object.

+ +
+
void tcbdbdel(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object.
+
If the database is not closed, it is closed implicitly. Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tcbdbecode' is used in order to get the last happened error code of a B+ tree database object.

+ +
+
int tcbdbecode(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object.
+
The return value is the last happened error code.
+
The following error codes are defined: `TCESUCCESS' for success, `TCETHREAD' for threading error, `TCEINVALID' for invalid operation, `TCENOFILE' for file not found, `TCENOPERM' for no permission, `TCEMETA' for invalid meta data, `TCERHEAD' for invalid record header, `TCEOPEN' for open error, `TCECLOSE' for close error, `TCETRUNC' for trunc error, `TCESYNC' for sync error, `TCESTAT' for stat error, `TCESEEK' for seek error, `TCEREAD' for read error, `TCEWRITE' for write error, `TCEMMAP' for mmap error, `TCELOCK' for lock error, `TCEUNLINK' for unlink error, `TCERENAME' for rename error, `TCEMKDIR' for mkdir error, `TCERMDIR' for rmdir error, `TCEKEEP' for existing record, `TCENOREC' for no record found, and `TCEMISC' for miscellaneous error.
+
+ +

The function `tcbdbsetmutex' is used in order to set mutual exclusion control of a B+ tree database object for threading.

+ +
+
bool tcbdbsetmutex(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object which is not opened.
+
If successful, the return value is true, else, it is false.
+
Note that the mutual exclusion control is needed if the object is shared by plural threads and this function should be called before the database is opened.
+
+ +

The function `tcbdbsetcmpfunc' is used in order to set the custom comparison function of a B+ tree database object.

+ +
+
bool tcbdbsetcmpfunc(TCBDB *bdb, TCCMP cmp, void *cmpop);
+
`bdb' specifies the B+ tree database object which is not opened.
+
`cmp' specifies the pointer to the custom comparison function. It receives five parameters. The first parameter is the pointer to the region of one key. The second parameter is the size of the region of one key. The third parameter is the pointer to the region of the other key. The fourth parameter is the size of the region of the other key. The fifth parameter is the pointer to the optional opaque object. It returns positive if the former is big, negative if the latter is big, 0 if both are equivalent.
+
`cmpop' specifies an arbitrary pointer to be given as a parameter of the comparison function. If it is not needed, `NULL' can be specified.
+
If successful, the return value is true, else, it is false.
+
The default comparison function compares keys of two records by lexical order. The functions `tccmplexical' (dafault), `tccmpdecimal', `tccmpint32', and `tccmpint64' are built-in. Note that the comparison function should be set before the database is opened. Moreover, user-defined comparison functions should be set every time the database is being opened.
+
+ +

The function `tcbdbtune' is used in order to set the tuning parameters of a B+ tree database object.

+ +
+
bool tcbdbtune(TCBDB *bdb, int32_t lmemb, int32_t nmemb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
`bdb' specifies the B+ tree database object which is not opened.
+
`lmemb' specifies the number of members in each leaf page. If it is not more than 0, the default value is specified. The default value is 128.
+
`nmemb' specifies the number of members in each non-leaf page. If it is not more than 0, the default value is specified. The default value is 256.
+
`bnum' specifies the number of elements of the bucket array. If it is not more than 0, the default value is specified. The default value is 32749. Suggested size of the bucket array is about from 1 to 4 times of the number of all pages to be stored.
+
`apow' specifies the size of record alignment by power of 2. If it is negative, the default value is specified. The default value is 8 standing for 2^8=256.
+
`fpow' specifies the maximum number of elements of the free block pool by power of 2. If it is negative, the default value is specified. The default value is 10 standing for 2^10=1024.
+
`opts' specifies options by bitwise-or: `BDBTLARGE' specifies that the size of the database can be larger than 2GB by using 64-bit bucket array, `BDBTDEFLATE' specifies that each page is compressed with Deflate encoding, `BDBTBZIP' specifies that each page is compressed with BZIP2 encoding, `BDBTTCBS' specifies that each page is compressed with TCBS encoding.
+
If successful, the return value is true, else, it is false.
+
Note that the tuning parameters should be set before the database is opened.
+
+ +

The function `tcbdbsetcache' is used in order to set the caching parameters of a B+ tree database object.

+ +
+
bool tcbdbsetcache(TCBDB *bdb, int32_t lcnum, int32_t ncnum);
+
`bdb' specifies the B+ tree database object which is not opened.
+
`lcnum' specifies the maximum number of leaf nodes to be cached. If it is not more than 0, the default value is specified. The default value is 4096.
+
`ncnum' specifies the maximum number of non-leaf nodes to be cached. If it is not more than 0, the default value is specified. The default value is 512.
+
If successful, the return value is true, else, it is false.
+
Note that the caching parameters should be set before the database is opened.
+
+ +

The function `tcbdbsetxmsiz' is used in order to set the size of the extra mapped memory of a B+ tree database object.

+ +
+
bool tcbdbsetxmsiz(TCBDB *bdb, int64_t xmsiz);
+
`bdb' specifies the B+ tree database object which is not opened.
+
`xmsiz' specifies the size of the extra mapped memory. If it is not more than 0, the extra mapped memory is disabled. It is disabled by default.
+
If successful, the return value is true, else, it is false.
+
Note that the mapping parameters should be set before the database is opened.
+
+ +

The function `tcbdbsetdfunit' is used in order to set the unit step number of auto defragmentation of a B+ tree database object.

+ +
+
bool tcbdbsetdfunit(TCBDB *bdb, int32_t dfunit);
+
`bdb' specifies the B+ tree database object which is not opened.
+
`dfunit' specifie the unit step number. If it is not more than 0, the auto defragmentation is disabled. It is disabled by default.
+
If successful, the return value is true, else, it is false.
+
Note that the defragmentation parameter should be set before the database is opened.
+
+ +

The function `tcbdbopen' is used in order to open a database file and connect a B+ tree database object.

+ +
+
bool tcbdbopen(TCBDB *bdb, const char *path, int omode);
+
`bdb' specifies the B+ tree database object which is not opened.
+
`path' specifies the path of the database file.
+
`omode' specifies the connection mode: `BDBOWRITER' as a writer, `BDBOREADER' as a reader. If the mode is `BDBOWRITER', the following may be added by bitwise-or: `BDBOCREAT', which means it creates a new database if not exist, `BDBOTRUNC', which means it creates a new database regardless if one exists, `BDBOTSYNC', which means every transaction synchronizes updated contents with the device. Both of `BDBOREADER' and `BDBOWRITER' can be added to by bitwise-or: `BDBONOLCK', which means it opens the database file without file locking, or `BDBOLCKNB', which means locking is performed without blocking.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcbdbclose' is used in order to close a B+ tree database object.

+ +
+
bool tcbdbclose(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object.
+
If successful, the return value is true, else, it is false.
+
Update of a database is assured to be written when the database is closed. If a writer opens a database but does not close it appropriately, the database will be broken.
+
+ +

The function `tcbdbput' is used in order to store a record into a B+ tree database object.

+ +
+
bool tcbdbput(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcbdbput2' is used in order to store a string record into a B+ tree database object.

+ +
+
bool tcbdbput2(TCBDB *bdb, const char *kstr, const char *vstr);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcbdbputkeep' is used in order to store a new record into a B+ tree database object.

+ +
+
bool tcbdbputkeep(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcbdbputkeep2' is used in order to store a new string record into a B+ tree database object.

+ +
+
bool tcbdbputkeep2(TCBDB *bdb, const char *kstr, const char *vstr);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcbdbputcat' is used in order to concatenate a value at the end of the existing record in a B+ tree database object.

+ +
+
bool tcbdbputcat(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcbdbputcat2' is used in order to concatenate a stirng value at the end of the existing record in a B+ tree database object.

+ +
+
bool tcbdbputcat2(TCBDB *bdb, const char *kstr, const char *vstr);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcbdbputdup' is used in order to store a record into a B+ tree database object with allowing duplication of keys.

+ +
+
bool tcbdbputdup(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, the new record is placed after the existing one.
+
+ +

The function `tcbdbputdup2' is used in order to store a string record into a B+ tree database object with allowing duplication of keys.

+ +
+
bool tcbdbputdup2(TCBDB *bdb, const char *kstr, const char *vstr);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, the new record is placed after the existing one.
+
+ +

The function `tcbdbputdup3' is used in order to store records into a B+ tree database object with allowing duplication of keys.

+ +
+
bool tcbdbputdup3(TCBDB *bdb, const void *kbuf, int ksiz, const TCLIST *vals);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the common key.
+
`ksiz' specifies the size of the region of the common key.
+
`vals' specifies a list object containing values.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, the new records are placed after the existing one.
+
+ +

The function `tcbdbout' is used in order to remove a record of a B+ tree database object.

+ +
+
bool tcbdbout(TCBDB *bdb, const void *kbuf, int ksiz);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true, else, it is false.
+
If the key of duplicated records is specified, the first one is selected.
+
+ +

The function `tcbdbout2' is used in order to remove a string record of a B+ tree database object.

+ +
+
bool tcbdbout2(TCBDB *bdb, const char *kstr);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true, else, it is false.
+
If the key of duplicated records is specified, the first one is selected.
+
+ +

The function `tcbdbout3' is used in order to remove records of a B+ tree database object.

+ +
+
bool tcbdbout3(TCBDB *bdb, const void *kbuf, int ksiz);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true, else, it is false.
+
If the key of duplicated records is specified, all of them are removed.
+
+ +

The function `tcbdbget' is used in order to retrieve a record in a B+ tree database object.

+ +
+
void *tcbdbget(TCBDB *bdb, const void *kbuf, int ksiz, int *sp);
+
`bdb' specifies the B+ tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
If the key of duplicated records is specified, the first one is selected. Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbdbget2' is used in order to retrieve a string record in a B+ tree database object.

+ +
+
char *tcbdbget2(TCBDB *bdb, const char *kstr);
+
`bdb' specifies the B+ tree database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
If the key of duplicated records is specified, the first one is selected. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbdbget3' is used in order to retrieve a record in a B+ tree database object as a volatile buffer.

+ +
+
const void *tcbdbget3(TCBDB *bdb, const void *kbuf, int ksiz, int *sp);
+
`bdb' specifies the B+ tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
If the key of duplicated records is specified, the first one is selected. Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is volatile and it may be spoiled by another operation of the database, the data should be copied into another involatile buffer immediately.
+
+ +

The function `tcbdbget4' is used in order to retrieve records in a B+ tree database object.

+ +
+
TCLIST *tcbdbget4(TCBDB *bdb, const void *kbuf, int ksiz);
+
`bdb' specifies the B+ tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is a list object of the values of the corresponding records. `NULL' is returned if no record corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcbdbvnum' is used in order to get the number of records corresponding a key in a B+ tree database object.

+ +
+
int tcbdbvnum(TCBDB *bdb, const void *kbuf, int ksiz);
+
`bdb' specifies the B+ tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is the number of the corresponding records, else, it is 0.
+
+ +

The function `tcbdbvnum2' is used in order to get the number of records corresponding a string key in a B+ tree database object.

+ +
+
int tcbdbvnum2(TCBDB *bdb, const char *kstr);
+
`bdb' specifies the B+ tree database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the number of the corresponding records, else, it is 0.
+
+ +

The function `tcbdbvsiz' is used in order to get the size of the value of a record in a B+ tree database object.

+ +
+
int tcbdbvsiz(TCBDB *bdb, const void *kbuf, int ksiz);
+
`bdb' specifies the B+ tree database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
If the key of duplicated records is specified, the first one is selected.
+
+ +

The function `tcbdbvsiz2' is used in order to get the size of the value of a string record in a B+ tree database object.

+ +
+
int tcbdbvsiz2(TCBDB *bdb, const char *kstr);
+
`bdb' specifies the B+ tree database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
If the key of duplicated records is specified, the first one is selected.
+
+ +

The function `tcbdbrange' is used in order to get keys of ranged records in a B+ tree database object.

+ +
+
TCLIST *tcbdbrange(TCBDB *bdb, const void *bkbuf, int bksiz, bool binc, const void *ekbuf, int eksiz, bool einc, int max);
+
`bdb' specifies the B+ tree database object.
+
`bkbuf' specifies the pointer to the region of the key of the beginning border. If it is `NULL', the first record is specified.
+
`bksiz' specifies the size of the region of the beginning key.
+
`binc' specifies whether the beginning border is inclusive or not.
+
`ekbuf' specifies the pointer to the region of the key of the ending border. If it is `NULL', the last record is specified.
+
`eksiz' specifies the size of the region of the ending key.
+
`einc' specifies whether the ending border is inclusive or not.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the keys of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcbdbrange2' is used in order to get string keys of ranged records in a B+ tree database object.

+ +
+
TCLIST *tcbdbrange2(TCBDB *bdb, const char *bkstr, bool binc, const char *ekstr, bool einc, int max);
+
`bdb' specifies the B+ tree database object.
+
`bkstr' specifies the string of the key of the beginning border. If it is `NULL', the first record is specified.
+
`binc' specifies whether the beginning border is inclusive or not.
+
`ekstr' specifies the string of the key of the ending border. If it is `NULL', the last record is specified.
+
`einc' specifies whether the ending border is inclusive or not.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the keys of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcbdbfwmkeys' is used in order to get forward matching keys in a B+ tree database object.

+ +
+
TCLIST *tcbdbfwmkeys(TCBDB *bdb, const void *pbuf, int psiz, int max);
+
`bdb' specifies the B+ tree database object.
+
`pbuf' specifies the pointer to the region of the prefix.
+
`psiz' specifies the size of the region of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcbdbfwmkeys2' is used in order to get forward matching string keys in a B+ tree database object.

+ +
+
TCLIST *tcbdbfwmkeys2(TCBDB *bdb, const char *pstr, int max);
+
`bdb' specifies the B+ tree database object.
+
`pstr' specifies the string of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tcbdbaddint' is used in order to add an integer to a record in a B+ tree database object.

+ +
+
int tcbdbaddint(TCBDB *bdb, const void *kbuf, int ksiz, int num);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is `INT_MIN'.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcbdbadddouble' is used in order to add a real number to a record in a B+ tree database object.

+ +
+
double tcbdbadddouble(TCBDB *bdb, const void *kbuf, int ksiz, double num);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is Not-a-Number.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcbdbsync' is used in order to synchronize updated contents of a B+ tree database object with the file and the device.

+ +
+
bool tcbdbsync(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
This function is useful when another process connects to the same database file.
+
+ +

The function `tcbdboptimize' is used in order to optimize the file of a B+ tree database object.

+ +
+
bool tcbdboptimize(TCBDB *bdb, int32_t lmemb, int32_t nmemb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
`lmemb' specifies the number of members in each leaf page. If it is not more than 0, the current setting is not changed.
+
`nmemb' specifies the number of members in each non-leaf page. If it is not more than 0, the current setting is not changed.
+
`bnum' specifies the number of elements of the bucket array. If it is not more than 0, the default value is specified. The default value is two times of the number of pages.
+
`apow' specifies the size of record alignment by power of 2. If it is negative, the current setting is not changed.
+
`fpow' specifies the maximum number of elements of the free block pool by power of 2. If it is negative, the current setting is not changed.
+
`opts' specifies options by bitwise-or: `BDBTLARGE' specifies that the size of the database can be larger than 2GB by using 64-bit bucket array, `BDBTDEFLATE' specifies that each record is compressed with Deflate encoding, `BDBTTCBS' specifies that each page is compressed with TCBS encoding. If it is `UINT8_MAX', the current setting is not changed.
+
If successful, the return value is true, else, it is false.
+
This function is useful to reduce the size of the database file with data fragmentation by successive updating.
+
+ +

The function `tcbdbvanish' is used in order to remove all records of a B+ tree database object.

+ +
+
bool tcbdbvanish(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcbdbcopy' is used in order to copy the database file of a B+ tree database object.

+ +
+
bool tcbdbcopy(TCBDB *bdb, const char *path);
+
`bdb' specifies the B+ tree database object.
+
`path' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line.
+
If successful, the return value is true, else, it is false. False is returned if the executed command returns non-zero code.
+
The database file is assured to be kept synchronized and not modified while the copying or executing operation is in progress. So, this function is useful to create a backup file of the database file.
+
+ +

The function `tcbdbtranbegin' is used in order to begin the transaction of a B+ tree database object.

+ +
+
bool tcbdbtranbegin(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
The database is locked by the thread while the transaction so that only one transaction can be activated with a database object at the same time. Thus, the serializable isolation level is assumed if every database operation is performed in the transaction. Because all pages are cached on memory while the transaction, the amount of referred records is limited by the memory capacity. If the database is closed during transaction, the transaction is aborted implicitly.
+
+ +

The function `tcbdbtrancommit' is used in order to commit the transaction of a B+ tree database object.

+ +
+
bool tcbdbtrancommit(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is fixed when it is committed successfully.
+
+ +

The function `tcbdbtranabort' is used in order to abort the transaction of a B+ tree database object.

+ +
+
bool tcbdbtranabort(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is discarded when it is aborted. The state of the database is rollbacked to before transaction.
+
+ +

The function `tcbdbpath' is used in order to get the file path of a B+ tree database object.

+ +
+
const char *tcbdbpath(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object.
+
The return value is the path of the database file or `NULL' if the object does not connect to any database file.
+
+ +

The function `tcbdbrnum' is used in order to get the number of records of a B+ tree database object.

+ +
+
uint64_t tcbdbrnum(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object.
+
The return value is the number of records or 0 if the object does not connect to any database file.
+
+ +

The function `tcbdbfsiz' is used in order to get the size of the database file of a B+ tree database object.

+ +
+
uint64_t tcbdbfsiz(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object.
+
The return value is the size of the database file or 0 if the object does not connect to any database file.
+
+ +

The function `tcbdbcurnew' is used in order to create a cursor object.

+ +
+
BDBCUR *tcbdbcurnew(TCBDB *bdb);
+
`bdb' specifies the B+ tree database object.
+
The return value is the new cursor object.
+
Note that the cursor is available only after initialization with the `tcbdbcurfirst' or the `tcbdbcurjump' functions and so on. Moreover, the position of the cursor will be indefinite when the database is updated after the initialization of the cursor.
+
+ +

The function `tcbdbcurdel' is used in order to delete a cursor object.

+ +
+
void tcbdbcurdel(BDBCUR *cur);
+
`cur' specifies the cursor object.
+
+ +

The function `tcbdbcurfirst' is used in order to move a cursor object to the first record.

+ +
+
bool tcbdbcurfirst(BDBCUR *cur);
+
`cur' specifies the cursor object.
+
If successful, the return value is true, else, it is false. False is returned if there is no record in the database.
+
+ +

The function `tcbdbcurlast' is used in order to move a cursor object to the last record.

+ +
+
bool tcbdbcurlast(BDBCUR *cur);
+
`cur' specifies the cursor object.
+
If successful, the return value is true, else, it is false. False is returned if there is no record in the database.
+
+ +

The function `tcbdbcurjump' is used in order to move a cursor object to the front of records corresponding a key.

+ +
+
bool tcbdbcurjump(BDBCUR *cur, const void *kbuf, int ksiz);
+
`cur' specifies the cursor object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true, else, it is false. False is returned if there is no record corresponding the condition.
+
The cursor is set to the first record corresponding the key or the next substitute if completely matching record does not exist.
+
+ +

The function `tcbdbcurjump2' is used in order to move a cursor object to the front of records corresponding a key string.

+ +
+
bool tcbdbcurjump2(BDBCUR *cur, const char *kstr);
+
`cur' specifies the cursor object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true, else, it is false. False is returned if there is no record corresponding the condition.
+
The cursor is set to the first record corresponding the key or the next substitute if completely matching record does not exist.
+
+ +

The function `tcbdbcurprev' is used in order to move a cursor object to the previous record.

+ +
+
bool tcbdbcurprev(BDBCUR *cur);
+
`cur' specifies the cursor object.
+
If successful, the return value is true, else, it is false. False is returned if there is no previous record.
+
+ +

The function `tcbdbcurnext' is used in order to move a cursor object to the next record.

+ +
+
bool tcbdbcurnext(BDBCUR *cur);
+
`cur' specifies the cursor object.
+
If successful, the return value is true, else, it is false. False is returned if there is no next record.
+
+ +

The function `tcbdbcurput' is used in order to insert a record around a cursor object.

+ +
+
bool tcbdbcurput(BDBCUR *cur, const void *vbuf, int vsiz, int cpmode);
+
`cur' specifies the cursor object of writer connection.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
`cpmode' specifies detail adjustment: `BDBCPCURRENT', which means that the value of the current record is overwritten, `BDBCPBEFORE', which means that the new record is inserted before the current record, `BDBCPAFTER', which means that the new record is inserted after the current record.
+
If successful, the return value is true, else, it is false. False is returned when the cursor is at invalid position.
+
After insertion, the cursor is moved to the inserted record.
+
+ +

The function `tcbdbcurput2' is used in order to insert a string record around a cursor object.

+ +
+
bool tcbdbcurput2(BDBCUR *cur, const char *vstr, int cpmode);
+
`cur' specifies the cursor object of writer connection.
+
`vstr' specifies the string of the value.
+
`cpmode' specifies detail adjustment: `BDBCPCURRENT', which means that the value of the current record is overwritten, `BDBCPBEFORE', which means that the new record is inserted before the current record, `BDBCPAFTER', which means that the new record is inserted after the current record.
+
If successful, the return value is true, else, it is false. False is returned when the cursor is at invalid position.
+
After insertion, the cursor is moved to the inserted record.
+
+ +

The function `tcbdbcurout' is used in order to remove the record where a cursor object is.

+ +
+
bool tcbdbcurout(BDBCUR *cur);
+
`cur' specifies the cursor object of writer connection.
+
If successful, the return value is true, else, it is false. False is returned when the cursor is at invalid position.
+
After deletion, the cursor is moved to the next record if possible.
+
+ +

The function `tcbdbcurkey' is used in order to get the key of the record where the cursor object is.

+ +
+
void *tcbdbcurkey(BDBCUR *cur, int *sp);
+
`cur' specifies the cursor object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the key, else, it is `NULL'. `NULL' is returned when the cursor is at invalid position.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbdbcurkey2' is used in order to get the key string of the record where the cursor object is.

+ +
+
char *tcbdbcurkey2(BDBCUR *cur);
+
`cur' specifies the cursor object.
+
If successful, the return value is the string of the key, else, it is `NULL'. `NULL' is returned when the cursor is at invalid position.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbdbcurkey3' is used in order to get the key of the record where the cursor object is, as a volatile buffer.

+ +
+
const void *tcbdbcurkey3(BDBCUR *cur, int *sp);
+
`cur' specifies the cursor object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the key, else, it is `NULL'. `NULL' is returned when the cursor is at invalid position.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is volatile and it may be spoiled by another operation of the database, the data should be copied into another involatile buffer immediately.
+
+ +

The function `tcbdbcurval' is used in order to get the value of the record where the cursor object is.

+ +
+
void *tcbdbcurval(BDBCUR *cur, int *sp);
+
`cur' specifies the cursor object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value, else, it is `NULL'. `NULL' is returned when the cursor is at invalid position.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbdbcurval2' is used in order to get the value string of the record where the cursor object is.

+ +
+
char *tcbdbcurval2(BDBCUR *cur);
+
`cur' specifies the cursor object.
+
If successful, the return value is the string of the value, else, it is `NULL'. `NULL' is returned when the cursor is at invalid position.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcbdbcurval3' is used in order to get the value of the record where the cursor object is, as a volatile buffer.

+ +
+
const void *tcbdbcurval3(BDBCUR *cur, int *sp);
+
`cur' specifies the cursor object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value, else, it is `NULL'. `NULL' is returned when the cursor is at invalid position.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is volatile and it may be spoiled by another operation of the database, the data should be copied into another involatile buffer immediately.
+
+ +

The function `tcbdbcurrec' is used in order to get the key and the value of the record where the cursor object is.

+ +
+
bool tcbdbcurrec(BDBCUR *cur, TCXSTR *kxstr, TCXSTR *vxstr);
+
`cur' specifies the cursor object.
+
`kxstr' specifies the object into which the key is wrote down.
+
`vxstr' specifies the object into which the value is wrote down.
+
If successful, the return value is true, else, it is false. False is returned when the cursor is at invalid position.
+
+ +

コード例

+ +

B+木データベースを使ったコード例を以下に示します。

+ +
#include <tcutil.h>
+#include <tcbdb.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+int main(int argc, char **argv){
+  TCBDB *bdb;
+  BDBCUR *cur;
+  int ecode;
+  char *key, *value;
+
+  /* オブジェクトを作成する */
+  bdb = tcbdbnew();
+
+  /* データベースを開く */
+  if(!tcbdbopen(bdb, "casket.tcb", BDBOWRITER | BDBOCREAT)){
+    ecode = tcbdbecode(bdb);
+    fprintf(stderr, "open error: %s\n", tcbdberrmsg(ecode));
+  }
+
+  /* レコードを格納する */
+  if(!tcbdbput2(bdb, "foo", "hop") ||
+     !tcbdbput2(bdb, "bar", "step") ||
+     !tcbdbput2(bdb, "baz", "jump")){
+    ecode = tcbdbecode(bdb);
+    fprintf(stderr, "put error: %s\n", tcbdberrmsg(ecode));
+  }
+
+  /* レコードを取得する */
+  value = tcbdbget2(bdb, "foo");
+  if(value){
+    printf("%s\n", value);
+    free(value);
+  } else {
+    ecode = tcbdbecode(bdb);
+    fprintf(stderr, "get error: %s\n", tcbdberrmsg(ecode));
+  }
+
+  /* 横断的にレコードを参照する */
+  cur = tcbdbcurnew(bdb);
+  tcbdbcurfirst(cur);
+  while((key = tcbdbcurkey2(cur)) != NULL){
+    value = tcbdbcurval2(cur);
+    if(value){
+      printf("%s:%s\n", key, value);
+      free(value);
+    }
+    free(key);
+    tcbdbcurnext(cur);
+  }
+  tcbdbcurdel(cur);
+
+  /* データベースを閉じる */
+  if(!tcbdbclose(bdb)){
+    ecode = tcbdbecode(bdb);
+    fprintf(stderr, "close error: %s\n", tcbdberrmsg(ecode));
+  }
+
+  /* オブジェクトを破棄する */
+  tcbdbdel(bdb);
+
+  return 0;
+}
+
+ +

CLI

+ +

B+木データベースAPIを簡単に利用するために、コマンドラインインターフェイスとして `tcbtest' と `tcbmttest' と `tcbmgr' が提供されます。

+ +

コマンド `tcbtest' は、B+木データベースAPIの機能テストや性能テストに用いるツールです。以下の書式で用います。`path' はデータベースファイルのパスを指定し、`rnum' は試行回数を指定し、`lmemb' はリーフ内メンバ数を指定し、`nmemb' は非リーフ内メンバ数を指定し、`bnum' はバケット数を指定し、`apow' はアラインメント力を指定し、`fpow' はフリーブロックプール力を指定します。

+ +
+
tcbtest write [-mt] [-cd|-ci|-cj] [-tl] [-td|-tb|-tt|-tx] [-lc num] [-nc num] [-xm num] [-df num] [-ls num] [-ca num] [-nl|-nb] [-rnd] path rnum [lmemb [nmemb [bnum [apow [fpow]]]]]
+
`00000001'、`00000002' のように変化する8バイトのキーと値を連続してデータベースに追加する。
+
tcbtest read [-mt] [-cd|-ci|-cj] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-wb] [-rnd] path
+
上記で生成したデータベースの全レコードを検索する。
+
tcbtest remove [-mt] [-cd|-ci|-cj] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path
+
上記で生成したデータベースの全レコードを削除する。
+
tcbtest rcat [-mt] [-cd|-ci|-cj] [-tl] [-td|-tb|-tt|-tx] [-lc num] [-nc num] [-xm num] [-df num] [-ls num] [-ca num] [-nl|-nb] [-pn num] [-dai|-dad|-rl|-ru] path rnum [lmemb [nmemb [bnum [apow [fpow]]]]]
+
キーがある程度重複するようにレコードの追加を行い、連結モードで処理する。
+
tcbtest queue [-mt] [-cd|-ci|-cj] [-tl] [-td|-tb|-tt|-tx] [-lc num] [-nc num] [-xm num] [-df num] [-ls num] [-ca num] [-nl|-nb] path rnum [lmemb [nmemb [bnum [apow [fpow]]]]]
+
キューの出し入れを行う。
+
tcbtest misc [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum
+
各種操作の組み合わせテストを行う。
+
tcbtest wicked [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum
+
各種更新操作を無作為に選択して実行する。
+
+ +

各オプションは以下の機能を持ちます

+ +
    +
  • -mt : 関数 `tcbdbsetmutex' を呼び出す。
  • +
  • -cd : 比較関数 `tccmpdecimal' を利用する。
  • +
  • -ci : 比較関数 `tccmpint32' を利用する。
  • +
  • -cj : 比較関数 `tccmpint64' を利用する。
  • +
  • -tl : オプション `BDBTLARGE' を有効にする。
  • +
  • -td : オプション `BDBTDEFLATE' を有効にする。
  • +
  • -tb : オプション `BDBTBZIP' を有効にする。
  • +
  • -tt : オプション `BDBTTCBS' を有効にする。
  • +
  • -tx : オプション `BDBTEXCODEC' を有効にする。
  • +
  • -lc num : リーフノード用キャッシュの最大数を指定する。
  • +
  • -nc num : 非リーフノード用キャッシュの最大数を指定する。
  • +
  • -xm num : 拡張マップメモリのサイズを指定する。
  • +
  • -df num : 自動デフラグの単位ステップ数を指定する。
  • +
  • -ls num : リーフノードの最大サイズを指定する。
  • +
  • -ca num : レコードの最大収容数を指定する。
  • +
  • -nl : オプション `BDBNOLCK' を有効にする。
  • +
  • -nb : オプション `BDBLCKNB' を有効にする。
  • +
  • -rnd : キーを無作為に選択する。
  • +
  • -wb : 関数 `tcbdbget' の代わりに関数 `tcbdbget3' を用いる。
  • +
  • -pn num : パターン数を指定する。
  • +
  • -dai : 関数 `tcbdbputcat' の代わりに関数 `tcbdbaddint' を用いる。
  • +
  • -dad : 関数 `tcbdbputcat' の代わりに関数 `tcbdbadddouble' を用いる。
  • +
  • -rl : 値を無作為な長さにする。
  • +
  • -ru : 更新操作を無作為に選択する。
  • +
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +

コマンド `tcbmttest' は、B+木データベースAPIの機能テストをマルチスレッドで行うツールです。以下の書式で用います。`path' はデータベースファイルのパスを指定し、`tnum' はスレッド数を指定し、`rnum' は試行回数を指定し、`lmemb' はリーフ内メンバ数を指定し、`nmemb' は非リーフ内メンバ数を指定し、`bnum' はバケット数を指定し、`apow' はアラインメント力を指定し、`fpow' はフリーブロックプール力を指定します。

+ +
+
tcbmttest write [-tl] [-td|-tb|-tt|-tx] [-xm num] [-df num] [-nl|-nb] [-rnd] path tnum rnum [lmemb [nmemb [bnum [apow [fpow]]]]]
+
`00000001'、`00000002' のように変化する8バイトのキーと値を連続してデータベースに追加する。
+
tcbmttest read [-xm num] [-df num] [-nl|-nb] [-wb] [-rnd] path tnum
+
上記で生成したデータベースの全レコードを検索する。
+
tcbmttest remove [-xm num] [-df num] [-nl|-nb] [-rnd] path tnum
+
上記で生成したデータベースの全レコードを削除する。
+
tcbmttest wicked [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] [-nc] path tnum rnum
+
各種更新操作を無作為に選択して実行する。
+
tcbmttest typical [-tl] [-td|-tb|-tt|-tx] [-xm num] [-df num] [-nl|-nb] [-nc] [-rr num] path tnum rnum [lmemb [nmemb [bnum [apow [fpow]]]]]
+
典型的な操作を無作為に選択して実行する。
+
tcbmttest race [-tl] [-td|-tb|-tt|-tx] [-xm num] [-df num] [-nl|-nb] path tnum rnum [lmemb [nmemb [bnum [apow [fpow]]]]]
+
レースコンディション検出のテストを行う。
+
+ +

各オプションは以下の機能を持ちます

+ +
    +
  • -tl : オプション `BDBTLARGE' を有効にする。
  • +
  • -td : オプション `BDBTDEFLATE' を有効にする。
  • +
  • -tb : オプション `BDBTBZIP' を有効にする。
  • +
  • -tt : オプション `BDBTTCBS' を有効にする。
  • +
  • -tx : オプション `BDBTEXCODEC' を有効にする。
  • +
  • -xm num : 拡張マップメモリのサイズを指定する。
  • +
  • -df num : 自動デフラグの単位ステップ数を指定する。
  • +
  • -nl : オプション `BDBNOLCK' を有効にする。
  • +
  • -nb : オプション `BDBLCKNB' を有効にする。
  • +
  • -rnd : キーを無作為に選択する。
  • +
  • -wb : 関数 `tcbdbget' の代わりに関数 `tcbdbget3' を用いる。
  • +
  • -nc : 比較テストを行わない。
  • +
  • -rr num : 読み込み操作の割合を百分率で指定する。
  • +
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +

コマンド `tcbmgr' は、B+木データベースAPIやそのアプリケーションのテストやデバッグに役立つツールです。以下の書式で用います。`path' はデータベースファイルのパスを指定し、`lmemb' はリーフ内メンバ数を指定し、`nmemb' は非リーフ内メンバ数を指定し、`bnum' はバケット数を指定し、`apow' はアラインメント力を指定し、`fpow' はフリーブロックプール力を指定し、`key' はレコードのキーを指定し、`value' はレコードの値を指定し、`file' は入力ファイルを指定します。

+ +
+
tcbmgr create [-cd|-ci|-cj] [-tl] [-td|-tb|-tt|-tx] path [lmemb [nmemb [bnum [apow [fpow]]]]]
+
データベースファイルを作成する。
+
tcbmgr inform [-nl|-nb] path
+
データベースの雑多な情報を出力する。
+
tcbmgr put [-cd|-ci|-cj] [-nl|-nb] [-sx] [-dk|-dc|-dai|-dad] path key value
+
レコードを追加する。
+
tcbmgr out [-cd|-ci|-cj] [-nl|-nb] [-sx] path key
+
レコードを削除する。
+
tcbmgr get [-cd|-ci|-cj] [-nl|-nb] [-sx] [-px] [-pz] path key
+
レコードの値を取得して標準出力する。
+
tcbmgr list [-cd|-ci|-cj] [-nl|-nb] [-m num] [-bk] [-pv] [-px] [-j str] [-rb bkey ekey] [-fm str] path
+
全てのレコードのキーを改行で区切って標準出力する。
+
tcbmgr optimize [-cd|-ci|-cj] [-tl] [-td|-tb|-tt|-tx] [-tz] [-nl|-nb] [-df] path [lmemb [nmemb [bnum [apow [fpow]]]]]
+
データベースを最適化する。
+
tcbmgr importtsv [-nl|-nb] [-sc] path [file]
+
TSVファイルの各行をキーと値とみなしてレコードを登録する。
+
tcbmgr version
+
Tokyo Cabinetのバージョン情報を標準出力する。
+
+ +

各オプションは以下の機能を持ちます

+ +
    +
  • -cd : 比較関数 `tccmpdecimal' を利用する。
  • +
  • -ci : 比較関数 `tccmpint32' を利用する。
  • +
  • -cj : 比較関数 `tccmpint64' を利用する。
  • +
  • -tl : オプション `BDBTLARGE' を有効にする。
  • +
  • -td : オプション `BDBTDEFLATE' を有効にする。
  • +
  • -tb : オプション `BDBTBZIP' を有効にする。
  • +
  • -tt : オプション `BDBTTCBS' を有効にする。
  • +
  • -tx : オプション `BDBTEXCODEC' を有効にする。
  • +
  • -nl : オプション `BDBNOLCK' を有効にする。
  • +
  • -nb : オプション `BDBLCKNB' を有効にする。
  • +
  • -sx : 入力を16進数の文字列で行う。
  • +
  • -dk : 関数 `tcbdbput' の代わりに関数 `tcbdbputkeep' を用いる。
  • +
  • -dc : 関数 `tcbdbput' の代わりに関数 `tcbdbputcat' を用いる。
  • +
  • -dd : 関数 `tcbdbput' の代わりに関数 `tcbdbputdup' を用いる。
  • +
  • -db : 関数 `tcbdbput' の代わりに関数 `tcbdbputdupback' を用いる。
  • +
  • -dai : 関数 `tcbdbput' の代わりに関数 `tcbdbaddint' を用いる。
  • +
  • -dad : 関数 `tcbdbput' の代わりに関数 `tcbdbadddouble' を用いる。
  • +
  • -px : 出力を16進数の文字列で行う。
  • +
  • -pz : 出力の末尾に改行を付加しない。
  • +
  • -m num : 出力の最大数を指定する。
  • +
  • -bk : 走査を逆方向で行う。
  • +
  • -pv : レコードの値も出力する。
  • +
  • -j str : カーソルを指定位置にジャンプさせる。
  • +
  • -rb bkey ekey : 処理対象を範囲指定する。
  • +
  • -fm str : キーの接頭辞を指定する。
  • +
  • -tz : オプション `UINT8_MAX' を有効にする。
  • +
  • -df : デフラグのみを行う。
  • +
  • -sc : キーを小文字に正規化する。
  • +
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +
+ +

固定長データベースAPI

+ +

固定長データベースは、固定長の要素からなる配列を単一のファイルに記録したデータベースです。それを扱うのが固定長データベースAPIです。`tcfdb.h' にAPIの仕様の完全な記述があります。

+ +

概要

+ +

固定長データベースAPIを使うためには、`tcutil.h'、`tcfdb.h' および関連する標準ヘッダファイルをインクルードしてください。通常、ソースファイルの冒頭付近で以下の記述を行います。

+ +
+
#include <tcutil.h>
+
#include <tcfdb.h>
+
#include <stdlib.h>
+
#include <stdbool.h>
+
#include <stdint.h>
+
+ +

固定長データベースを扱う際には、`TCFDB' 型へのポインタをオブジェクトとして用います。固定長データベースオブジェクトは、関数 `tcfdbnew' で作成し、関数 `tcfdbdel' で破棄します。作成したオブジェクトを使い終わったら必ず破棄してください。そうしないとメモリリークが発生します。

+ +

レコードの格納や探索を行う前提として、固定長データベースオブジェクトをデータベースファイルと接続させる必要があります。データベースファイルを開いて接続するには関数 `tcfdbopen' を用い、接続の解除してファイルを閉じるには関数 `tcfdbclose' を用います。開いたデータベースファイルは必ず閉じてください。そうしないとデータベースファイルが壊れたり格納したデータが失われたりする可能性があります。単一のプロセス内で複数のデータベースオブジェクトが同じデータベースファイルを同時に開くことはできません。

+ +

API(英語スマソ)

+ +

The function `tcfdberrmsg' is used in order to get the message string corresponding to an error code.

+ +
+
const char *tcfdberrmsg(int ecode);
+
`ecode' specifies the error code.
+
The return value is the message string of the error code.
+
+ +

The function `tcfdbnew' is used in order to create a fixed-length database object.

+ +
+
TCFDB *tcfdbnew(void);
+
The return value is the new fixed-length database object.
+
+ +

The function `tcfdbdel' is used in order to delete a fixed-length database object.

+ +
+
void tcfdbdel(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
If the database is not closed, it is closed implicitly. Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tcfdbecode' is used in order to get the last happened error code of a fixed-length database object.

+ +
+
int tcfdbecode(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
The return value is the last happened error code.
+
The following error codes are defined: `TCESUCCESS' for success, `TCETHREAD' for threading error, `TCEINVALID' for invalid operation, `TCENOFILE' for file not found, `TCENOPERM' for no permission, `TCEMETA' for invalid meta data, `TCERHEAD' for invalid record header, `TCEOPEN' for open error, `TCECLOSE' for close error, `TCETRUNC' for trunc error, `TCESYNC' for sync error, `TCESTAT' for stat error, `TCESEEK' for seek error, `TCEREAD' for read error, `TCEWRITE' for write error, `TCEMMAP' for mmap error, `TCELOCK' for lock error, `TCEUNLINK' for unlink error, `TCERENAME' for rename error, `TCEMKDIR' for mkdir error, `TCERMDIR' for rmdir error, `TCEKEEP' for existing record, `TCENOREC' for no record found, and `TCEMISC' for miscellaneous error.
+
+ +

The function `tcfdbsetmutex' is used in order to set mutual exclusion control of a fixed-length database object for threading.

+ +
+
bool tcfdbsetmutex(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object which is not opened.
+
If successful, the return value is true, else, it is false.
+
Note that the mutual exclusion control is needed if the object is shared by plural threads and this function should be called before the database is opened.
+
+ +

The function `tcfdbtune' is used in order to set the tuning parameters of a fixed-length database object.

+ +
+
bool tcfdbtune(TCFDB *fdb, int32_t width, int64_t limsiz);
+
`fdb' specifies the fixed-length database object which is not opened.
+
`width' specifies the width of the value of each record. If it is not more than 0, the default value is specified. The default value is 255.
+
`limsiz' specifies the limit size of the database file. If it is not more than 0, the default value is specified. The default value is 268435456.
+
If successful, the return value is true, else, it is false.
+
Note that the tuning parameters should be set before the database is opened.
+
+ +

The function `tcfdbopen' is used in order to open a database file and connect a fixed-length database object.

+ +
+
bool tcfdbopen(TCFDB *fdb, const char *path, int omode);
+
`fdb' specifies the fixed-length database object which is not opened.
+
`path' specifies the path of the database file.
+
`omode' specifies the connection mode: `FDBOWRITER' as a writer, `FDBOREADER' as a reader. If the mode is `FDBOWRITER', the following may be added by bitwise-or: `FDBOCREAT', which means it creates a new database if not exist, `FDBOTRUNC', which means it creates a new database regardless if one exists, `FDBOTSYNC', which means every transaction synchronizes updated contents with the device. Both of `FDBOREADER' and `FDBOWRITER' can be added to by bitwise-or: `FDBONOLCK', which means it opens the database file without file locking, or `FDBOLCKNB', which means locking is performed without blocking.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcfdbclose' is used in order to close a fixed-length database object.

+ +
+
bool tcfdbclose(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
If successful, the return value is true, else, it is false.
+
Update of a database is assured to be written when the database is closed. If a writer opens a database but does not close it appropriately, the database will be broken.
+
+ +

The function `tcfdbput' is used in order to store a record into a fixed-length database object.

+ +
+
bool tcfdbput(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDPREV', the number less by one than the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified. If it is `FDBIDNEXT', the number greater by one than the maximum ID number of existing records is specified.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value. If the size of the value is greater than the width tuning parameter of the database, the size is cut down to the width.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcfdbput2' is used in order to store a record with a decimal key into a fixed-length database object.

+ +
+
bool tcfdbput2(TCFDB *fdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "prev", the number less by one than the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified. If it is "next", the number greater by one than the maximum ID number of existing records is specified.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value. If the size of the value is greater than the width tuning parameter of the database, the size is cut down to the width.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcfdbput3' is used in order to store a string record with a decimal key into a fixed-length database object.

+ +
+
bool tcfdbput3(TCFDB *fdb, const char *kstr, const void *vstr);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kstr' specifies the string of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "prev", the number less by one than the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified. If it is "next", the number greater by one than the maximum ID number of existing records is specified.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcfdbputkeep' is used in order to store a new record into a fixed-length database object.

+ +
+
bool tcfdbputkeep(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDPREV', the number less by one than the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified. If it is `FDBIDNEXT', the number greater by one than the maximum ID number of existing records is specified.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value. If the size of the value is greater than the width tuning parameter of the database, the size is cut down to the width.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcfdbputkeep2' is used in order to store a new record with a decimal key into a fixed-length database object.

+ +
+
bool tcfdbputkeep2(TCFDB *fdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "prev", the number less by one than the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified. If it is "next", the number greater by one than the maximum ID number of existing records is specified.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value. If the size of the value is greater than the width tuning parameter of the database, the size is cut down to the width.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcfdbputkeep3' is used in order to store a new string record with a decimal key into a fixed-length database object.

+ +
+
bool tcfdbputkeep3(TCFDB *fdb, const char *kstr, const void *vstr);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kstr' specifies the string of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "prev", the number less by one than the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified. If it is "next", the number greater by one than the maximum ID number of existing records is specified.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcfdbputcat' is used in order to concatenate a value at the end of the existing record in a fixed-length database object.

+ +
+
bool tcfdbputcat(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDPREV', the number less by one than the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified. If it is `FDBIDNEXT', the number greater by one than the maximum ID number of existing records is specified.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value. If the size of the value is greater than the width tuning parameter of the database, the size is cut down to the width.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcfdbputcat2' is used in order to concatenate a value with a decimal key in a fixed-length database object.

+ +
+
bool tcfdbputcat2(TCFDB *fdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "prev", the number less by one than the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified. If it is "next", the number greater by one than the maximum ID number of existing records is specified.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value. If the size of the value is greater than the width tuning parameter of the database, the size is cut down to the width.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcfdbputcat3' is used in order to concatenate a string value with a decimal key in a fixed-length database object.

+ +
+
bool tcfdbputcat3(TCFDB *fdb, const char *kstr, const void *vstr);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kstr' specifies the string of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "prev", the number less by one than the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified. If it is "next", the number greater by one than the maximum ID number of existing records is specified.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcfdbout' is used in order to remove a record of a fixed-length database object.

+ +
+
bool tcfdbout(TCFDB *fdb, int64_t id);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcfdbout2' is used in order to remove a record with a decimal key of a fixed-length database object.

+ +
+
bool tcfdbout2(TCFDB *fdb, const void *kbuf, int ksiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcfdbout3' is used in order to remove a string record with a decimal key of a fixed-length database object.

+ +
+
bool tcfdbout3(TCFDB *fdb, const char *kstr);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`kstr' specifies the string of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcfdbget' is used in order to retrieve a record in a fixed-length database object.

+ +
+
void *tcfdbget(TCFDB *fdb, int64_t id, int *sp);
+
`fdb' specifies the fixed-length database object.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcfdbget2' is used in order to retrieve a record with a decimal key in a fixed-length database object.

+ +
+
void *tcfdbget2(TCFDB *fdb, const void *kbuf, int ksiz, int *sp);
+
`fdb' specifies the fixed-length database object.
+
`kbuf' specifies the pointer to the region of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcfdbget3' is used in order to retrieve a string record with a decimal key in a fixed-length database object.

+ +
+
char *tcfdbget3(TCFDB *fdb, const char *kstr);
+
`fdb' specifies the fixed-length database object.
+
`kstr' specifies the string of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcfdbget4' is used in order to retrieve a record in a fixed-length database object and write the value into a buffer.

+ +
+
int tcfdbget4(TCFDB *fdb, int64_t id, void *vbuf, int max);
+
`fdb' specifies the fixed-length database object.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified.
+
`vbuf' specifies the pointer to the buffer into which the value of the corresponding record is written.
+
`max' specifies the size of the buffer.
+
If successful, the return value is the size of the written data, else, it is -1. -1 is returned if no record corresponds to the specified key.
+
Note that an additional zero code is not appended at the end of the region of the writing buffer.
+
+ +

The function `tcfdbvsiz' is used in order to get the size of the value of a record in a fixed-length database object.

+ +
+
int tcfdbvsiz(TCFDB *fdb, int64_t id);
+
`fdb' specifies the fixed-length database object.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcfdbvsiz2' is used in order to get the size of the value with a decimal key in a fixed-length database object.

+ +
+
int tcfdbvsiz2(TCFDB *fdb, const void *kbuf, int ksiz);
+
`fdb' specifies the fixed-length database object.
+
`kbuf' specifies the pointer to the region of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcfdbvsiz3' is used in order to get the size of the string value with a decimal key in a fixed-length database object.

+ +
+
int tcfdbvsiz3(TCFDB *fdb, const char *kstr);
+
`fdb' specifies the fixed-length database object.
+
`kstr' specifies the string of the decimal key. It should be more than 0. If it is "min", the minimum ID number of existing records is specified. If it is "max", the maximum ID number of existing records is specified.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcfdbiterinit' is used in order to initialize the iterator of a fixed-length database object.

+ +
+
bool tcfdbiterinit(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
If successful, the return value is true, else, it is false.
+
The iterator is used in order to access the key of every record stored in a database.
+
+ +

The function `tcfdbiternext' is used in order to get the next ID number of the iterator of a fixed-length database object.

+ +
+
uint64_t tcfdbiternext(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
If successful, the return value is the next ID number of the iterator, else, it is 0. 0 is returned when no record is to be get out of the iterator.
+
It is possible to access every record by iteration of calling this function. It is allowed to update or remove records whose keys are fetched while the iteration. The order of this traversal access method is ascending of the ID number.
+
+ +

The function `tcfdbiternext2' is used in order to get the next decimay key of the iterator of a fixed-length database object.

+ +
+
void *tcfdbiternext2(TCFDB *fdb, int *sp);
+
`fdb' specifies the fixed-length database object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the next decimal key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. It is allowed to update or remove records whose keys are fetched while the iteration. The order of this traversal access method is ascending of the ID number.
+
+ +

The function `tcfdbiternext3' is used in order to get the next decimay key string of the iterator of a fixed-length database object.

+ +
+
char *tcfdbiternext3(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
If successful, the return value is the string of the next decimal key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. It is allowed to update or remove records whose keys are fetched while the iteration. The order of this traversal access method is ascending of the ID number.
+
+ +

The function `tcfdbrange' is used in order to get range matching ID numbers in a fixed-length database object.

+ +
+
uint64_t *tcfdbrange(TCFDB *fdb, int64_t lower, int64_t upper, int max, int *np);
+
`fdb' specifies the fixed-length database object.
+
`lower' specifies the lower limit of the range. If it is `FDBIDMIN', the minimum ID is specified.
+
`upper' specifies the upper limit of the range. If it is `FDBIDMAX', the maximum ID is specified.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
`np' specifies the pointer to the variable into which the number of elements of the return value is assigned.
+
If successful, the return value is the pointer to an array of ID numbers of the corresponding records. `NULL' is returned on failure. This function does never fail. It returns an empty array even if no key corresponds.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcfdbrange2' is used in order to get range matching decimal keys in a fixed-length database object.

+ +
+
TCLIST *tcfdbrange2(TCFDB *fdb, const void *lbuf, int lsiz, const void *ubuf, int usiz, int max);
+
`fdb' specifies the fixed-length database object.
+
`lbuf' specifies the pointer to the region of the lower key. If it is "min", the minimum ID number of existing records is specified.
+
`lsiz' specifies the size of the region of the lower key.
+
`ubuf' specifies the pointer to the region of the upper key. If it is "max", the maximum ID number of existing records is specified.
+
`usiz' specifies the size of the region of the upper key.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding decimal keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcfdbrange3' is used in order to get range matching decimal keys with strings in a fixed-length database object.

+ +
+
TCLIST *tcfdbrange3(TCFDB *fdb, const char *lstr, const char *ustr, int max);
+
`fdb' specifies the fixed-length database object.
+
`lstr' specifies the string of the lower key. If it is "min", the minimum ID number of existing records is specified.
+
`ustr' specifies the string of the upper key. If it is "max", the maximum ID number of existing records is specified.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding decimal keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcfdbrange4' is used in order to get keys with an interval notation in a fixed-length database object.

+ +
+
TCLIST *tcfdbrange4(TCFDB *fdb, const void *ibuf, int isiz, int max);
+
`fdb' specifies the fixed-length database object.
+
`ibuf' specifies the pointer to the region of the interval notation.
+
`isiz' specifies the size of the region of the interval notation.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding decimal keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcfdbrange5' is used in order to get keys with an interval notation string in a fixed-length database object.

+ +
+
TCLIST *tcfdbrange5(TCFDB *fdb, const void *istr, int max);
+
`fdb' specifies the fixed-length database object.
+
`istr' specifies the pointer to the region of the interval notation string.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding decimal keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcfdbaddint' is used in order to add an integer to a record in a fixed-length database object.

+ +
+
int tcfdbaddint(TCFDB *fdb, int64_t id, int num);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDPREV', the number less by one than the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified. If it is `FDBIDNEXT', the number greater by one than the maximum ID number of existing records is specified.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is `INT_MIN'.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcfdbadddouble' is used in order to add a real number to a record in a fixed-length database object.

+ +
+
double tcfdbadddouble(TCFDB *fdb, int64_t id, double num);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`id' specifies the ID number. It should be more than 0. If it is `FDBIDMIN', the minimum ID number of existing records is specified. If it is `FDBIDPREV', the number less by one than the minimum ID number of existing records is specified. If it is `FDBIDMAX', the maximum ID number of existing records is specified. If it is `FDBIDNEXT', the number greater by one than the maximum ID number of existing records is specified.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is Not-a-Number.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcfdbsync' is used in order to synchronize updated contents of a fixed-length database object with the file and the device.

+ +
+
bool tcfdbsync(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
This function is useful when another process connects to the same database file.
+
+ +

The function `tcfdboptimize' is used in order to optimize the file of a fixed-length database object.

+ +
+
bool tcfdboptimize(TCFDB *fdb, int32_t width, int64_t limsiz);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
`width' specifies the width of the value of each record. If it is not more than 0, the current setting is not changed.
+
`limsiz' specifies the limit size of the database file. If it is not more than 0, the current setting is not changed.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcfdbvanish' is used in order to remove all records of a fixed-length database object.

+ +
+
bool tcfdbvanish(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcfdbcopy' is used in order to copy the database file of a fixed-length database object.

+ +
+
bool tcfdbcopy(TCFDB *fdb, const char *path);
+
`fdb' specifies the fixed-length database object.
+
`path' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line.
+
If successful, the return value is true, else, it is false. False is returned if the executed command returns non-zero code.
+
The database file is assured to be kept synchronized and not modified while the copying or executing operation is in progress. So, this function is useful to create a backup file of the database file.
+
+ +

The function `tcfdbtranbegin' is used in order to begin the transaction of a fixed-length database object.

+ +
+
bool tcfdbtranbegin(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
The database is locked by the thread while the transaction so that only one transaction can be activated with a database object at the same time. Thus, the serializable isolation level is assumed if every database operation is performed in the transaction. All updated regions are kept track of by write ahead logging while the transaction. If the database is closed during transaction, the transaction is aborted implicitly.
+
+ +

The function `tcfdbtrancommit' is used in order to commit the transaction of a fixed-length database object.

+ +
+
bool tcfdbtrancommit(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is fixed when it is committed successfully.
+
+ +

The function `tcfdbtranabort' is used in order to abort the transaction of a fixed-length database object.

+ +
+
bool tcfdbtranabort(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is discarded when it is aborted. The state of the database is rollbacked to before transaction.
+
+ +

The function `tcfdbpath' is used in order to get the file path of a fixed-length database object.

+ +
+
const char *tcfdbpath(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
The return value is the path of the database file or `NULL' if the object does not connect to any database file.
+
+ +

The function `tcfdbrnum' is used in order to get the number of records of a fixed-length database object.

+ +
+
uint64_t tcfdbrnum(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
The return value is the number of records or 0 if the object does not connect to any database file.
+
+ +

The function `tcfdbfsiz' is used in order to get the size of the database file of a fixed-length database object.

+ +
+
uint64_t tcfdbfsiz(TCFDB *fdb);
+
`fdb' specifies the fixed-length database object.
+
The return value is the size of the database file or 0 if the object does not connect to any database file.
+
+ +

コード例

+ +

固定長データベースを使ったコード例を以下に示します。

+ +
#include <tcutil.h>
+#include <tcfdb.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+int main(int argc, char **argv){
+  TCFDB *fdb;
+  int ecode;
+  char *key, *value;
+
+  /* オブジェクトを作成する */
+  fdb = tcfdbnew();
+
+  /* データベースを開く */
+  if(!tcfdbopen(fdb, "casket.tcf", FDBOWRITER | FDBOCREAT)){
+    ecode = tcfdbecode(fdb);
+    fprintf(stderr, "open error: %s\n", tcfdberrmsg(ecode));
+  }
+
+  /* レコードを格納する */
+  if(!tcfdbput3(fdb, "1", "one") ||
+     !tcfdbput3(fdb, "12", "twelve") ||
+     !tcfdbput3(fdb, "144", "one forty four")){
+    ecode = tcfdbecode(fdb);
+    fprintf(stderr, "put error: %s\n", tcfdberrmsg(ecode));
+  }
+
+  /* レコードを取得する */
+  value = tcfdbget3(fdb, "1");
+  if(value){
+    printf("%s\n", value);
+    free(value);
+  } else {
+    ecode = tcfdbecode(fdb);
+    fprintf(stderr, "get error: %s\n", tcfdberrmsg(ecode));
+  }
+
+  /* 横断的にレコードを参照する */
+  tcfdbiterinit(fdb);
+  while((key = tcfdbiternext3(fdb)) != NULL){
+    value = tcfdbget3(fdb, key);
+    if(value){
+      printf("%s:%s\n", key, value);
+      free(value);
+    }
+    free(key);
+  }
+
+  /* データベースを閉じる */
+  if(!tcfdbclose(fdb)){
+    ecode = tcfdbecode(fdb);
+    fprintf(stderr, "close error: %s\n", tcfdberrmsg(ecode));
+  }
+
+  /* オブジェクトを破棄する */
+  tcfdbdel(fdb);
+
+  return 0;
+}
+
+ +

CLI

+ +

固定長データベースAPIを簡単に利用するために、コマンドラインインターフェイスとして `tcftest' と `tcfmttest' と `tcfmgr' が提供されます。

+ +

コマンド `tcftest' は、ハッシュデータベースAPIの機能テストや性能テストに用いるツールです。以下の書式で用います。`path' はデータベースファイルのパスを指定し、`rnum' は試行回数を指定し、`width' は各レコードの値の幅を指定し、`limsiz' はデータベースファイルの制限サイズを指定します。

+ +
+
tcftest write [-mt] [-nl|-nb] [-rnd] path rnum [width [limsiz]]
+
`00000001'、`00000002' のように変化する8バイトのキーと値を連続してデータベースに追加する。
+
tcftest read [-mt] [-nl|-nb] [-wb] [-rnd] path
+
上記で生成したデータベースの全レコードを検索する。
+
tcftest remove [-mt] [-nl|-nb] [-rnd] path
+
上記で生成したデータベースの全レコードを削除する。
+
tcftest rcat [-mt] [-nl|-nb] [-pn num] [-dai|-dad|-rl] path rnum [width [limsiz]]
+
キーがある程度重複するようにレコードの追加を行い、連結モードで処理する。
+
tcftest misc [-mt] [-nl|-nb] path rnum
+
各種操作の組み合わせテストを行う。
+
tcftest wicked [-mt] [-nl|-nb] path rnum
+
各種更新操作を無作為に選択して実行する。
+
+ +

各オプションは以下の機能を持ちます

+ +
    +
  • -mt : 関数 `tcfdbsetmutex' を呼び出す。
  • +
  • -nl : オプション `FDBNOLCK' を有効にする。
  • +
  • -nb : オプション `FDBLCKNB' を有効にする。
  • +
  • -rnd : キーを無作為に選択する。
  • +
  • -wb : 関数 `tcfdbget' の代わりに関数 `tcfdbget3' を用いる。
  • +
  • -pn num : パターン数を指定する。
  • +
  • -dai : 関数 `tcfdbputcat' の代わりに関数 `tcfdbaddint' を用いる。
  • +
  • -dad : 関数 `tcfdbputcat' の代わりに関数 `tcfdbadddouble' を用いる。
  • +
  • -rl : 値を無作為な長さにする。
  • +
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +

コマンド `tcfmttest' は、ハッシュデータベースAPIの機能テストをマルチスレッドで行うツールです。以下の書式で用います。`path' はデータベースファイルのパスを指定し、`tnum' はスレッド数を指定し、`rnum' は試行回数を指定し、`width' は各レコードの値の幅を指定し、`limsiz' はデータベースファイルの制限サイズを指定します。

+ +
+
tcfmttest write [-nl|-nb] [-rnd] path tnum rnum [width [limsiz]]
+
`00000001'、`00000002' のように変化する8バイトのキーと値を連続してデータベースに追加する。
+
tcfmttest read [-nl|-nb] [-wb] [-rnd] path tnum
+
上記で生成したデータベースの全レコードを検索する。
+
tcfmttest remove [-nl|-nb] [-rnd] path tnum
+
上記で生成したデータベースの全レコードを削除する。
+
tcfmttest wicked [-nl|-nb] [-nc] path tnum rnum
+
各種更新操作を無作為に選択して実行する。
+
tcfmttest typical [-nl|-nb] [-nc] [-rr num] path tnum rnum [width [limsiz]]
+
典型的な操作を無作為に選択して実行する。
+
+ +

各オプションは以下の機能を持ちます

+ +
    +
  • -nl : オプション `FDBNOLCK' を有効にする。
  • +
  • -nb : オプション `FDBLCKNB' を有効にする。
  • +
  • -rnd : キーを無作為に選択する。
  • +
  • -wb : 関数 `tcfdbget' の代わりに関数 `tcfdbget3' を用いる。
  • +
  • -nc : 比較テストを行わない。
  • +
  • -rr num : 読み込み操作の割合を百分率で指定する。
  • +
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +

コマンド `tcfmgr' は、ハッシュデータベースAPIやそのアプリケーションのテストやデバッグに役立つツールです。以下の書式で用います。`path' はデータベースファイルのパスを指定し、`width' は各レコードの値の幅を指定し、`limsiz' はデータベースファイルの制限サイズを指定し、`key' はレコードのキーを指定し、`value' はレコードの値を指定し、`file' は入力ファイルを指定します。

+ +
+
tcfmgr create path [width [limsiz]]
+
データベースファイルを作成する。
+
tcfmgr inform [-nl|-nb] path
+
データベースの雑多な情報を出力する。
+
tcfmgr put [-nl|-nb] [-sx] [-dk|-dc|-dai|-dad] path key value
+
レコードを追加する。
+
tcfmgr out [-nl|-nb] [-sx] path key
+
レコードを削除する。
+
tcfmgr get [-nl|-nb] [-sx] [-px] [-pz] path key
+
レコードの値を取得して標準出力する。
+
tcfmgr list [-nl|-nb] [-m num] [-pv] [-px] [-rb lkey ukey] [-ri str] path
+
全てのレコードのキーを改行で区切って標準出力する。
+
tcfmgr optimize [-tz] [-nl|-nb] path [width [limsiz]]
+
データベースを最適化する。
+
tcfmgr importtsv [-nl|-nb] [-sc] path [file]
+
TSVファイルの各行をキーと値とみなしてレコードを登録する。
+
tcfmgr version
+
Tokyo Cabinetのバージョン情報を標準出力する。
+
+ +

各オプションは以下の機能を持ちます

+ +
    +
  • -nl : オプション `FDBNOLCK' を有効にする。
  • +
  • -nb : オプション `FDBLCKNB' を有効にする。
  • +
  • -sx : 入力を16進数の文字列で行う。
  • +
  • -dk : 関数 `tcfdbput' の代わりに関数 `tcfdbputkeep' を用いる。
  • +
  • -dc : 関数 `tcfdbput' の代わりに関数 `tcfdbputcat' を用いる。
  • +
  • -dai : 関数 `tcfdbput' の代わりに関数 `tcfdbaddint' を用いる。
  • +
  • -dad : 関数 `tcfdbput' の代わりに関数 `tcfdbadddouble' を用いる。
  • +
  • -px : 出力を16進数の文字列で行う。
  • +
  • -pz : 出力の末尾に改行を付加しない。
  • +
  • -m num : 出力の最大数を指定する。
  • +
  • -pv : レコードの値も出力する。
  • +
  • -rb lkey ukey : 処理対象を範囲指定する。
  • +
  • -ri str : 処理対象の範囲を区間記法で指定する。
  • +
  • -sc : キーを小文字に正規化する。
  • +
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +
+ +

テーブルデータベースAPI

+ +

テーブルデータベースは、プライマリキーと任意のコラムを持つレコード群を単一のファイルに記録したデータベースです。それを扱うのがテーブルデータベースAPIです。`tctdb.h' にAPIの仕様の完全な記述があります。

+ +

概要

+ +

テーブルデータベースAPIを使うためには、`tcutil.h'、`tctdb.h' および関連する標準ヘッダファイルをインクルードしてください。通常、ソースファイルの冒頭付近で以下の記述を行います。

+ +
+
#include <tcutil.h>
+
#include <tctdb.h>
+
#include <stdlib.h>
+
#include <stdbool.h>
+
#include <stdint.h>
+
+ +

テーブルデータベースを扱う際には、`TCTDB' 型へのポインタをオブジェクトとして用います。テーブルデータベースオブジェクトは、関数 `tctdbnew' で作成し、関数 `tctdbdel' で破棄します。作成したオブジェクトを使い終わったら必ず破棄してください。そうしないとメモリリークが発生します。

+ +

レコードの格納や探索を行う前提として、テーブルデータベースオブジェクトをデータベースファイルと接続させる必要があります。データベースファイルを開いて接続するには関数 `tctdbopen' を用い、接続の解除してファイルを閉じるには関数 `tctdbclose' を用います。開いたデータベースファイルは必ず閉じてください。そうしないとデータベースファイルが壊れたり格納したデータが失われたりする可能性があります。単一のプロセス内で複数のデータベースオブジェクトが同じデータベースファイルを同時に開くことはできません。

+ +

API(英語スマメ)

+ +

The function `tctdberrmsg' is used in order to get the message string corresponding to an error code.

+ +
+
const char *tctdberrmsg(int ecode);
+
`ecode' specifies the error code.
+
The return value is the message string of the error code.
+
+ +

The function `tctdbnew' is used in order to create a table database object.

+ +
+
TCTDB *tctdbnew(void);
+
The return value is the new table database object.
+
+ +

The function `tctdbdel' is used in order to delete a table database object.

+ +
+
void tctdbdel(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
If the database is not closed, it is closed implicitly. Note that the deleted object and its derivatives can not be used anymore.
+
+ +

The function `tctdbecode' is used in order to get the last happened error code of a table database object.

+ +
+
int tctdbecode(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
The return value is the last happened error code.
+
The following error codes are defined: `TCESUCCESS' for success, `TCETHREAD' for threading error, `TCEINVALID' for invalid operation, `TCENOFILE' for file not found, `TCENOPERM' for no permission, `TCEMETA' for invalid meta data, `TCERHEAD' for invalid record header, `TCEOPEN' for open error, `TCECLOSE' for close error, `TCETRUNC' for trunc error, `TCESYNC' for sync error, `TCESTAT' for stat error, `TCESEEK' for seek error, `TCEREAD' for read error, `TCEWRITE' for write error, `TCEMMAP' for mmap error, `TCELOCK' for lock error, `TCEUNLINK' for unlink error, `TCERENAME' for rename error, `TCEMKDIR' for mkdir error, `TCERMDIR' for rmdir error, `TCEKEEP' for existing record, `TCENOREC' for no record found, and `TCEMISC' for miscellaneous error.
+
+ +

The function `tctdbsetmutex' is used in order to set mutual exclusion control of a table database object for threading.

+ +
+
bool tctdbsetmutex(TCTDB *tdb);
+
`tdb' specifies the table database object which is not opened.
+
If successful, the return value is true, else, it is false.
+
Note that the mutual exclusion control is needed if the object is shared by plural threads and this function should be called before the database is opened.
+
+ +

The function `tctdbtune' is used in order to set the tuning parameters of a table database object.

+ +
+
bool tctdbtune(TCTDB *tdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
`tdb' specifies the table database object which is not opened.
+
`bnum' specifies the number of elements of the bucket array. If it is not more than 0, the default value is specified. The default value is 131071. Suggested size of the bucket array is about from 0.5 to 4 times of the number of all records to be stored.
+
`apow' specifies the size of record alignment by power of 2. If it is negative, the default value is specified. The default value is 4 standing for 2^4=16.
+
`fpow' specifies the maximum number of elements of the free block pool by power of 2. If it is negative, the default value is specified. The default value is 10 standing for 2^10=1024.
+
`opts' specifies options by bitwise-or: `TDBTLARGE' specifies that the size of the database can be larger than 2GB by using 64-bit bucket array, `TDBTDEFLATE' specifies that each record is compressed with Deflate encoding, `TDBTBZIP' specifies that each record is compressed with BZIP2 encoding, `TDBTTCBS' specifies that each record is compressed with TCBS encoding.
+
If successful, the return value is true, else, it is false.
+
Note that the tuning parameters should be set before the database is opened.
+
+ +

The function `tctdbsetcache' is set the caching parameters of a table database object.

+ +
+
bool tctdbsetcache(TCTDB *tdb, int32_t rcnum, int32_t lcnum, int32_t ncnum);
+
`tdb' specifies the table database object which is not opened.
+
`rcnum' specifies the maximum number of records to be cached. If it is not more than 0, the record cache is disabled. It is disabled by default.
+
`lcnum' specifies the maximum number of leaf nodes to be cached. If it is not more than 0, the default value is specified. The default value is 2048.
+
`ncnum' specifies the maximum number of non-leaf nodes to be cached. If it is not more than 0, the default value is specified. The default value is 512.
+
If successful, the return value is true, else, it is false.
+
Note that the caching parameters should be set before the database is opened. Leaf nodes and non-leaf nodes are used in column indices.
+
+ +

The function `tctdbsetxmsiz' is used in order to set the size of the extra mapped memory of a table database object.

+ +
+
bool tctdbsetxmsiz(TCTDB *tdb, int64_t xmsiz);
+
`tdb' specifies the table database object which is not opened.
+
`xmsiz' specifies the size of the extra mapped memory. If it is not more than 0, the extra mapped memory is disabled. The default size is 67108864.
+
If successful, the return value is true, else, it is false.
+
Note that the mapping parameters should be set before the database is opened.
+
+ +

The function `tctdbsetdfunit' is used in order to set the unit step number of auto defragmentation of a table database object.

+ +
+
bool tctdbsetdfunit(TCTDB *tdb, int32_t dfunit);
+
`tdb' specifies the table database object which is not opened.
+
`dfunit' specifie the unit step number. If it is not more than 0, the auto defragmentation is disabled. It is disabled by default.
+
If successful, the return value is true, else, it is false.
+
Note that the defragmentation parameters should be set before the database is opened.
+
+ +

The function `tctdbopen' is used in order to open a database file and connect a table database object.

+ +
+
bool tctdbopen(TCTDB *tdb, const char *path, int omode);
+
`tdb' specifies the table database object which is not opened.
+
`path' specifies the path of the database file.
+
`omode' specifies the connection mode: `TDBOWRITER' as a writer, `TDBOREADER' as a reader. If the mode is `TDBOWRITER', the following may be added by bitwise-or: `TDBOCREAT', which means it creates a new database if not exist, `TDBOTRUNC', which means it creates a new database regardless if one exists, `TDBOTSYNC', which means every transaction synchronizes updated contents with the device. Both of `TDBOREADER' and `TDBOWRITER' can be added to by bitwise-or: `TDBONOLCK', which means it opens the database file without file locking, or `TDBOLCKNB', which means locking is performed without blocking.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tctdbclose' is used in order to close a table database object.

+ +
+
bool tctdbclose(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
If successful, the return value is true, else, it is false.
+
Update of a database is assured to be written when the database is closed. If a writer opens a database but does not close it appropriately, the database will be broken.
+
+ +

The function `tctdbput' is used in order to store a record into a table database object.

+ +
+
bool tctdbput(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols);
+
`tdb' specifies the table database object connected as a writer.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
`cols' specifies a map object containing columns.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tctdbput2' is used in order to store a string record into a table database object with a zero separated column string.

+ +
+
bool tctdbput2(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz);
+
`tdb' specifies the table database object connected as a writer.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
`cbuf' specifies the pointer to the region of the zero separated column string where the name and the value of each column are situated one after the other.
+
`csiz' specifies the size of the region of the column string.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tctdbput3' is used in order to store a string record into a table database object with a tab separated column string.

+ +
+
bool tctdbput3(TCTDB *tdb, const char *pkstr, const char *cstr);
+
`tdb' specifies the table database object connected as a writer.
+
`pkstr' specifies the string of the primary key.
+
`cstr' specifies the string of the the tab separated column string where the name and the value of each column are situated one after the other.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tctdbputkeep' is used in order to store a new record into a table database object.

+ +
+
bool tctdbputkeep(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols);
+
`tdb' specifies the table database object connected as a writer.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
`cols' specifies a map object containing columns.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tctdbputkeep2' is used in order to store a new string record into a table database object with a zero separated column string.

+ +
+
bool tctdbputkeep2(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz);
+
`tdb' specifies the table database object connected as a writer.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
`cbuf' specifies the pointer to the region of the zero separated column string where the name and the value of each column are situated one after the other.
+
`csiz' specifies the size of the region of the column string.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tctdbputkeep3' is used in order to store a new string record into a table database object with a tab separated column string.

+ +
+
bool tctdbputkeep3(TCTDB *tdb, const char *pkstr, const char *cstr);
+
`tdb' specifies the table database object connected as a writer.
+
`pkstr' specifies the string of the primary key.
+
`cstr' specifies the string of the the tab separated column string where the name and the value of each column are situated one after the other.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tctdbputcat' is used in order to concatenate columns of the existing record in a table database object.

+ +
+
bool tctdbputcat(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols);
+
`tdb' specifies the table database object connected as a writer.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
`cols' specifies a map object containing columns.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tctdbputcat2' is used in order to concatenate columns in a table database object with a zero separated column string.

+ +
+
bool tctdbputcat2(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz);
+
`tdb' specifies the table database object connected as a writer.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
`cbuf' specifies the pointer to the region of the zero separated column string where the name and the value of each column are situated one after the other.
+
`csiz' specifies the size of the region of the column string.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tctdbputcat3' is used in order to concatenate columns in a table database object with with a tab separated column string.

+ +
+
bool tctdbputcat3(TCTDB *tdb, const char *pkstr, const char *cstr);
+
`tdb' specifies the table database object connected as a writer.
+
`pkstr' specifies the string of the primary key.
+
`cstr' specifies the string of the the tab separated column string where the name and the value of each column are situated one after the other.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tctdbout' is used in order to remove a record of a table database object.

+ +
+
bool tctdbout(TCTDB *tdb, const void *pkbuf, int pksiz);
+
`tdb' specifies the table database object connected as a writer.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tctdbout2' is used in order to remove a string record of a table database object.

+ +
+
bool tctdbout2(TCTDB *tdb, const char *pkstr);
+
`tdb' specifies the table database object connected as a writer.
+
`pkstr' specifies the string of the primary key.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tctdbget' is used in order to retrieve a record in a table database object.

+ +
+
TCMAP *tctdbget(TCTDB *tdb, const void *pkbuf, int pksiz);
+
`tdb' specifies the table database object.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
If successful, the return value is a map object of the columns of the corresponding record. `NULL' is returned if no record corresponds.
+
Because the object of the return value is created with the function `tcmapnew', it should be deleted with the function `tcmapdel' when it is no longer in use.
+
+ +

The function `tctdbget2' is used in order to retrieve a record in a table database object as a zero separated column string.

+ +
+
char *tctdbget2(TCTDB *tdb, const void *pkbuf, int pksiz, int *sp);
+
`tdb' specifies the table database object.
+
`pkbuf' specifies the pointer to the region of the primary key.
+
`pksiz' specifies the size of the region of the primary key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the column string of the corresponding record. `NULL' is returned if no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tctdbget3' is used in order to retrieve a string record in a table database object as a tab separated column string.

+ +
+
char *tctdbget3(TCTDB *tdb, const char *pkstr);
+
`tdb' specifies the table database object.
+
`pkstr' specifies the string of the primary key.
+
If successful, the return value is the tab separated column string of the corresponding record. `NULL' is returned if no record corresponds.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tctdbvsiz' is used in order to get the size of the value of a record in a table database object.

+ +
+
int tctdbvsiz(TCTDB *tdb, const void *pkbuf, int pksiz);
+
`tdb' specifies the table database object.
+
`kbuf' specifies the pointer to the region of the primary key.
+
`ksiz' specifies the size of the region of the primary key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tctdbvsiz2' is used in order to get the size of the value of a string record in a table database object.

+ +
+
int tctdbvsiz2(TCTDB *tdb, const char *pkstr);
+
`tdb' specifies the table database object.
+
`kstr' specifies the string of the primary key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tctdbiterinit' is used in order to initialize the iterator of a table database object.

+ +
+
bool tctdbiterinit(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
If successful, the return value is true, else, it is false.
+
The iterator is used in order to access the primary key of every record stored in a database.
+
+ +

The function `tctdbiternext' is used in order to get the next primary key of the iterator of a table database object.

+ +
+
void *tctdbiternext(TCTDB *tdb, int *sp);
+
`tdb' specifies the table database object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the next primary key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. It is allowed to update or remove records whose keys are fetched while the iteration. However, it is not assured if updating the database is occurred while the iteration. Besides, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access.
+
+ +

The function `tctdbiternext2' is used in order to get the next primary key string of the iterator of a table database object.

+ +
+
char *tctdbiternext2(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
If successful, the return value is the string of the next primary key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. However, it is not assured if updating the database is occurred while the iteration. Besides, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access.
+
+ +

The function `tctdbiternext3' is used in order to get the columns of the next record of the iterator of a table database object.

+ +
+
TCMAP *tctdbiternext3(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
If successful, the return value is a map object of the columns of the next record, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator. The primary key is added into the map as a column of an empty string key.
+
Because the object of the return value is created with the function `tcmapnew', it should be deleted with the function `tcmapdel' when it is no longer in use. It is possible to access every record by iteration of calling this function. However, it is not assured if updating the database is occurred while the iteration. Besides, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access.
+
+ +

The function `tctdbfwmkeys' is used in order to get forward matching primary keys in a table database object.

+ +
+
TCLIST *tctdbfwmkeys(TCTDB *tdb, const void *pbuf, int psiz, int max);
+
`tdb' specifies the table database object.
+
`pbuf' specifies the pointer to the region of the prefix.
+
`psiz' specifies the size of the region of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tctdbfwmkeys2' is used in order to get forward matching string primary keys in a table database object.

+ +
+
TCLIST *tctdbfwmkeys2(TCTDB *tdb, const char *pstr, int max);
+
`tdb' specifies the table database object.
+
`pstr' specifies the string of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tctdbaddint' is used in order to add an integer to a column of a record in a table database object.

+ +
+
int tctdbaddint(TCTDB *tdb, const void *pkbuf, int pksiz, int num);
+
`tdb' specifies the table database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the primary key.
+
`ksiz' specifies the size of the region of the primary key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is `INT_MIN'.
+
The additional value is stored as a decimal string value of a column whose name is "_num". If no record corresponds, a new record with the additional value is stored.
+
+ +

The function `tctdbadddouble' is used in order to add a real number to a column of a record in a table database object.

+ +
+
double tctdbadddouble(TCTDB *tdb, const void *pkbuf, int pksiz, double num);
+
`tdb' specifies the table database object connected as a writer.
+
`kbuf' specifies the pointer to the region of the primary key.
+
`ksiz' specifies the size of the region of the primary key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is Not-a-Number.
+
The additional value is stored as a decimal string value of a column whose name is "_num". If no record corresponds, a new record with the additional value is stored.
+
+ +

The function `tctdbsync' is used in order to synchronize updated contents of a table database object with the file and the device.

+ +
+
bool tctdbsync(TCTDB *tdb);
+
`tdb' specifies the table database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
This function is useful when another process connects to the same database file.
+
+ +

The function `tctdboptimize' is used in order to optimize the file of a table database object.

+ +
+
bool tctdboptimize(TCTDB *tdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts);
+
`tdb' specifies the table database object connected as a writer.
+
`bnum' specifies the number of elements of the bucket array. If it is not more than 0, the default value is specified. The default value is two times of the number of records.
+
`apow' specifies the size of record alignment by power of 2. If it is negative, the current setting is not changed.
+
`fpow' specifies the maximum number of elements of the free block pool by power of 2. If it is negative, the current setting is not changed.
+
`opts' specifies options by bitwise-or: `TDBTLARGE' specifies that the size of the database can be larger than 2GB by using 64-bit bucket array, `TDBTDEFLATE' specifies that each record is compressed with Deflate encoding, `TDBTBZIP' specifies that each record is compressed with BZIP2 encoding, `TDBTTCBS' specifies that each record is compressed with TCBS encoding. If it is `UINT8_MAX', the current setting is not changed.
+
If successful, the return value is true, else, it is false.
+
This function is useful to reduce the size of the database file with data fragmentation by successive updating.
+
+ +

The function `tctdbvanish' is used in order to remove all records of a table database object.

+ +
+
bool tctdbvanish(TCTDB *tdb);
+
`tdb' specifies the table database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tctdbcopy' is used in order to copy the database file of a table database object.

+ +
+
bool tctdbcopy(TCTDB *tdb, const char *path);
+
`tdb' specifies the table database object.
+
`path' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line.
+
If successful, the return value is true, else, it is false. False is returned if the executed command returns non-zero code.
+
The database file is assured to be kept synchronized and not modified while the copying or executing operation is in progress. So, this function is useful to create a backup file of the database file.
+
+ +

The function `tctdbtranbegin' is used in order to begin the transaction of a table database object.

+ +
+
bool tctdbtranbegin(TCTDB *tdb);
+
`tdb' specifies the table database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
The database is locked by the thread while the transaction so that only one transaction can be activated with a database object at the same time. Thus, the serializable isolation level is assumed if every database operation is performed in the transaction. Because all pages are cached on memory while the transaction, the amount of referred records is limited by the memory capacity. If the database is closed during transaction, the transaction is aborted implicitly.
+
+ +

The function `tctdbtrancommit' is used in order to commit the transaction of a table database object.

+ +
+
bool tctdbtrancommit(TCTDB *tdb);
+
`tdb' specifies the table database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is fixed when it is committed successfully.
+
+ +

The function `tctdbtranabort' is used in order to abort the transaction of a table database object.

+ +
+
bool tctdbtranabort(TCTDB *tdb);
+
`tdb' specifies the table database object connected as a writer.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is discarded when it is aborted. The state of the database is rollbacked to before transaction.
+
+ +

The function `tctdbpath' is used in order to get the file path of a table database object.

+ +
+
const char *tctdbpath(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
The return value is the path of the database file or `NULL' if the object does not connect to any database file.
+
+ +

The function `tctdbrnum' is used in order to get the number of records ccccof a table database object.

+ +
+
uint64_t tctdbrnum(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
The return value is the number of records or 0 if the object does not connect to any database file.
+
+ +

The function `tctdbfsiz' is used in order to get the size of the database file of a table database object.

+ +
+
uint64_t tctdbfsiz(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
The return value is the size of the database file or 0 if the object does not connect to any database file.
+
+ +

The function `tctdbsetindex' is used in order to set a column index to a table database object.

+ +
+
bool tctdbsetindex(TCTDB *tdb, const char *name, int type);
+
`tdb' specifies the table database object connected as a writer.
+
`name' specifies the name of a column. If the name of an existing index is specified, the index is rebuilt. An empty string means the primary key.
+
`type' specifies the index type: `TDBITLEXICAL' for lexical string, `TDBITDECIMAL' for decimal string, `TDBITTOKEN' for token inverted index, `TDBITQGRAM' for q-gram inverted index. If it is `TDBITOPT', the index is optimized. If it is `TDBITVOID', the index is removed. If `TDBITKEEP' is added by bitwise-or and the index exists, this function merely returns failure.
+
If successful, the return value is true, else, it is false.
+
Note that the setting indices should be set after the database is opened.
+
+ +

The function `tctdbgenuid' is used in order to generate a unique ID number of a table database object.

+ +
+
int64_t tctdbgenuid(TCTDB *tdb);
+
`tdb' specifies the table database object connected as a writer.
+
The return value is the new unique ID number or -1 on failure.
+
+ +

The function `tctdbqrynew' is used in order to create a query object.

+ +
+
TDBQRY *tctdbqrynew(TCTDB *tdb);
+
`tdb' specifies the table database object.
+
The return value is the new query object.
+
+ +

The function `tctdbqrydel' is used in order to delete a query object.

+ +
+
void tctdbqrydel(TDBQRY *qry);
+
`qry' specifies the query object.
+
+ +

The function `tctdbqryaddcond' is used in order to add a narrowing condition to a query object.

+ +
+
void tctdbqryaddcond(TDBQRY *qry, const char *name, int op, const char *expr);
+
`qry' specifies the query object.
+
`name' specifies the name of a column. An empty string means the primary key.
+
`op' specifies an operation type: `TDBQCSTREQ' for string which is equal to the expression, `TDBQCSTRINC' for string which is included in the expression, `TDBQCSTRBW' for string which begins with the expression, `TDBQCSTREW' for string which ends with the expression, `TDBQCSTRAND' for string which includes all tokens in the expression, `TDBQCSTROR' for string which includes at least one token in the expression, `TDBQCSTROREQ' for string which is equal to at least one token in the expression, `TDBQCSTRRX' for string which matches regular expressions of the expression, `TDBQCNUMEQ' for number which is equal to the expression, `TDBQCNUMGT' for number which is greater than the expression, `TDBQCNUMGE' for number which is greater than or equal to the expression, `TDBQCNUMLT' for number which is less than the expression, `TDBQCNUMLE' for number which is less than or equal to the expression, `TDBQCNUMBT' for number which is between two tokens of the expression, `TDBQCNUMOREQ' for number which is equal to at least one token in the expression, `TDBQCFTSPH' for full-text search with the phrase of the expression, `TDBQCFTSAND' for full-text search with all tokens in the expression, `TDBQCFTSOR' for full-text search with at least one token in the expression, `TDBQCFTSEX' for full-text search with the compound expression. All operations can be flagged by bitwise-or: `TDBQCNEGATE' for negation, `TDBQCNOIDX' for using no index.
+
`expr' specifies an operand exression.
+
+ +

The function `tctdbqrysetorder' is used in order to set the order of a query object.

+ +
+
void tctdbqrysetorder(TDBQRY *qry, const char *name, int type);
+
`qry' specifies the query object.
+
`name' specifies the name of a column. An empty string means the primary key.
+
`type' specifies the order type: `TDBQOSTRASC' for string ascending, `TDBQOSTRDESC' for string descending, `TDBQONUMASC' for number ascending, `TDBQONUMDESC' for number descending.
+
+ +

The function `tctdbqrysetlimit' is used in order to set the limit number of records of the result of a query object.

+ +
+
void tctdbqrysetlimit(TDBQRY *qry, int max, int skip);
+
`qry' specifies the query object.
+
`max' specifies the maximum number of records of the result. If it is negative, no limit is specified.
+
`skip' specifies the number of skipped records of the result. If it is not more than 0, no record is skipped.
+
+ +

The function `tctdbqrysearch' is used in order to execute the search of a query object.

+ +
+
TCLIST *tctdbqrysearch(TDBQRY *qry);
+
`qry' specifies the query object.
+
The return value is a list object of the primary keys of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

The function `tctdbqrysearchout' is used in order to remove each record corresponding to a query object.

+ +
+
bool tctdbqrysearchout(TDBQRY *qry);
+
`qry' specifies the query object of the database connected as a writer.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tctdbqryproc' is used in order to process each record corresponding to a query object.

+ +
+
bool tctdbqryproc(TDBQRY *qry, TDBQRYPROC proc, void *op);
+
`qry' specifies the query object of the database connected as a writer.
+
`proc' specifies the pointer to the iterator function called for each record. It receives four parameters. The first parameter is the pointer to the region of the primary key. The second parameter is the size of the region of the primary key. The third parameter is a map object containing columns. The fourth parameter is the pointer to the optional opaque object. It returns flags of the post treatment by bitwise-or: `TDBQPPUT' to modify the record, `TDBQPOUT' to remove the record, `TDBQPSTOP' to stop the iteration.
+
`op' specifies an arbitrary pointer to be given as a parameter of the iterator function. If it is not needed, `NULL' can be specified.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tctdbqryhint' is used in order to get the hint string of a query object.

+ +
+
const char *tctdbqryhint(TDBQRY *qry);
+
`qry' specifies the query object.
+
The return value is the hint string.
+
+ +

The function `tctdbmetasearch' is used in order to retrieve records with multiple query objects and get the set of the result.

+ +
+
TCLIST *tctdbmetasearch(TDBQRY **qrys, int num, int type);
+
`qrys' specifies an array of the query objects.
+
`num' specifies the number of elements of the array.
+
`type' specifies a set operation type: `TDBMSUNION' for the union set, `TDBMSISECT' for the intersection set, `TDBMSDIFF' for the difference set.
+
The return value is a list object of the primary keys of the corresponding records. This function does never fail. It returns an empty list even if no record corresponds.
+
If the first query object has the order setting, the result array is sorted by the order. Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

コード例

+ +

テーブルデータベースを使ったコード例を以下に示します。

+ +
#include <tcutil.h>
+#include <tctdb.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+int main(int argc, char **argv){
+  TCTDB *tdb;
+  int ecode, pksiz, i, rsiz;
+  char pkbuf[256];
+  const char *rbuf, *name;
+  TCMAP *cols;
+  TDBQRY *qry;
+  TCLIST *res;
+
+  /* オブジェクトを作成する */
+  tdb = tctdbnew();
+
+  /* データベースを開く */
+  if(!tctdbopen(tdb, "casket.tct", TDBOWRITER | TDBOCREAT)){
+    ecode = tctdbecode(tdb);
+    fprintf(stderr, "open error: %s\n", tctdberrmsg(ecode));
+  }
+
+  /* レコードを格納する */
+  pksiz = sprintf(pkbuf, "%ld", (long)tctdbgenuid(tdb));
+  cols = tcmapnew3("name", "mikio", "age", "30", "lang", "ja,en,c", NULL);
+  if(!tctdbput(tdb, pkbuf, pksiz, cols)){
+    ecode = tctdbecode(tdb);
+    fprintf(stderr, "put error: %s\n", tctdberrmsg(ecode));
+  }
+  tcmapdel(cols);
+
+  /* 素朴な方法でレコードを格納する */
+  pksiz = sprintf(pkbuf, "12345");
+  cols = tcmapnew();
+  tcmapput2(cols, "name", "falcon");
+  tcmapput2(cols, "age", "31");
+  tcmapput2(cols, "lang", "ja");
+  if(!tctdbput(tdb, pkbuf, pksiz, cols)){
+    ecode = tctdbecode(tdb);
+    fprintf(stderr, "put error: %s\n", tctdberrmsg(ecode));
+  }
+  tcmapdel(cols);
+
+  /* TSV文字列を使ってレコードを格納する */
+  if(!tctdbput3(tdb, "abcde", "name\tjoker\tage\t19\tlang\ten,es")){
+    ecode = tctdbecode(tdb);
+    fprintf(stderr, "put error: %s\n", tctdberrmsg(ecode));
+  }
+
+  /* レコードを検索する */
+  qry = tctdbqrynew(tdb);
+  tctdbqryaddcond(qry, "age", TDBQCNUMGE, "20");
+  tctdbqryaddcond(qry, "lang", TDBQCSTROR, "ja,en");
+  tctdbqrysetorder(qry, "name", TDBQOSTRASC);
+  tctdbqrysetlimit(qry, 10, 0);
+  res = tctdbqrysearch(qry);
+  for(i = 0; i < tclistnum(res); i++){
+    rbuf = tclistval(res, i, &rsiz);
+    cols = tctdbget(tdb, rbuf, rsiz);
+    if(cols){
+      printf("%s", rbuf);
+      tcmapiterinit(cols);
+      while((name = tcmapiternext2(cols)) != NULL){
+        printf("\t%s\t%s", name, tcmapget2(cols, name));
+      }
+      printf("\n");
+      tcmapdel(cols);
+    }
+  }
+  tclistdel(res);
+  tctdbqrydel(qry);
+
+  /* データベースを閉じる */
+  if(!tctdbclose(tdb)){
+    ecode = tctdbecode(tdb);
+    fprintf(stderr, "close error: %s\n", tctdberrmsg(ecode));
+  }
+
+  /* オブジェクトを破棄する */
+  tctdbdel(tdb);
+
+  return 0;
+}
+
+ +

CLI

+ +

テーブルデータベースAPIを簡単に利用するために、コマンドラインインターフェイスとして `tcttest' と `tctmttest' と `tctmgr' が提供されます。

+ +

コマンド `tcttest' は、テーブルデータベースAPIの機能テストや性能テストに用いるツールです。以下の書式で用います。`path' はデータベースファイルのパスを指定し、`rnum' は試行回数を指定し、`bnum' はバケット数を指定し、`apow' はアラインメント力を指定し、`fpow' はフリーブロックプール力を指定します。

+ +
+
tcttest write [-mt] [-tl] [-td|-tb|-tt|-tx] [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-ip] [-is] [-in] [-it] [-if] [-ix] [-nl|-nb] [-rnd] path rnum [bnum [apow [fpow]]]
+
"str" と "num" と "type" と "flag" をコラムに持つレコード群を連続してデータベースに追加する。
+
tcttest read [-mt] [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path
+
上記で生成したデータベースの全レコードを検索する。
+
tcttest remove [-mt] [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path
+
上記で生成したデータベースの全レコードを削除する。
+
tcttest rcat [-mt] [-tl] [-td|-tb|-tt|-tx] [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-ip] [-is] [-in] [-it] [-if] [-ix] [-nl|-nb] [-pn num] [-dai|-dad|-rl|-ru] path rnum [bnum [apow [fpow]]]
+
キーがある程度重複するようにレコードの追加を行い、連結モードで処理する。
+
tcttest misc [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum
+
各種操作の組み合わせテストを行う。
+
tcttest wicked [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum
+
各種更新操作を無作為に選択して実行する。
+
+ +

各オプションは以下の機能を持ちます

+ +
    +
  • -mt : 関数 `tctdbsetmutex' を呼び出す。
  • +
  • -tl : オプション `TDBTLARGE' を有効にする。
  • +
  • -td : オプション `TDBTDEFLATE' を有効にする。
  • +
  • -tb : オプション `TDBTBZIP' を有効にする。
  • +
  • -tt : オプション `TDBTTCBS' を有効にする。
  • +
  • -tx : オプション `TDBTEXCODEC' を有効にする。
  • +
  • -rc num : レコード用キャッシュの最大数を指定する。
  • +
  • -lc num : リーフノード用キャッシュの最大数を指定する。
  • +
  • -nc num : 非リーフノード用キャッシュの最大数を指定する。
  • +
  • -xm num : 拡張マップメモリのサイズを指定する。
  • +
  • -df num : 自動デフラグの単位ステップ数を指定する。
  • +
  • -ip : 主キーに数値型のインデックスを張る。
  • +
  • -is : "str" コラムに文字列型のインデックスを張る。
  • +
  • -in : "num" コラムに数値型のインデックスを張る。
  • +
  • -it : "type" コラムに文字列型のインデックスを張る。
  • +
  • -if : "flag" コラムにトークン転置インデックスを張る。
  • +
  • -ix : "text" コラムにq-gram転置インデックスを張る。
  • +
  • -nl : オプション `TDBNOLCK' を有効にする。
  • +
  • -nb : オプション `TDBLCKNB' を有効にする。
  • +
  • -rnd : キーを無作為に選択する。
  • +
  • -pn num : パターン数を指定する。
  • +
  • -dai : 関数 `tctdbputcat' の代わりに関数 `tctdbaddint' を用いる。
  • +
  • -dad : 関数 `tctdbputcat' の代わりに関数 `tctdbadddouble' を用いる。
  • +
  • -rl : 値を無作為な長さにする。
  • +
  • -ru : 更新操作を無作為に選択する。
  • +
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +

コマンド `tctmttest' は、テーブルデータベースAPIの機能テストをマルチスレッドで行うツールです。以下の書式で用います。`path' はデータベースファイルのパスを指定し、`tnum' はスレッド数を指定し、`rnum' は試行回数を指定し、`bnum' はバケット数を指定し、`apow' はアラインメント力を指定し、`fpow' はフリーブロックプール力を指定します。

+ +
+
tctmttest write [-tl] [-td|-tb|-tt|-tx] [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-ip] [-is] [-in] [-it] [-if] [-ix] [-nl|-nb] [-rnd] path tnum rnum [bnum [apow [fpow]]]
+
"str" と "num" と "type" と "flag" をコラムに持つレコード群を連続してデータベースに追加する。
+
tctmttest read [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path tnum
+
上記で生成したデータベースの全レコードを検索する。
+
tctmttest remove [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path tnum
+
上記で生成したデータベースの全レコードを削除する。
+
tctmttest wicked [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path tnum rnum
+
各種更新操作を無作為に選択して実行する。
+
tctmttest typical [-tl] [-td|-tb|-tt|-tx] [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-rr num] path tnum rnum [bnum [apow [fpow]]
+
典型的な操作を無作為に選択して実行する。
+
+ +

各オプションは以下の機能を持ちます

+ +
    +
  • -tl : オプション `TDBTLARGE' を有効にする。
  • +
  • -td : オプション `TDBTDEFLATE' を有効にする。
  • +
  • -tb : オプション `TDBTBZIP' を有効にする。
  • +
  • -tt : オプション `TDBTTCBS' を有効にする。
  • +
  • -tx : オプション `TDBTEXCODEC' を有効にする。
  • +
  • -rc num : レコード用キャッシュの最大数を指定する。
  • +
  • -lc num : リーフノード用キャッシュの最大数を指定する。
  • +
  • -nc num : 非リーフノード用キャッシュの最大数を指定する。
  • +
  • -xm num : 拡張マップメモリのサイズを指定する。
  • +
  • -df num : 自動デフラグの単位ステップ数を指定する。
  • +
  • -ip : 主キーに数値型のインデックスを張る。
  • +
  • -is : "str" コラムに文字列型のインデックスを張る。
  • +
  • -in : "num" コラムに数値型のインデックスを張る。
  • +
  • -it : "type" コラムに文字列型のインデックスを張る。
  • +
  • -if : "flag" コラムにトークン転置インデックスを張る。
  • +
  • -ix : "text" コラムにq-gram転置インデックスを張る。
  • +
  • -nl : オプション `TDBNOLCK' を有効にする。
  • +
  • -nb : オプション `TDBLCKNB' を有効にする。
  • +
  • -rnd : キーを無作為に選択する。
  • +
  • -rr num : 読み込み操作の割合を百分率で指定する。
  • +
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +

コマンド `tctmgr' は、テーブルデータベースAPIやそのアプリケーションのテストやデバッグに役立つツールです。以下の書式で用います。`path' はデータベースファイルのパスを指定し、`bnum' はバケット数を指定し、`apow' はアラインメント力を指定し、`fpow' はフリーブロックプール力を指定し、`pkey' はレコードの主キーを指定し、`cols' はコラムの名前と値を交互に指定し、`name' はコラムの名前を指定し、`op' は演算子を指定し、`expr' は条件式を指定し、`file' は入力ファイルを指定します。

+ +
+
tctmgr create [-tl] [-td|-tb|-tt|-tx] path [bnum [apow [fpow]]]
+
データベースファイルを作成する。
+
tctmgr inform [-nl|-nb] path
+
データベースの雑多な情報を出力する。
+
tctmgr put [-nl|-nb] [-sx] [-dk|-dc|-dai|-dad] path pkey [cols ...]
+
レコードを追加する。
+
tctmgr out [-nl|-nb] [-sx] path pkey
+
レコードを削除する。
+
tctmgr get [-nl|-nb] [-sx] [-px] [-pz] path pkey
+
レコードの値を取得して標準出力する。
+
tctmgr list [-nl|-nb] [-m num] [-pv] [-px] [-fm str] path
+
全てのレコードの主キーを改行で区切って標準出力する。
+
tctmgr search [-nl|-nb] [-ord name type] [-m num] [-sk num] [-kw] [-pv] [-px] [-ph] [-bt num] [-rm] [-ms type] path [name op expr ...]
+
検索条件に合致するレコードを改行で区切って標準出力する。
+
tctmgr optimize [-tl] [-td|-tb|-tt|-tx] [-tz] [-nl|-nb] [-df] path [bnum [apow [fpow]]]
+
データベースを最適化する。
+
tctmgr setindex [-nl|-nb] [-it type] path name
+
インデックスを設定する。
+
tctmgr importtsv [-nl|-nb] [-sc] path [file]
+
TSVファイルの各行をキーと値とみなしてレコードを登録する。
+
tctmgr version
+
Tokyo Cabinetのバージョン情報を標準出力する。
+
+ +

各オプションは以下の機能を持ちます

+ +
    +
  • -tl : オプション `TDBTLARGE' を有効にする。
  • +
  • -td : オプション `TDBTDEFLATE' を有効にする。
  • +
  • -tb : オプション `TDBTBZIP' を有効にする。
  • +
  • -tt : オプション `TDBTTCBS' を有効にする。
  • +
  • -tx : オプション `TDBTEXCODEC' を有効にする。
  • +
  • -nl : オプション `TDBNOLCK' を有効にする。
  • +
  • -nb : オプション `TDBLCKNB' を有効にする。
  • +
  • -sx : 入力を16進数の文字列で行う。
  • +
  • -dk : 関数 `tctdbput' の代わりに関数 `tctdbputkeep' を用いる。
  • +
  • -dc : 関数 `tctdbput' の代わりに関数 `tctdbputcat' を用いる。
  • +
  • -dai : 関数 `tctdbput' の代わりに関数 `tctdbaddint' を用いる。
  • +
  • -dad : 関数 `tctdbput' の代わりに関数 `tctdbadddouble' を用いる。
  • +
  • -px : 出力を16進数の文字列で行う。
  • +
  • -pz : 出力の末尾に改行を付加しない。
  • +
  • -m num : 出力の最大数を指定する。
  • +
  • -pv : レコードの値も出力する。
  • +
  • -fm str : キーの接頭辞を指定する。
  • +
  • -ord name type : 結果の並び順を指定する。
  • +
  • -sk num : 結果のスキップ件数を指定する。
  • +
  • -kw : KWIC文字列を出力する。
  • +
  • -ph : ヒント情報も出力する。
  • +
  • -bt : ベンチマークテストの回数を指定する。
  • +
  • -rm : 結果のレコードを全て削除する。
  • +
  • -ms type : メタ検索の集合演算を指定する。
  • +
  • -tz : オプション `UINT8_MAX' を有効にする。
  • +
  • -df : デフラグのみを行う。
  • +
  • -it : インデックスの型を "lexical" か "decimal" か "token" か "qgram" か "void" で指定する。
  • +
  • -cv : 既存のインデックスを削除する。
  • +
  • -sc : キーを小文字に正規化する。
  • +
+ +

`search' サブコマンドの演算子には、"STREQ", "STRINC", "STRBW", "STREW", "STRAND", "STROR", "STROREQ", "STRRX", "NUMEQ", "NUMGT", "NUMGE", "NUMLT", "NUMLE", "NUMBT", "NUMOREQ", "FTSPH", "FTSAND", "FTSOR", "FTSEX" のいずれかを用いることができます。各演算子に "~" を接頭させると論理的な意味が反転されます。"+" を接頭させるとその演算子にはインデックスが適用されません。`-ord' オプションの型指定には、"STRASC", "STRDESC", "NUMASC", "NUMDESC" のいずれかを用いることができます。`-ms' オプションの型指定には、"UNION", "ISECT", "DIFF" のいずれかを用いることができます。このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +
+ +

抽象データベースAPI

+ +

抽象データベースは、オンメモリハッシュデータベースとオンメモリツリーデータベースとハッシュデータベースとB+木データベースと固定長データベースとテーブルデータベースを同一のAPIで抽象化したデータベースです。それを扱うのが抽象データベースAPIです。`tcadb.h' にAPIの仕様の完全な記述があります。

+ +

概要

+ +

抽象データベースAPIを使うためには、`tcutil.h'、`tcadb.h' および関連する標準ヘッダファイルをインクルードしてください。通常、ソースファイルの冒頭付近で以下の記述を行います。

+ +
+
#include <tcutil.h>
+
#include <tcadb.h>
+
#include <stdlib.h>
+
#include <stdbool.h>
+
#include <stdint.h>
+
+ +

抽象データベースを扱う際には、`TCADB' 型へのポインタをオブジェクトとして用います。B+木データベースオブジェクトは、関数 `tcadbnew' で作成し、関数 `tcadbdel' で破棄します。作成したオブジェクトを使い終わったら必ず破棄してください。そうしないとメモリリークが発生します。

+ +

レコードの格納や探索を行う前提として、抽象データベースオブジェクトを具象データベースと接続させる必要があります。具象データベースを開いて接続するには関数 `tcadbopen' を用い、接続の解除してファイルを閉じるには関数 `tcadbclose' を用います。開いた具象データベースは必ず閉じてください。そうしないと具象データベースが壊れたり格納したデータが失われたりする可能性があります。単一のプロセス内で複数のデータベースオブジェクトが同じデータベースファイルを同時に開くことはできません。

+ +

API(英語ごめんね)

+ +

The function `tcadbnew' is used in order to create an abstract database object.

+ +
+
TCADB *tcadbnew(void);
+
The return value is the new abstract database object.
+
+ +

The function `tcadbdel' is used in order to delete an abstract database object.

+ +
+
void tcadbdel(TCADB *adb);
+
`adb' specifies the abstract database object.
+
+ +

The function `tcadbopen' is used in order to open an abstract database.

+ +
+
bool tcadbopen(TCADB *adb, const char *name);
+
`adb' specifies the abstract database object.
+
`name' specifies the name of the database. If it is "*", the database will be an on-memory hash database. If it is "+", the database will be an on-memory tree database. If its suffix is ".tch", the database will be a hash database. If its suffix is ".tcb", the database will be a B+ tree database. If its suffix is ".tcf", the database will be a fixed-length database. If its suffix is ".tct", the database will be a table database. Otherwise, this function fails. Tuning parameters can trail the name, separated by "#". Each parameter is composed of the name and the value, separated by "=". On-memory hash database supports "bnum", "capnum", and "capsiz". On-memory tree database supports "capnum" and "capsiz". Hash database supports "mode", "bnum", "apow", "fpow", "opts", "rcnum", "xmsiz", and "dfunit". B+ tree database supports "mode", "lmemb", "nmemb", "bnum", "apow", "fpow", "opts", "lcnum", "ncnum", "xmsiz", and "dfunit". Fixed-length database supports "mode", "width", and "limsiz". Table database supports "mode", "bnum", "apow", "fpow", "opts", "rcnum", "lcnum", "ncnum", "xmsiz", "dfunit", and "idx".
+
If successful, the return value is true, else, it is false.
+
The tuning parameter "capnum" specifies the capacity number of records. "capsiz" specifies the capacity size of using memory. Records spilled the capacity are removed by the storing order. "mode" can contain "w" of writer, "r" of reader, "c" of creating, "t" of truncating, "e" of no locking, and "f" of non-blocking lock. The default mode is relevant to "wc". "opts" can contains "l" of large option, "d" of Deflate option, "b" of BZIP2 option, and "t" of TCBS option. "idx" specifies the column name of an index and its type separated by ":". For example, "casket.tch#bnum=1000000#opts=ld" means that the name of the database file is "casket.tch", and the bucket number is 1000000, and the options are large and Deflate.
+
+ +

The function `tcadbclose' is used in order to close an abstract database object.

+ +
+
bool tcadbclose(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is true, else, it is false.
+
Update of a database is assured to be written when the database is closed. If a writer opens a database but does not close it appropriately, the database will be broken.
+
+ +

The function `tcadbput' is used in order to store a record into an abstract database object.

+ +
+
bool tcadbput(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcadbput2' is used in order to store a string record into an abstract object.

+ +
+
bool tcadbput2(TCADB *adb, const char *kstr, const char *vstr);
+
`adb' specifies the abstract database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, it is overwritten.
+
+ +

The function `tcadbputkeep' is used in order to store a new record into an abstract database object.

+ +
+
bool tcadbputkeep(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcadbputkeep2' is used in order to store a new string record into an abstract database object.

+ +
+
bool tcadbputkeep2(TCADB *adb, const char *kstr, const char *vstr);
+
`adb' specifies the abstract database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If a record with the same key exists in the database, this function has no effect.
+
+ +

The function `tcadbputcat' is used in order to concatenate a value at the end of the existing record in an abstract database object.

+ +
+
bool tcadbputcat(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`vbuf' specifies the pointer to the region of the value.
+
`vsiz' specifies the size of the region of the value.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcadbputcat2' is used in order to concatenate a string value at the end of the existing record in an abstract database object.

+ +
+
bool tcadbputcat2(TCADB *adb, const char *kstr, const char *vstr);
+
`adb' specifies the abstract database object.
+
`kstr' specifies the string of the key.
+
`vstr' specifies the string of the value.
+
If successful, the return value is true, else, it is false.
+
If there is no corresponding record, a new record is created.
+
+ +

The function `tcadbout' is used in order to remove a record of an abstract database object.

+ +
+
bool tcadbout(TCADB *adb, const void *kbuf, int ksiz);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcadbout2' is used in order to remove a string record of an abstract database object.

+ +
+
bool tcadbout2(TCADB *adb, const char *kstr);
+
`adb' specifies the abstract database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcadbget' is used in order to retrieve a record in an abstract database object.

+ +
+
void *tcadbget(TCADB *adb, const void *kbuf, int ksiz, int *sp);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcadbget2' is used in order to retrieve a string record in an abstract database object.

+ +
+
char *tcadbget2(TCADB *adb, const char *kstr);
+
`adb' specifies the abstract database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the string of the value of the corresponding record. `NULL' is returned if no record corresponds.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use.
+
+ +

The function `tcadbvsiz' is used in order to get the size of the value of a record in an abstract database object.

+ +
+
int tcadbvsiz(TCADB *adb, const void *kbuf, int ksiz);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcadbvsiz2' is used in order to get the size of the value of a string record in an abstract database object.

+ +
+
int tcadbvsiz2(TCADB *adb, const char *kstr);
+
`adb' specifies the abstract database object.
+
`kstr' specifies the string of the key.
+
If successful, the return value is the size of the value of the corresponding record, else, it is -1.
+
+ +

The function `tcadbiterinit' is used in order to initialize the iterator of an abstract database object.

+ +
+
bool tcadbiterinit(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is true, else, it is false.
+
The iterator is used in order to access the key of every record stored in a database.
+
+ +

The function `tcadbiternext' is used in order to get the next key of the iterator of an abstract database object.

+ +
+
void *tcadbiternext(TCADB *adb, int *sp);
+
`adb' specifies the abstract database object.
+
`sp' specifies the pointer to the variable into which the size of the region of the return value is assigned.
+
If successful, the return value is the pointer to the region of the next key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because an additional zero code is appended at the end of the region of the return value, the return value can be treated as a character string. Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. It is allowed to update or remove records whose keys are fetched while the iteration. However, it is not assured if updating the database is occurred while the iteration. Besides, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access.
+
+ +

The function `tcadbiternext2' is used in order to get the next key string of the iterator of an abstract database object.

+ +
+
char *tcadbiternext2(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is the string of the next key, else, it is `NULL'. `NULL' is returned when no record is to be get out of the iterator.
+
Because the region of the return value is allocated with the `malloc' call, it should be released with the `free' call when it is no longer in use. It is possible to access every record by iteration of calling this function. However, it is not assured if updating the database is occurred while the iteration. Besides, the order of this traversal access method is arbitrary, so it is not assured that the order of storing matches the one of the traversal access.
+
+ +

The function `tcadbfwmkeys' is used in order to get forward matching keys in an abstract database object.

+ +
+
TCLIST *tcadbfwmkeys(TCADB *adb, const void *pbuf, int psiz, int max);
+
`adb' specifies the abstract database object.
+
`pbuf' specifies the pointer to the region of the prefix.
+
`psiz' specifies the size of the region of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcadbfwmkeys2' is used in order to get forward matching string keys in an abstract database object.

+ +
+
TCLIST *tcadbfwmkeys2(TCADB *adb, const char *pstr, int max);
+
`adb' specifies the abstract database object.
+
`pstr' specifies the string of the prefix.
+
`max' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified.
+
The return value is a list object of the corresponding keys. This function does never fail. It returns an empty list even if no key corresponds.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use. Note that this function may be very slow because every key in the database is scanned.
+
+ +

The function `tcadbaddint' is used in order to add an integer to a record in an abstract database object.

+ +
+
int tcadbaddint(TCADB *adb, const void *kbuf, int ksiz, int num);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is `INT_MIN'.
+
If the corresponding record exists, the value is treated as an integer and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcadbadddouble' is used in order to add a real number to a record in an abstract database object.

+ +
+
double tcadbadddouble(TCADB *adb, const void *kbuf, int ksiz, double num);
+
`adb' specifies the abstract database object.
+
`kbuf' specifies the pointer to the region of the key.
+
`ksiz' specifies the size of the region of the key.
+
`num' specifies the additional value.
+
If successful, the return value is the summation value, else, it is Not-a-Number.
+
If the corresponding record exists, the value is treated as a real number and is added to. If no record corresponds, a new record of the additional value is stored.
+
+ +

The function `tcadbsync' is used in order to synchronize updated contents of an abstract database object with the file and the device.

+ +
+
bool tcadbsync(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcadboptimize' is used in order to optimize the storage of an abstract database object.

+ +
+
bool tcadboptimize(TCADB *adb, const char *params);
+
`adb' specifies the abstract database object.
+
`params' specifies the string of the tuning parameters, which works as with the tuning of parameters the function `tcadbopen'. If it is `NULL', it is not used.
+
If successful, the return value is true, else, it is false.
+
This function is useful to reduce the size of the database storage with data fragmentation by successive updating.
+
+ +

The function `tcadbvanish' is used in order to remove all records of an abstract database object.

+ +
+
bool tcadbvanish(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is true, else, it is false.
+
+ +

The function `tcadbcopy' is used in order to copy the database file of an abstract database object.

+ +
+
bool tcadbcopy(TCADB *adb, const char *path);
+
`adb' specifies the abstract database object.
+
`path' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line.
+
If successful, the return value is true, else, it is false. False is returned if the executed command returns non-zero code.
+
The database file is assured to be kept synchronized and not modified while the copying or executing operation is in progress. So, this function is useful to create a backup file of the database file.
+
+ +

The function `tcadbtranbegin' is used in order to begin the transaction of an abstract database object.

+ +
+
bool tcadbtranbegin(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is true, else, it is false.
+
The database is locked by the thread while the transaction so that only one transaction can be activated with a database object at the same time. Thus, the serializable isolation level is assumed if every database operation is performed in the transaction. All updated regions are kept track of by write ahead logging while the transaction. If the database is closed during transaction, the transaction is aborted implicitly.
+
+ +

The function `tcadbtrancommit' is used in order to commit the transaction of an abstract database object.

+ +
+
bool tcadbtrancommit(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is fixed when it is committed successfully.
+
+ +

The function `tcadbtranabort' is used in order to abort the transaction of an abstract database object.

+ +
+
bool tcadbtranabort(TCADB *adb);
+
`adb' specifies the abstract database object.
+
If successful, the return value is true, else, it is false.
+
Update in the transaction is discarded when it is aborted. The state of the database is rollbacked to before transaction.
+
+ +

The function `tcadbpath' is used in order to get the file path of an abstract database object.

+ +
+
const char *tcadbpath(TCADB *adb);
+
`adb' specifies the abstract database object.
+
The return value is the path of the database file or `NULL' if the object does not connect to any database. "*" stands for on-memory hash database. "+" stands for on-memory tree database.
+
+ +

The function `tcadbrnum' is used in order to get the number of records of an abstract database object.

+ +
+
uint64_t tcadbrnum(TCADB *adb);
+
`adb' specifies the abstract database object.
+
The return value is the number of records or 0 if the object does not connect to any database instance.
+
+ +

The function `tcadbsize' is used in order to get the size of the database of an abstract database object.

+ +
+
uint64_t tcadbsize(TCADB *adb);
+
`adb' specifies the abstract database object.
+
The return value is the size of the database or 0 if the object does not connect to any database instance.
+
+ +

The function `tcadbmisc' is used in order to call a versatile function for miscellaneous operations of an abstract database object.

+ +
+
TCLIST *tcadbmisc(TCADB *adb, const char *name, const TCLIST *args);
+
`adb' specifies the abstract database object.
+
`name' specifies the name of the function. All databases support "put", "out", "get", "putlist", "outlist", "getlist", and "getpart". "put" is to store a record. It receives a key and a value, and returns an empty list. "out" is to remove a record. It receives a key, and returns an empty list. "get" is to retrieve a record. It receives a key, and returns a list of the values. "putlist" is to store records. It receives keys and values one after the other, and returns an empty list. "outlist" is to remove records. It receives keys, and returns an empty list. "getlist" is to retrieve records. It receives keys, and returns keys and values of corresponding records one after the other. "getpart" is to retrieve the partial value of a record. It receives a key, the offset of the region, and the length of the region.
+
`args' specifies a list object containing arguments.
+
If successful, the return value is a list object of the result. `NULL' is returned on failure.
+
Because the object of the return value is created with the function `tclistnew', it should be deleted with the function `tclistdel' when it is no longer in use.
+
+ +

コード例

+ +

抽象データベースを使ったコード例を以下に示します。

+ +
#include <tcutil.h>
+#include <tcadb.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+int main(int argc, char **argv){
+  TCADB *adb;
+  char *key, *value;
+
+  /* オブジェクトを作成する */
+  adb = tcadbnew();
+
+  /* データベースを開く */
+  if(!tcadbopen(adb, "casket.tch")){
+    fprintf(stderr, "open error\n");
+  }
+
+  /* レコードを格納する */
+  if(!tcadbput2(adb, "foo", "hop") ||
+     !tcadbput2(adb, "bar", "step") ||
+     !tcadbput2(adb, "baz", "jump")){
+    fprintf(stderr, "put error\n");
+  }
+
+  /* レコードを取得する */
+  value = tcadbget2(adb, "foo");
+  if(value){
+    printf("%s\n", value);
+    free(value);
+  } else {
+    fprintf(stderr, "get error\n");
+  }
+
+  /* 横断的にレコードを参照する */
+  tcadbiterinit(adb);
+  while((key = tcadbiternext2(adb)) != NULL){
+    value = tcadbget2(adb, key);
+    if(value){
+      printf("%s:%s\n", key, value);
+      free(value);
+    }
+    free(key);
+  }
+
+  /* データベースを閉じる */
+  if(!tcadbclose(adb)){
+    fprintf(stderr, "close error\n");
+  }
+
+  /* オブジェクトを破棄する */
+  tcadbdel(adb);
+
+  return 0;
+}
+
+ +

CLI

+ +

抽象データベースAPIを簡単に利用するために、コマンドラインインターフェイスとして `tcatest' と `tcamttest' と `tcamgr' が提供されます。

+ +

コマンド `tcatest' は、抽象データベースAPIの機能テストや性能テストに用いるツールです。以下の書式で用います。`name' はデータベースの名前を指定し、`rnum' は試行回数を指定し、`tnum' はトランザクションの回数を指定します。

+ +
+
tcatest write name rnum
+
`00000001'、`00000002' のように変化する8バイトのキーと値を連続してデータベースに追加する。
+
tcatest read name
+
上記で生成したデータベースの全レコードを検索する。
+
tcatest remove name
+
上記で生成したデータベースの全レコードを削除する。
+
tcatest rcat name rnum
+
キーがある程度重複するようにレコードの追加を行い、連結モードで処理する。
+
tcatest misc name rnum
+
各種操作の組み合わせテストを行う。
+
tcatest wicked name rnum
+
各種更新操作を無作為に選択して実行する。
+
tcatest compare name tnum rnum
+
各種データベースの比較テストを行う。
+
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +

コマンド `tcamttest' は、抽象データベースAPIの機能テストをマルチスレッドで行うツールです。以下の書式で用います。`name' はデータベースの名前を指定し、`tnum' はスレッド数を指定し、`rnum' は試行回数を指定します。

+ +
+
tcamttest write name tnum rnum
+
`00000001'、`00000002' のように変化する8バイトのキーと値を連続してデータベースに追加する。
+
tcamttest read name tnum
+
上記で生成したデータベースの全レコードを検索する。
+
tcamttest remove name tnum
+
上記で生成したデータベースの全レコードを削除する。
+
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +

コマンド `tcamgr' は、抽象データベースAPIやそのアプリケーションのテストやデバッグに役立つツールです。以下の書式で用います。`name' はデータベースの名前を指定し、`key' はレコードのキーを指定し、`value' はレコードの値を指定し、`params' はチューニングパラメータを指定し、`func' は関数の名前を指定し、`arg' は関数の引数を指定し、`dest' は格納先のファイルを指定します。

+ +
+
tcamgr create name
+
データベースを作成する。
+
tcamgr inform name
+
データベースの雑多な情報を出力する。
+
tcamgr put [-sx] [-sep chr] [-dk|-dc|-dai|-dad] name key value
+
レコードを追加する。
+
tcamgr out [-sx] [-sep chr] name key
+
レコードを削除する。
+
tcamgr get [-sx] [-sep chr] [-px] [-pz] name key
+
レコードの値を取得して標準出力する。
+
tcamgr list [-sep chr] [-m num] [-pv] [-px] [-fm str] name
+
全てのレコードのキーを改行で区切って標準出力する。
+
tcamgr optimize name params
+
データベースを最適化する。
+
tcamgr misc [-sx] [-sep chr] [-px] name func [arg...]
+
雑多な操作の多目的関数を呼び出す。
+
tcamgr map [-fm str] name dest
+
レコードを別のB+木データベース内に写像する。
+
tcamgr version
+
Tokyo Cabinetのバージョン情報を標準出力する。
+
+ +

各オプションは以下の機能を持ちます

+ +
    +
  • -sx : 入力を16進数の文字列で行う。
  • +
  • -sep chr : 入力文字列の区切り文字を指定する。
  • +
  • -dk : 関数 `tcadbput' の代わりに関数 `tcadbputkeep' を用いる。
  • +
  • -dc : 関数 `tcadbput' の代わりに関数 `tcadbputcat' を用いる。
  • +
  • -dai : 関数 `tcadbput' の代わりに関数 `tcadbaddint' を用いる。
  • +
  • -dad : 関数 `tcadbput' の代わりに関数 `tcadbadddouble' を用いる。
  • +
  • -px : 出力を16進数の文字列で行う。
  • +
  • -pz : 出力の末尾に改行を付加しない。
  • +
  • -m num : 出力の最大数を指定する。
  • +
  • -pv : レコードの値も出力する。
  • +
  • -fm str : キーの接頭辞を指定する。
  • +
+ +

このコマンドは処理が正常に終了すれば 0 を返し、エラーがあればそれ以外の値を返して終了します。

+ +

CGI

+ +

抽象データベースAPIを簡単に利用するために、コモンゲートウェイインタフェースとして `tcawmgr.cgi' が提供されます。

+ +

CGIスクリプト `tcawmgr.cgi' は、Webインターフェイスで抽象データベースの内容を閲覧したり編集したりするのに役立つツールです。操作対象のデータベースは、このCGIスクリプトのカレントディレクトリに "casket.tch" または "casket.tcb" または "casket.tcf" という名前で設置されている必要があります。また、そのパーミッションにおいてCGIスクリプトの実行ユーザに対する読み込みと書き込みが可能になっていることが必要です。このCGIスクリプトをWebサーバの公開ディレクトリに設置したら、割り当てられたURLにWebブラウザでアクセスすると利用を開始することができます。

+ +
+ +

ちょっとしたコツ

+ +

この節ではTokyo Cabinetの使い方のコツや知っておくと便利な小技を紹介します。

+ +

ユーティリティAPI

+ +

C++、Perl、Ruby、Javaといった高水準な言語では必ずといってリストやマップといったデータ構造を簡単に利用できる機能が標準ライブラリとしてついてきます。しかし、C言語にはそれに相当するものはありません。GNOME GlibやApache APRなどの非標準ライブラリを使うのも一興ですが、Tokyo Cabinetにも高機能・高性能なユーティリティが付属しています。STL(C++の標準テンプレートライブラリ)のstringにあたるものがTCXSTRで、listにあたるものがTCLISTで、mapやsetにあたるものがTCMAPとTCTREEです。他にも文字列処理や各種符号処理のユーティリティも提供されます。それらを使いこなすとC言語でもC++やその他の高水準言語並みの直感的なプログラミングができるでしょう。

+ +

TCXSTRの何が便利かと言えば、`tcxstrcat' です。特にバッファリングに有用で、後ろにデータをどんどんくっつけていけるのです。メモリ領域は内部で適宜拡張してくれるので、アプリケーション側でメモリ管理に悩む必要はありませんし、性能もかなり良いです。

+ +

TCLISTは配列で実装されたリストです。これはスタック(`tclistpush' で格納して `tclistpop' で取り出す)としてもキュー(`tclistpush' で格納して `tclistshift' で取り出す)としても使えます。もちろんメモリ管理は内部でよろしくやってくれますし、性能もかなり良いです。

+ +

TCMAPはハッシュ表によるマップ(連想配列)の実装です。任意のキーに対応づけて任意の値を格納できます。ハッシュデータベースのオンメモリ版と考えてもよいでしょう。TCMAPのイテレータはレコードを格納した順番に取り出すことができるというのが特徴で、かつ任意のレコードを先頭や末尾に移動させることもできるので、LRU消去方式のキャッシュとしても利用することができます。もちろんメモリ管理は内部でよろしくやってくれますし、性能もかなり良いです。

+ +

TCTREEは順序木によるマップ(連想配列)の実装です。任意のキーに対応づけて任意の値を格納できます。B+木データベースのオンメモリ版と考えてもよいでしょう。TCTREEのイテレータはレコードを比較関数の昇順に取り出すことができるというのが特徴で、かつイテレータを任意の場所に飛ばすことができるので、文字列の前方一致検索や数値の範囲を行うことができます。もちろんメモリ管理は内部でよろしくやってくれますし、性能もかなり良いです。

+ +

TCXSTRとTCLISTとTCMAPとTCTREEの各関数はリエントラントですが、該当のオブジェクトを複数のスレッドで共有する場合にはアプリケーション側で排他制御を行うことが求められます。ただし、ハッシュマップと順序木に関しては排他制御を内部で行う実装としてTCMDBとTCNDBが提供されます。

+ +

TCMPOOLというのもあります。これはいわゆるメモリプールの実装で、メモリ管理の単位を一括して楽をすることができる機能です。例えば `malloc で確保した領域は必ず `free' で解放しないとメモリリークになってしまいますが、`tcmpoolmalloc' で確保した領域は明示的に解放しないでよいのです。ではいつ解放されるのかと言えば、メモリプール自体を解放した時です。つまりアプリケーション側ではメモリプールの寿命にだけ気を付ければよく、個々のオブジェクトの寿命を気にしなくてもよくなるということです。メモリプールはTCXSTRやTCLISTやTCMAPやTCTREEのオブジェクトを発生させることもできますし、任意のオブジェクトをデストラクタとともに登録することもできます。典型的には以下のような使い方をします。

+ +
TCMPOOL *mpool;
+int i, j;
+char *buf;
+for(i = 0; i < 100; i++){
+  mpool = tcmpoolnew();
+  for(j = 0; j < 100; ++){
+    buf = tcmpoolmalloc(10); // メモリプール内オブジェクトの生成
+    ...                      // いちいち解放しなくてOK
+  }
+  tcmpooldel(mpool);         // ここで一気に解放
+}
+
+ +

ハッシュデータベースのチューニング

+ +

チューニングをするかしないかでデータベース操作の性能は劇的に変わるので、まじめなユースケースでは、チューニングは必須となるでしょう。関数 `tchdbtune' でそれを行います。この関数では「バケット数」と「アラインメント力」と「フリーブロックプール力」と「オプション」が指定されます。

+ +

最も重要なのは、バケット数の設定です。これは、データベースに格納するレコードの最終的な数の数倍(2〜4倍程度がオススメ)を指定すべきです。デフォルトは131071なので、100000個以上のレコードを入れるならばまずこれを設定すべきです。例えば100万レコードくらいを入れる予定ならば、バケット数は200万〜400万くらいにしておくとよいでしょう。バケット配列の個々の要素のサイズは4バイト(32ビット)なので、バケット数を200万にした場合にはファイルサイズが8MB増えて、メモリも8MB必要となるわけですが、21世紀のコンピュータならそれくらい大したことないでしょう。とりあえずバケット数は大きめにとりましょう。

+ +

アラインメントは、レコードの開始位置を揃える機構です。指定したアラインメント力で1を高位にビットシフトした数に開始アドレスが揃えられます。デフォルトは4です。例えばアラインメント力を8にしたならば、1<<8で、256の倍数に開始位置が揃えられます。アラインメントの利点は三つあります。一つめは、開始アドレスを揃えることでレコード間にパディング(隙間)ができることです。レコードサイズの増減がパディングの範囲に収まれば、更新時にレコードの位置を変えなくてもよくなります。二つめは、レコードの読み書きをファイルシステムのブロック単位にあわせて行うことができるために、OSレベルでのI/Oの処理が効率化されることです。三つめは、開始アドレスをアラインメントの商として記録できるようになるため、4バイトのバケットで表せる変域が増加することです。アラインメントを用いない場合は2GB(1<<31)までのデータベースファイルしか扱えませんが、例えばアラインメントが256であれば、2GB*256で512GBまでのデータベースファイルを扱うことができます。

+ +

フリーブロックとは、更新によってできたファイル内の未使用領域のことです。フリーブロックプールはそれを管理して再利用する機構です。指定したフリーブロックプール力で1を高位にビットシフトした数がフリーブロックプールの容量になります。デフォルトは10です。この設定を変える必要はほとんどないでしょう。

+ +

オプションとは、レコードの格納方法を指定するフラグの集合のことです。`HDBTLARGE' と `HDBTDEFLATE' と `HDBTBZIP' と `HDBTTCBS' と `HDBTEXCODEC' の論理和で指定します。`HDBTLARGE' を指定すると、バケットの個々の要素を8バイト(64ビット)で扱います。バケット配列のサイズが2倍になるかわりに、データベースのサイズの上限を8EBに引き上げます。`HDBTDEFLATE' を指定すると、レコードをDeflateアルゴリズムで圧縮してから記録します。大きいサイズ(だいたい256バイト以上)のレコードを圧縮して格納する場合に有利です。`HDBTBZIP' を指定すると、レコードをBZIP2アルゴリズムで圧縮して格納します。Deflateよりは遅いですが、圧縮率は有利です。`HDBTTCBS' を指定すると、レコードをBWT、MTF、Elias Gamma符号で圧縮して格納します。小さいサイズ(256バイト未満)のレコードを圧縮して格納する場合に有利です。`HDBTEXCODEC' は外部の圧縮伸長アルゴリズムを使うためのオプションです。具体的なアルゴリズムは隠しAPIの関数 `tchdbsetcodecfunc' で指定します。

+ +

チューニングパラメータの設定はデータベースを作成する前に行う必要があります。チューニングパラメータはメタデータとしてデータベース内に記録されるので、作成した後は指定する必要はありません。なお、いったん作成したデータベースのチューニングを変更することはできません(最適化すればできますが)。バケット数を1000000、アラインメント数を12(4096)、フリーブロックをデフォルト、オプションを `HDBTLARGE' と `HDBTDEFLATE' に指定してデータベースを作成する場合、以下のようなコードになります。

+ +
TCHDB *hdb;
+hdb = tchdbnew();
+tchdbtune(hdb, 1000000, 12, -1, HDBTLARGE | HDBTDEFLATE);
+tchdbopen(hdb, "casket.tch", HDBOWRITER | HDBOCREAT);
+...
+
+ +

ハッシュデータベースはキャッシュ機構を備えます。これは一旦検索されたレコードをメモリ上に保持しておくもので、同一のレコードが何度も検索される場合の性能を向上させてくれます。キャッシュ上にあるレコードが更新された場合、そのレコードはキャッシュから削除されますので、検索の頻度よりも更新の頻度が多い場合にはあまり効果はありません。また、キャッシュを有効にするとキャッシュを管理するためのオーバーヘッドがかかるので、キャッシュのヒット率がある程度以上でないと逆に処理が遅くなってしまいます。したがって、キャッシュのヒット率がかなり高い場合(つまり同じレコードを何度も参照するような場合)にのみキャッシュ機構を利用すべきです。ハッシュデータベースのキャッシュはデフォルトでは無効になっていますので、有効にする場合は関数 `tchdbsetcache' で設定してください。キャッシュパラメータの設定はデータベースに接続する前に行う必要があり、また接続する度に毎回行う必要があります。

+ +

ハッシュデータベースはmmapを介してファイル入出力を行うための拡張マップメモリという機構を備えます。これは、デフォルトでmmapによってマップされるバケット配列とは別に、レコード用の領域をmmapでメモリにマップしたものです。mmapを介したファイル入出力はpreadやpwriteを使った入出力よりも高速で、並列処理性能も高いという利点もあります。その反面、データベースを開いた瞬間に拡張マップメモリとして指定したサイズの領域が仮想メモリ空間に確保され、そのサイズが実メモリの利用可能量を上回った場合にはスワップが発生してしまいます。デフォルトでは64MBの拡張マップメモリが利用されますが、想定されるデータベースファイルがそれより大きくて実メモリ容量よりも小さいような場合は、データベースサイズよりも少し大きいくらいの拡張マップメモリを指定するとよいでしょう。拡張マップメモリのサイズは関数 `tchdbsetxmsiz' で指定してください。拡張マップメモリのパラメータの設定はデータベースに接続する前に行う必要があり、また接続する度に毎回行う必要があります。

+ +

レコードの削除を頻繁に行ったり、値の長さを変えるような更新を頻繁に行ったりする場合は、フリーブロックプールを使ったとしても少しずつ断片化が起こってしまいます。断片化が進むとデータベースファイルのサイズが肥大化してきますが、それを解消してファイルサイズを小さくするためにはデフラグと呼ばれる操作を行うことになります。デフラグの最も簡単な方法は、関数 `tchdboptimize' によってデータベースに最適化をかけることです。これはデータベース全体を一気に作り直すことで断片化を解消します。もう一つの方法は、関数 `tchdbsetdfunit' で自動デフラグ設定をしてから更新を行うことです。そうすると、断片化が発生する度に動的に少しずつ最適化処理を行うようになるので、性能が少し犠牲になりますが、見掛け上は肥大化がほとんど発生しないようになります。この関数のパラメータとして指定する単位ステップ数とは、何個の領域の断片化を検出したらデフラグ操作を行うかを指定するものです。この数を増やした方が処理効率は上がりますが、デフラグ操作を行っている間のロックの粒度が上がるので増やしすぎるのも考え物です。通常は8くらいにしておくとよいでしょう。自動デフラグのパラメータの設定はデータベースに接続する前に行う必要があり、また接続する度に毎回行う必要があります。

+ +

B+木データベースのチューニング

+ +

チューニングをするかしないかで性能が劇的に変わるのはB+木データベースについても同じです。まじめなユースケースではちゃんとチューニングしましょう。チューニングは関数 `tcbdbtune' で行います。この関数では「リーフ内メンバ数」「非リーフ内メンバ数」「バケット数」と「アラインメント力」と「フリーブロックプール力」と「オプション」が指定されます。

+ +

リーフまたはリーフページとは、B+木の末端のノードのことで、複数のレコードのキーと値のリストが格納される記憶単位のことです。リーフ内メンバ数とは、一つのリーフの中にいくつのレコードを格納するかの設定です。デフォルトは128です。比較関数の順序通りにレコードを格納または探索することが多い場合はこの値を大きくした方が性能がよくなり、逆に比較関数の順序とは無関係にレコードを格納または探索することが多い場合は小さくした方がよくなります。非リーフまたは非リーフページとはB+木の末端以外のノードのことで、複数のレコードのキーのみが格納される記憶単位のことです。非リーフの数はリーフに比べて少なく、性能に与える影響はあまり大きくありません。非リーフ内メンバ数をデフォルトから変える必要はほとんどないでしょう。

+ +

バケット数やその他のパラメータ、B+木データベースの下層にあるハッシュデータベースにそのまま渡されます。B+木の各ページはハッシュデータベースのレコードとして記録されるので、バケット数などのパラメータはその際に意味を持ちます。したがって、ここで指定するバケット数は、B+木データベースにおける最終的なレコード数をリーフ内メンバ数で割った値の数倍に設定するのが最善です。とはいえB+木データベースにおいてはバケット数などのパラメータを変更する必要はあまりないでしょう。

+ +

チューニングの例として、平均8バイトのキーと平均32バイトの値のレコードを100万件格納することを考えてみます。各コードのヘッダなどのオーバーヘッドは5バイト程度です。ファイルシステムのブロックサイズは4096バイトとします。すると、1ブロックに入れられるレコード数は4096/(8+32+5)で90個ほどということになります。さらに、Deflate圧縮オプションを有効にして、その圧縮率が50%ほどだとしましょう。となると180個ほどのレコードが1ブロックに収まることが期待されます。各リーフのサイズは2ブロックか3ブロックのサイズが望ましいので、180を2倍した360がリーフ内メンバ数の理想値になります。となると、バケット数は1000000/360で2777となり、デフォルトの32749から変える必要はないでしょう。アラインメント力はファイルシステムのブロックサイズにあわせるためにlog2(4096)で12にします。以上の設定をコードに反映すると以下のようになります。

+ +
TCHDB *bdb;
+bdb = tcbdbnew();
+tcbdbtune(hdb, 360, -1, -1, 12, -1, BDBTDEFLATE);
+tcbdbopen(hdb, "casket.tcb", BDBOWRITER | BDBOCREAT);
+...
+
+ +

B+木データベースもキャッシュ機構を備えます。これは処理対象のページをメモリ上に保持しておくもので、同一のページが何度も読み書きされる場合の性能を向上させてくれます。キャッシュ上にあるページが更新された場合でも、そのページはメモリ上に保持されたままなので、検索も更新も高速化されます。B+木データベースのキャッシュはデフォルトでは小さめに設定されていますので、メモリを多く使っても高速化したい場合は関数 `tcbdbsetcache' で設定してください。キャッシュパラメータの設定はデータベースに接続する前に行う必要があり、また接続する度に毎回行う必要があります。

+ +

B+木データベースでも拡張マップメモリを利用することができます。しかし、B+木のキャッシュ機構がファイル入出力のバッファリングの役目を果たしているので、デフォルトでは無効になっています。メモリ利用効率は無視してとにかくスループットを追求したい場合のみ、関数 `tcbdbsetxmsiz' で拡張マップメモリを有効化してください。拡張マップメモリのパラメータの設定はデータベースに接続する前に行う必要があり、また接続する度に毎回行う必要があります。

+ +

B+木データベースでも断片化は起きうるので、デフラグをかけるのはよい考えです。静的な最適化は関数 `tcbdboptimize' で行い、自動デフラグの設定は関数 `tcbdbsetdfunit' で行います。B+木データベースではハッシュデータベースよりもI/Oの粒度が大きいので、自動デフラグを行う際の単位ステップ数は2くらいにしておくとよいでしょう。自動デフラグのパラメータの設定はデータベースに接続する前に行う必要があり、また接続する度に毎回行う必要があります。

+ +

システム設定

+ +

ハードウェアやOSなどの「システム側」の設定も、データベースの操作を高速化するためには重要です。まず、できれば、データベースのサイズと同等以上のRAMをマシンに搭載してください。そして、I/Oバッファのサイズを大きくし、ダーティバッファをフラッシュする頻度が少なくするように設定してください。そうすることによって、デバイスアクセスの頻度を最小化し、I/Oの待ち時間による性能劣化を抑止できます。Linux上では、`sysctl' コマンドや `/etc/sysctl.conf' ファイルでそれらの設定を行うことになるでしょう。ファイルシステムの選択も重要です。Linux上では、通常はEXT2が最高速ですが、EXT3のwritebackモードの方が速いこともあります。ReiserFSもかなり高速です。EXT3のその他のモードはかなり遅いです。他のファイルシステムに関しては各自で実験してみてください。

+ +

補助記憶装置にHDD(ハードディスクドライブ)でなくSSD(ソリッドステートドライブ)を使うというのもよい考えです。HDDはシーケンシャルアクセスの速度に比べてランダムアクセスが桁違いに遅くなる傾向にあり、ランダムアクセスを頻繁に行うDBMのストレージとしては不向きなのです。それに対してSSDはランダムアクセスの速度があまり劣化しないので、DBMのストレージとしては最適です。HDDよりバイト毎の単価がかなり高いSSDですが、とっても速くて便利なのでぜひ導入を検討してください。また、購入する製品を選択する際には、カタログに書いてある転送スループットに惑わされてはいけません。それはシーケンシャルアクセスの性能を示しているだけだからです。そうでなく、ランダムアクセスの性能をWebなどで調べて、それが良いものを選んでください。

+ +

マルチスレッド対応

+ +

Tokyo CabinetのAPIにおける各関数はリエントラントなので、引数として与えるデータが各スレッドで別々のものであれば完全に並列に操作を実行することができます。しかし、データベースオブジェクトは内部状態を持つので、一つのデータベースオブジェクトを複数のスレッドで共有する場合には、更新操作に関連して排他制御を行う必要があります。とはいえ、特に難しいことはありません。複数のスレッドで共有するデータベースオブジェクトに対して、作成した直後に関数 `tchdbsetmutex' や `tcbdbsetmutex' を呼び出すだけでOKです。そうすると以後の操作の内部で適切にロックを用いて排他制御が行われるようになります。複数のスレッドを使うが各々が別個のデータベースオブジェクトにアクセスする場合には排他制御は必要ありませんし、排他制御をしない方が高速に動作します。

+ +

スレッド間の排他制御はリードライトロックで行われます。`open'、`close'、`put'、`out' などの操作にはライトロック(排他ロック)がかけられ、`get'、`curkey'、`curval' などの操作にはリードロック(共有ロック)がかけられます。ロックの単位は、ハッシュデータベースではレコード単位で、B+木データベースではデータベース単位になります。同一のロックに対する読み込みは激しく同時に行えますが、書き込みをしている間は他のスレッドはブロックされます。排他制御の設定はデータベースに接続する前に行う必要があり、また接続する度に毎回行う必要があります。以下のようなコードになります。

+ +
TCHDB *hdb;
+hdb = tchdbnew();
+tchdbsetmutex(hdb);
+tchdbopen(hdb, "casket.tch", HDBOWRITER);
+...
+
+ +

トランザクション

+ +

ファイル上の(オンメモリでない)データベースにはトランザクション機構があります。トランザクションを開始してから行った一連の操作は、コミットすることで確定させたり、アボートすることでなかったことにしたりすることができます。トランザクション中にアプリケーションがクラッシュした場合にも、トランザクション中の操作がなかったことになるだけで、データベースの整合性は維持されます。トランザクションは以下のようなコードで用います。

+ +
tchdbtranbegin(hdb);
+do_something();
+if(is_all_ok){
+  tchdbtrancommit(hdb);
+} else {
+  tchdbtranabort(hdb);
+}
+
+ +

トランザクションを実行できるのは同時1スレッドのみで、他のスレッドはその間にトランザクションを開始しようとするとブロックされます。したがって、データベースの参照をトランザクション内でのみ行うならば、トランザクションの分離レベルは直列化可能(serializable)になります。しかし、あるスレッドがトランザクションの最中でも他のスレッドはトランザクションを実行せずにデータベースを参照できます。その場合の分離レベルは非コミット読み取り(read uncommitted)になります。状況に応じて使い分けてください。

+ +

トランザクション機構は、ハッシュデータベースではファイル上のログ先行書き込み(write ahead logging)によって実現され、B+木データベースではメモリ上のシャドウページング(shadow paging)によって実現されます。これらの手法とロックによって、データベース単位のACID属性(atomicity、consistency、isolation、durability)が確保されます。

+ +

ファイルシステムのdurabilityすらも信用しない場合(突然の電源切断に耐える確率を上げたい場合)には、データベースを開く際に `HDBOTSYNC' または `BDBOTSYNC' オプションをつけてください。そうすると、すべてのトランザクションの前後にfsyncで更新内容とディスクの内容の同期がとられるようになります(めちゃくちゃ遅くなりますが)。とはいえ、いかにトランザクションを使ってもディスクが壊れたらオシマイなので、重要なデータベースに関してはバックアップや冗長化の手法を適用してください。

+ +

カーソル

+ +

B+木データベースにはカーソル機構があります。カーソルは指定したキーの場所にジャンプさせることができ、そこから前後に一つずつずらしながらレコードを参照したり更新したりすることができます。例えば文字列の前方一致検索を行う場合、接頭辞をキーとして指定してカーソルをジャンプさせて、そこから前に進みながらキーを一つ一つ参照していって、前方一致しなかった時点で止めるという処理になります。例えば "tokyo" で始まるキーのレコードを取り出すには以下のようなコードになるでしょう。

+ +
cur = tcbdbcurnew();
+tcbdbcurjump2(cur, "tokyo");
+while((key = tcbdbcurkey2(cur)) != NULL){
+  if(!tcstrfwm(kbuf, "tokyo")){
+    free(key);
+    break;
+  }
+  if((val = tcbdbcurval2(cur)) != NULL){
+    do_something(key, val);
+    free(val);
+  }
+  free(key);
+  tcbdbcurnext();
+}
+tcbdbcurdel(cur);
+
+ +

カーソルをジャンプさせてから、他のスレッドが同一のデータベースに対して更新を行った場合、そのカーソルの位置はずれる可能性があります。具体的には、カーソルのあるリーフ上でカーソルより前にレコード挿入された場合、カーソルは小さい方向に一つずれます。また、カーソルのあるリーフ上でカーソルより前にあるレコードが削除された場合、カーソルは大きい方向に一つずれます。したがって、検索などの非クリティカルな操作では特別な配慮は必要ありませんが、更新にカーソルを使う場合には、処理中にカーソルの位置がずれないようにトランザクションを使うか、アプリケーション側の責任で排他制御をすることになるでしょう。なお、典型的な検索操作である範囲検索をアトミックに行うために関数 `tcbdbrange' および関数 `tcbdbfwmkeys' が提供されています。

+ +

バックアップ

+ +

データベースファイルのバックアップは、通常のファイルと同様にcpやtarやcpioといったコマンドで行うことができます。ただし、ライタとして接続しているプロセスがデータベースを更新中である場合、コピー元のファイルの状態が中途半端になっている可能性があるため、コピー先のファイルに不整合が起きる場合があります。したがって、データベースが更新中でないこと確認してからバックアップ作業を行うことが必要となります。

+ +

デーモンプロセスなどの常駐プロセスがデータベースに接続し続けるユースケースでは上記の手順は現実的ではありません。そういった場合、その常駐プロセスの責任でバックアップ処理を駆動することができます。関数 `tchdbcopy' や `tcbdbcopy' を呼び出すと、更新内容をデータベースファイルと同期させた上で、その間にファイルの複製を行います。

+ +

バックアップ用関数は任意のコマンドを呼び出すこともできます。コピー先のファイル名の代わりに "@" で始まるコマンド名を指定するとそれが呼び出されます。そのコマンドの第1引数にはデータベース名が指定され、第2引数には現在のUNIX時間のマイクロ秒が指定されます。例えば、以下のようなシェルスクリプトを用意してそれを呼び出すようにするとよいでしょう。

+ +
#! /bin/sh
+srcpath="$1"
+destpath="$1.$2"
+rm -f "$destpath"
+cp -f "$srcpath" "$destpath"
+
+ +

バックアップ用のコマンドを実行している間はそのデータベースの更新はブロックしますので、コピーに時間がかかる場合には留意が必要です。無停止のホットバックアップを望むならば、"cp" などによる単純なファイル複製の代わりにファイルシステム(LVM)のスナップショット機能を使うとよいでしょう。

+ +

ハッシュデータベースとB+木データベースの比較

+ +

キーと値のペアを格納したいというのははっきりしているが、ハッシュデータベースとB+木データベースのどちらを使えばよいかわからないという場合もあるかもしれません。その場合、レコードの検索条件が完全一致だけで済むのなら、ハッシュデータベースを試してください。レコードを順序に基づいて参照したいなら、B+木データベースを試してください。メモリ上だけ保持してファイルに書き出す必要がないならば、ユーティリティAPIのハッシュマップを試してください。

+ +

検索条件が完全一致の場合にはハッシュデータベースを使うのが一般的ですが、B+木でも完全一致検索はできます。ファイルシステムのI/Oキャッシュに乗らない大規模のデータベースでは、ハッシュデータベースとB+木データベースの性能特性を考えて、使うデータベースの種類を選択することが重要です。

+ +

ハッシュデータベースのキャッシュ機構はレコード単位ですが、B+木データベースはキャッシュ機構はページ単位であるというのが性能上の最大の留意点です。B+木データベースにおいては、データベース内の全てのレコードはキーの昇順で並べられ、順番が近いレコードをページにまとめて管理します。キャッシュやI/Oはページを単位として行います。したがって、順番が近いレコードを参照する場合にはキャッシュがヒットしてI/Oを伴わずに操作が完結するので効率がよくなります。ということは、多数のレコードを格納する際に、対象のレコード群をキーの昇順でソートしてからデータベースに格納すると、I/Oの回数が最小化されて時間効率も空間効率も最高になります。これはアプリケーション層でもキャッシュ機構を持つことを要求するものですが、至高を求めるあなたには不可能ではないはずです。全文検索システムHyper Estraierのインデクシングが高速な秘訣はまさにここにあります。

+ +

逆に考えれば、データベースにアクセスする順序が制御できない場合は、B+木データベースよりもハッシュデータベースを使う方が有利ということになります。キャッシュに乗らない場合には、ハッシュデータベースの方がメモリ使用量も小さく、個々のレコードを取り出す際の計算量も小くて済みます。なお、ハッシュデータベースの構築時に一気にレコードを入れるような用途の場合には、非同期モードを使うとB+木データベース以上の更新性能を実現できます。新しいレコードはファイルの末尾に記録されることを利用して、ファイルの末尾部分に特化したキャッシュを作ることができるからです。

+ +

テーブルデータベースの仕組み

+ +

テーブルデータベースは、リレーショナルデータベースのテーブルのように、複数の列からなるレコードを格納できるデータベースです。ハッシュデータベースのように主キーでレコードを識別しながらも、リレーショナルデータベースのようにレコード内に名前をつけた複数のコラムを持たせることができます。ハッシュデータベースと違って、レコード内の個々のコラムの値を条件にしてレコードの集合を問い合わせることができます。リレーショナルデータベースとは違って、あらかじめスキーマを定義する必要がなく、レコード毎に異なる種類のコラムを持たせることができます。

+ +

レコードの検索は、合致条件や順序指定を組み合わせたクエリオブジェクトをデータベースに渡すことで実行されます。合致条件の演算子には以下のものがあります。コマンドラインでは、「TDBQC」の部分を省いた文字列を用います。合致条件の真偽を反転させるには、各演算子と `TDBQCNEGATE' のビット和を用います(コマンドラインでは "~" を接頭させます)。

+ +
    +
  • TDBQCSTREQ : 右辺の文字列が完全一致する
  • +
  • TDBQCSTRINC : 右辺の文字列を含む
  • +
  • TDBQCSTRBW : 右辺の文字列で始まる
  • +
  • TDBQCSTREW : 右辺の文字列で終わる
  • +
  • TDBQCSTRAND : 右辺の文字列内の空白またはコンマ区切りの文字列の全てを含む
  • +
  • TDBQCSTROR : 右辺の文字列内の空白またはコンマ区切りの文字列のいずれかを含む
  • +
  • TDBQCSTROREQ : 右辺の文字列内の空白またはコンマ区切りの文字列のいずれかと完全一致する
  • +
  • TDBQCSTRRX : 右辺の文字列の正規表現と一致する
  • +
  • TDBQCNUMEQ : 右辺の数値と一致する
  • +
  • TDBQCNUMGT : 右辺の数値より大きい
  • +
  • TDBQCNUMGE : 右辺の数値と同じかより大きい
  • +
  • TDBQCNUMLT : 右辺の数値より小さい
  • +
  • TDBQCNUMLE : 右辺の数値と同じかより小さい
  • +
  • TDBQCNUMBT : 右辺の空白またはコンマ区切りの2つの数値の間である
  • +
  • TDBQCNUMOREQ : 右辺の空白またはコンマ区切りの数値のいずれかと一致する
  • +
  • TDBQCFTSPH : 右辺の文字列を用いてフレーズ検索の全文検索を行う
  • +
  • TDBQCFTSAND : 右辺の空白またはコンマ区切りの文字列を用いてAND検索の全文検索を行う
  • +
  • TDBQCFTSOR : 右辺の空白またはコンマ区切りの文字列を用いてOR検索の全文検索を行う
  • +
  • TDBQCFTSEX : 右辺の文字列を用いて複合検索式の全文検索を行う
  • +
+ +

順序指定の演算子には以下のものがあります。デフォルトは順序不定です。

+ +
    +
  • TDBQOSTRASC : 文字列の辞書順の昇順
  • +
  • TDBQOSTRDESC : 文字列の辞書順の降順
  • +
  • TDBQOSTRASC : 数値の昇順
  • +
  • TDBQOSTRDESC : 数値の降順
  • +
+ +

`TDBQCSTRAND' と `TDBQCSTROR' は、いわゆるタグ検索のための演算子です。タグ検索とは、空白またはコンマで区切られたトークンをタグとみなして、そのタグが存在するか否かを判定してレコードを探す操作です。`TDBQCSTRINC' のように任意の部分文字列を探すのではなく、トークン単位の完全一致を判定します。タグ検索を高速化するには後述のトークン転置インデックスを張ることが推奨されます。`TDBQCSTROREQ' 演算子もタグ検索に使うことができますが、SQLのIN演算子と同じように、検索されるレコードのコラムには単一のトークンしか含まれてはならないという制約があります。その分、通常の文字列型のインデックスが効くという利点があります。

+ +

`TDBQCFTSPH' と `TDBQCFTSAND' と `TDBQCFTSOR' と `TDBQCFTSEX' は、いわゆる全文検索のための演算子です。全文検索は前述のタグ検索と違って、空白やコンマの区切りを単位としない任意の部分文字列の一致を判定できる点で `TDBQCSTRINC' に類似しています。ただし、大文字小文字やアクセントマークなどの違いを吸収するため、ユーザが入力した任意のテキストを検索するのに便利です。全文検索を実用的な速度で動作させるためには後述のq-gram転置インデックスを張っておくことが必要です。`TDBQCFTSEX' 演算子で用いる複合検索式においては、空白で区切って複数のトークンを指定すると、その全てのトークンを含むというAND条件で検索できます。空白および「&&」で区切っても同じ意味になります。空白および「||」で区切ると、両辺のトークンのどちらかを含むというOR条件で検索できます。トークンに空白を含めたい場合は「""」で括ります。演算子の結合優先順位は「""」「||」「&&」の順になります。同一順位の演算子は左結合で評価されます。

+ +

テーブルデータベースのインデックス

+ +

テーブルデータベースを検索する際に、合致条件の判定や順序指定によるソートを高速化するために、任意のコラムを対象としてインデックスを張ることができます。コラムには型がありませんが、インデックスには型があります。文字列型の演算を高速化させたい場合は文字列型のインデックスを、数値型の演算子を高速化させたい場合は数値型のインデックスを、トークン型の演算子を高速化させたい場合はトークン転置インデックスを張ることになります。ただし、型が異なる場合でもインデックスを張っておくと、メインのハッシュデータベースの全表スキャンの代わりに、それよりは小さいインデックスの全表スキャンを用いるので、計算量は同じですが処理時間は短くなります。

+ +
    +
  • 文字列型インデックス(TDBITLEXICAL):
      +
    • 計算量が小さくなる演算子:TDBQCSTREQ、TDBQCSTRBW、TDBQCSTROREQ
    • +
    • 計算量は同じだが高速化する演算子:TDBQCSTRINC、TDBQCSTREW、TDBQCSTRAND、TDBQCSTROR、TDBQCSTRRX、TDBQCNUMEQ、TDBQCNUMGT、TDBQCNUMGE、TDBQCNUMLT、TDBQCNUMLE、TDBQCNUMBT、TDBQCNUMOREQ
    • +
    • 計算量が小さくなる順序指定:TDBQOSTRASC、TDBQOSTRDESC
    • +
  • +
  • 数値型インデックス(TDBITDECIMAL):
      +
    • 計算量が小さくなる演算子:TDBQCNUMEQ、TDBQCNUMGT、TDBQCNUMGE、TDBQCNUMLT、TDBQCNUMLE、TDBQCNUMBT、TDBQCNUMOREQ
    • +
    • 計算量は同じだが高速化する演算子:TDBQCSTREQ、TDBQCSTRBW、TDBQCSTROREQ、TDBQCSTRINC、TDBQCSTREW、TDBQCSTRAND、TDBQCSTROR、TDBQCSTRRX
    • +
    • 計算量が小さくなる順序指定:TDBQONUMASC、TDBQONUMDESC
    • +
  • +
  • トークン転置インデックス(TDBITTOKEN):
      +
    • 計算量が小さくなる演算子:TDBQCSTRAND、TDBQCSTROR
    • +
  • +
  • q-gram転置インデックス(TDBITQGRAM):
      +
    • 計算量が小さくなる演算子:TDBQCFTSPH、TDBQCFTSAND、TDBQCFTSOR、TDBQCFTSEX
    • +
  • +
+ +

合致条件に利用できるインデックスが複数ある場合、型が一致する最初に指定された演算子に対して適用されます。したがって、カーディナリティが高い条件を先に指定する方が効率的になります。合致条件の演算子を `TDBQCNOIDX' とのビット和にすると、その条件の判定にインデックスを適用しないようになります(コマンドラインでは "+" を接頭させます)。否定の合致条件にはインデックスは適用されません。インデックスのデータは、レコード本体を格納したデータベースファイルとは別個に、B+木データベースのファイルとして記録されます。

+ +

転置インデックスとは、検索対象の文字列がどのレコードに含まれているかを記録して効率的に探し出すためのインデックスです。トークン転置インデックスとq-gram転置インデックスの二種類の方式をサポートしています。トークン転置インデックスは、空白もしくはコンマで区切られた単語をキーにしてレコードを探すための構造で、`TDBQCSTRAND' などの演算子を高速化するので、いわゆるタグ検索などに重宝するでしょう。q-gram転置インデックスは、3文字毎の部分文字列(tri-gram)をキーにしてレコードを探すための構造で、`TDBQCFTSPH' などの演算子を高速化するので、いわゆる全文検索などに重宝するでしょう。全文検索系の演算子は大文字小文字の違いやアクセント記号の有無などを無視して検索してくれるので便利です。転置インデックス(特にq-gram転置インデックス)はサイズがかなり大きくなり、更新処理にかかるオーバーヘッドも大きくなってしまうので、ご利用は計画的にお願いします。

+ +

抽象データベース

+ +

ハッシュデーターベースかB+木データベースかを実行時に決定したい場合には、抽象データベースAPIを使うとよいでしょう。抽象データベースAPIはハッシュデータベースAPIとB+木データベースAPIの共通のインターフェイスで、関数 `tcadbopen' でデータベースを開く際のデータベース名で具体的にどの種類のデータベースを扱うかを指定することができます。ハッシュデータベースの名前には接尾辞として ".tch" をつけ、B+木データベースの名前には接尾辞として ".tcb" をつけることで区別されます。チューニングパラメータは、名前の後に "#" で区切って "name=value" の形式で指定します。例えば "casket.tch#bnum=1000000#apow=10" などとします。数値表現には "k"、"m"、"g" などの2進接頭辞を接尾させることもできます。また、データベース名の接尾辞に ".tcf" をつけると固定長データベースになります。連番のID番号をキーにして固定長のデータを管理する場合には最も効率が良くなります。

+ +

抽象データベースAPIはオンメモリハッシュデータベースやオンメモリツリーデータベースとしても利用することができます。データベース名を "*" とするとオンメモリハッシュデータベースになり、"+" とするとオンメモリツリーデータベースになります。また、それらをキャッシュとして利用したい場合は、"*#capsiz=100m" などとするとよいでしょう。キャッシュの容量を100MBに限定して、それを越えた際には格納した順序が古いレコードから自動的に消していくようになります。オンメモリハッシュデータベースとオンメモリツリーデータベースの使い分けですが、パフォーマンスを求める場合には前者を用い、メモリ効率を求めたり前方一致検索を行いたい場合には後者を用いるとよいでしょう。

+ +

DBMとして一般的でない機能は関数 `tcadbmisc' に隠蔽されています。この関数はサブ関数名を第1引数に指定し、それに応じて解釈の変わる引数リストを与えて実行します。サポートされるサブ関数は具象データベースの型によって異なります。複数のレコードを一度に扱える "putlist"、"outlist"、"getlist" は全ての具象データベースでサポートされています。その他にも様々な機能がありますが、作者の気まぐれで増えるのでここでは全てを説明できません。詳しくはソースコードをご覧ください。

+ +

抽象データベースによるテーブル操作

+ +

抽象データベースの接尾辞に ".tct" をつけるとテーブルデータベースになります。テーブルデータベースでは単一の文字列を値とする代わりにコラムの名前と値のマップが用いられます。抽象データベースでテーブルデータベースを扱う場合、単一の文字列とマップを同一のインターフェイスで扱うために、ゼロ文字('\0')を区切り文字としてコラムの名前と値を交互に並べて直列化した文字列を用います。レコードを格納する際(`tcadbput')の引数やレコードを取得する際(`tcadbget')の戻り値にはそのゼロ区切り文字列が使われます。

+ +

レコードの検索は関数 `tcadbmisc' のサブ関数 "search" で行います。引数に "addcond" か "setorder" か "setlimit" か "get" か "out" を接頭させてゼロ文字区切りで演算式を記述した文字列を与えることでクエリを組み立てます。例えば "addcond\0name\0STRBW\0john" と "setorder\0age\0NUMASC" を指定すると、「コラム "name" の値が "john" で始まるレコードをコラム "age" の数値の昇順で取り出す」というクエリになります。戻り値は該当するレコードの主キーのリストです。ただし、"get" が指定されると、コラム名と値のマップをゼロ区切り文字列で返します。"get\0name\0age" などとして特定のコラムに絞り込むこともできます。"out" が指定されると、該当のコラムを削除します。"get" と "out" を組み合わせるとキューとして利用することができます。サブ関数 "genuid" は、ユニークなID番号を採番して返します。

+ +

隠しAPI

+ +

この文書に書いてあるAPIは、全体の70%くらいです。つまり、この文書に書いていない隠しAPIが30%くらいあります。興味のある人はヘッダファイル(`tcutil.h'、`tchdb.h'、`tcbdb.h'、`tcfdb.h'、`tctdb.h'、`tcadb.h')の中身を覗いてみてください。上級者用でちょっと癖が強いけれども、使いこなすと機能や性能の面でにかなり有利になるAPIが揃っています。その中でも特に便利なのは、"putproc" 系の関数です。これはputと同様にレコードの挿入を試みるのですが、既存のレコードがあった場合にそれを引数にしてコールバック関数を呼ぶので、任意の更新操作をアトミックに行うことができます。また、"foreach" 系の関数も便利です。これはデータベース内の全てのレコードをアトミックに走査しながら、各々のレコードを引数にしてコールバック関数を呼び出します。

+ +

抽象データベースAPIの隠しAPI関数 `tcadbsetskel' は激アツです。拡張データベーススケルトンと呼ばれる構造体によって関数ポインタの集合を指定することで、抽象データベースの全てのメソッドの振る舞いをオーバーライドすることができるようになります。そうすると、DBM風のインターフェイスを持つ全てのライブラリをTokyo Cabinetと同じインターフェイスで使えるようになります。典型的には以下のような実装になります。

+ +
ADBSKEL skel;
+memset(0, &skel, sizeof(skel));
+skel.opq = mydbnew();    // レシーバオブジェクトを生成して設定
+skel.del = mydbdel;      // デストラクタをオーバーライド
+skel.open = mydbopen;    // openメソッドをオーバーライド
+skel.close = mydbclose;  // closeメソッドをオーバーライド
+...                      // その他、好きなメソッドをオーバーライド
+TCADB *adb = tcadbnew();
+tcadbsetskel(adb, &skel);
+tcadbopen(adb, "foobarbaz");
+...
+
+ +

拡張データベーススケルトンの一実装である「複式抽象データベース」を設定するユーティリティとして、隠しAPI関数 `tcadbsetskelmulti' が提供されます。これを適用した抽象データベースは、パラメータで指定した数にデータベースが分割されるようになりますが、具象データベースの種類に関わらず、レコードの挿入や削除などの操作を透過的に行うことができます。データベース名はファイル名ではなくディレクトリ名として扱われ、そのディレクトリの中に複数のデータベースファイルが作られます。レコードはキーのハッシュ値により分散されてどれかひとつのデータベースに格納されます。データベースを分割すると何が嬉しいかというと、データベース操作に要する排他制御の粒度がその分割数に応じて細分化することです。したがって、たとえ下層のデータベースが最適化などのグローバルなロックを要する操作を行っていたとしても、複式抽象データベースを用いていれば、スレッドがブロックする時間を分割数の逆数にまで下げることができます。ただしその代償として、CPUにオーバヘッドがかかることと、`tcadbfwmkeys' などの集合演算の結果の順序が不定になることは覚悟してください。複式抽象データベースに対して `tcadbmisc' を実行する際には、サブ関数名に "@" か "%" を接頭させて引数毎の操作対象を指定できます。"@" は全ての引数をキーとみなして、引数毎に別々の内部データベースを対象としてサブ関数を実行します。"%" は引数をキーと値のペアのリストとみなして、そのペア毎に別々の内部データベースを対象としてサブ関数を実行します。すなわち、"getlist" は "@getlist" として実行すべきで、"putlist" は "%putlist" として実行すべきです。"@" も "%" もつかない場合には各々の内部データベースに対して全ての引数を渡して該当の操作を実行します。

+ +
TCADB *adb = tcadbnew();
+tcadbsetskelmulti(adb, 8);     // 8分割の複式抽象データベースとしてマーク
+tcadbopen(adb, "casket.tch");  // ハッシュデータベースとして開く
+...
+
+ +

リモートインターフェイス

+ +

多種のアプリケーションでデータベースを共有したい場合やWebアプリケーション等でマルチプロセスの並列処理を行う場合は、Tokyo Cabinetのファイルロック機構が鬱陶しく感じるかもしれません。また、複数のマシンからデータベースを参照したい場合にはTokyo Cabinetだと困ってしまうかもしれません。

+ +

データベースの管理のみを行うサーバを別プロセスとして立ちあげて、アプリケーションのプロセスがネットワークソケットを介してそのサーバに接続すれば上記の問題は解決します。そのようなデータベースサーバとそれに接続するためのライブラリが別パッケージ「Tokyo Tyrant」として提供されています。Tokyo Tyrantのサーバは抽象データベースを扱うので、Tokyo Cabinetの全種類のデータベースをリモートインターフェイスで操作することができます。

+ +

C言語以外の言語のバインディング

+ +

PerlとRubyとJavaとLuaの言語バインディングに関しては、Tokyo Cabinetの作者が開発およびメンテナンスを行います。それ以外の言語に関しては、第三者が提供してくれることを望みます。現状では、少なくともPythonとPHPとSchemeとCommon LispとErlangとHaskellの処理系でもTokyo Cabinetを利用できるようです。

+ +

ユーザの利便性を考えると、C言語以外の言語においても、APIのシンボル名や使い方はできるだけ似通ったものにすることが望ましいでしょう。そのために、`tokyocabinet.idl' が提供されます。これはIDLで言語共通の(最大公約数的な)インターフェイスを定義したものですので、新たな言語バインディングを設計する際には、できるだけそれに準拠するようにしてください。IDLで定義されていない機能は各言語の流儀にできるだけ合わせてください。インストールの手順やドキュメントなどのパッケージの構造についても、各言語の流儀にできるだけ合わせるとよいでしょう。

+ +
+ +

ファイルフォーマット

+ +

この節ではデータベースファイルのフォーマットに関する仕様を示します。

+ +

ハッシュデータベースのファイルフォーマット

+ +

ハッシュデータベースが管理するデータベースファイルの内容は、ヘッダ部、バケット部、フリーブロックプール部、レコード部の4つに大別されます。ファイルに記録される数値は固定長数値もしくは可変長数値として記録されます。前者は数値を特定の領域にリトルエンディアンで直列化したものです。後者は数値を可変長の領域に128進法のデルタ符号で直列化したものです。

+ +

ヘッダ部はファイルの先頭から256バイトの固定長でとられ、以下の情報が記録されます。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
名前オフセットデータ長機能
マジックナンバ032データベースファイルであることの判別。「ToKyO CaBiNeT」で始まる
データベースタイプ321ハッシュ表(0x01)かB+木(0x02)か固定長(0x03)かテーブル(0x04)
追加フラグ331開きっぱなし(1<<0)、致命的エラー(1<<1)の論理和
アラインメント力341アラインメントに対する2の冪乗
フリーブロックプール力351フリーブロックプールの要素数に対する2の冪乗
オプション361ラージモード(1<<0)、Deflate圧縮モード(1<<1)、BZIP2圧縮モード(1<<2)、TCBS圧縮モード(1<<3)、外部圧縮モード(1<<4)の論理和
バケット数408バケット配列の要素数
レコード数488格納しているレコードの数
ファイルサイズ568データベースファイルのサイズ
先頭レコード648最初のレコードのオフセット
不透明領域128128ユーザが自由に使える領域
+ +

バケット部はヘッダ部の直後にバケット配列の要素数に応じた大きさでとられ、ハッシュチェーンの先頭要素のオフセットが各要素に記録されます。各要素は固定長数値で、そのサイズはノーマルモードでは4バイト、ラージモードでは8バイトです。また、オフセットはアラインメントで割った商として記録されます。

+ +

フリーブロックプール部はバケット部の直後にフリーブロックプールの要素数に応じた大きさでとられ、未使用領域のオフセットと長さが各要素に記録されます。オフセットはアラインメントで割った商に変換した上で、直前の要素の値との差分として記録されます。オフセットとサイズは可変長数値として扱われます。

+ +

レコード部はバケット部の直後からファイルの末尾までを占め、各レコードの以下の情報を持つ要素が記録されます。各レコードの領域は常にアラインメントされた位置から始まります。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
名前オフセットデータ長機能
マジックナンバ01データの識別と整合性確認に用いる。0xC8固定
ハッシュ値11チェーンの進路決定に用いるハッシュ値
左チェーン24左チェーン接続先のオフセットのアラインメント商
右チェーン64右チェーン接続先のオフセットのアラインメント商
パディングサイズ102パディングのサイズ
キーサイズ12可変キーのサイズ
値サイズ可変可変値のサイズ
キー可変可変キーのデータ
値可変可変値のデータ
パディング可変可変意味を持たないデータ
+ +

ただし、フリーブロックとなった領域には、各レコードの以下の情報を持つ要素が記録されます。

+ + + + + + + + + + + + + + + + + + + + +
名前オフセットデータ長機能
マジックナンバ01データの識別と整合性確認に用いる。0xB0固定
ブロックサイズ14ブロックのサイズ
+ +

トランザクションログはデータベース名に ".wal" を後置した名前のファイルとして記録されます。ファイルの先頭8バイトにトランザクション開始時のデータベースファイルのサイズを記録し、その後に更新操作による差分情報を持つ以下の要素を連結します。

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
名前オフセットデータ長機能
オフセット08更新された領域の先頭のオフセット
サイズ84更新された領域のサイズ
データ12可変更新される領域の更新前のデータ
+ +

B+木データベースのファイルフォーマット

+ +

B+木データベースが扱う全てのデータはハッシュデータベースに記録されます。記録されるデータは、メタデータと論理ページに分類されます。論理ページはリーフノードと非リーフノードに分類されます。固定長数値と可変長数値の形式はハッシューデータベースと同じです。

+ +

メタデータはハッシュデータベースのヘッダにおける不透明領域にとられ、以下の情報が記録されます。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
名前オフセットデータ長機能
比較関数01比較関数がtccmplexical(デフォルト)なら0x00、tccmpdecimalなら0x01、tccmpint32なら0x02、tccmpint64なら0x03、それ以外なら0xff
予約領域17現状では利用していない。
リーフ内レコード数84個々のリーフノードに入れるレコードの最大数
非リーフ内インデックス数124個々の非リーフノードに入れるインデックスの最大数
ルートノードID168B+木のルートノードのページID
先頭リーフID248先頭のリーフノードのID
末尾リーフID328末尾のリーフノードのID
リーフ数408リーフノードの数
非リーフ数488非リーフノードの数
レコード数568格納しているレコードの数
+ +

リーフノードはレコードのリストを保持し、非リーフノードはページを参照する疎インデックスを保持します。レコードはユーザデータの論理的な単位です。キーが重複する論理レコードは物理的には単一のレコードにまとめられます。物理レコードは以下の形式で直列化されます。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
名前オフセットデータ長機能
キーサイズ0可変キーのサイズ
値サイズ可変可変最初の値のサイズ
重複数可変可変キーが重複した値の数
キー可変可変キーのデータ
値可変可変最初の値のデータ
重複レコード可変可変値のサイズと値のデータのリスト
+ +

リーフノードはレコードの集合を格納するための物理的な単位です。リーフノードは1からインクリメントして振られるID番号で識別されます。リーフノードはID番号を16進数の文字列として表現したデータをキーとし、以下の値を持つレコードとしてハッシュデータベースに格納されます。レコードは常にキーの昇順に整列した状態で保持されます。

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
名前オフセットデータ長機能
前リーフ0可変直前のリーフノードのID
後リーフ可変可変直後のリーフノードのID
レコードリスト可変可変ページのレコードを直列化して連結したデータ
+ +

インデックスは子ページを探索するためのポインタの論理的な単位です。インデックスは以下の形式で直列化されます。

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
名前オフセットデータ長機能
ページID0可変参照先のページのID
キーサイズ可変可変キーのサイズ
キー可変可変キーのデータ
+ +

非リーフノードはインデックスの集合を格納するための物理的な単位です。非リーフノードは281474976710657からインクリメントして振られるID番号で識別されます。非リーフノードはID番号から281474976710657を引いた値を16進数の文字列とにした上で「#」を接頭させた文字列をキーとし、以下の値を持つレコードとしてハッシュデータベースに格納されます。インデックスは常に昇順に整列した状態で保持されます。

+ + + + + + + + + + + + + + + + + + + + +
名前オフセットデータ長機能
継承ID0可変最初の子ノードのID
インデックスリスト可変可変ページ内のインデックスを直列化して連結したデータ
+ +

固定長データベースのファイルフォーマット

+ +

固定長データベースが管理するデータベースファイルの内容は、ヘッダ部とレコード部の2つに大別されます。ファイルに記録される数値はリトルエンディアンの固定長数値として記録されます。

+ +

ヘッダ部はファイルの先頭から256バイトの固定長でとられ、以下の情報が記録されます。

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
名前オフセットデータ長機能
マジックナンバ032データベースファイルであることの判別。「ToKyO CaBiNeT」で始まる
データベースタイプ3210x03固定
追加フラグ331開きっぱなし(1<<0)、致命的エラー(1<<1)の論理和
レコード数488格納しているレコードの数
ファイルサイズ568データベースファイルのサイズ
レコード幅648各レコードの値の幅
制限サイズ728データベースファイルの制限サイズ
最小ID808現在のレコードIDの最小値
最大ID888現在のレコードIDの最大値
不透明領域128128ユーザが自由に使える領域
+ +

レコード部はヘッダ部の直後からファイルの末尾までを占め、各レコードの以下の情報を持つ要素が記録されます。値サイズに必要な領域は、レコード幅が255以下なら1バイト、65535以下なら2バイト、それを越えれば4バイトです。レコード長は値サイズに必要な領域とレコード幅を足したものです。各レコードの領域は、レコードIDから1を引いた値にレコード長を掛け、それに256を足した位置から始まります。

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
名前オフセットデータ長機能
値のサイズ0可変値のサイズ
値可変可変値のデータ
パディング可変可変値サイズが0の時は、先頭バイトの真偽値でレコードの有無を示す
+ +

トランザクションログの命名規則やフォーマットはハッシュデータベースのものと同じです。

+ +

注記

+ +

データベースファイルはスパースではないので、通常のファイルと同様に複製等の操作を行うことができます。またフォーマットも実行環境のバイトオーダに依存しないので、バイトオーダの異なる環境にデータベースファイルを移設してもそのままで利用できます。

+ +

なるべくなら、ハッシュデータベースのファイルをネットワークで配布する際には、MIMEタイプを `application/x-tokyocabinet-hash' にしてください。ファイル名の接尾辞は `.tch' にしてください。B+木データベースのファイルをネットワークで配布する際には、MIMEタイプを `application/x-tokyocabinet-btree' にしてください。ファイル名の接尾辞は `.tcb' にしてください。固定長データベースのファイルをネットワークで配布する際には、MIMEタイプを `application/x-tokyocabinet-fixed' にしてください。ファイル名の接尾辞は `.tcf' にしてください。テーブルデータベースのファイルをネットワークで配布する際には、MIMEタイプを `application/x-tokyocabinet-table' にしてください。ファイル名の接尾辞は `.tct' にしてください。

+ +

データベースファイルのマジックデータを `file' コマンドに識別させたい場合は、`magic' ファイルに以下の行を追記してください。

+ +
# Tokyo Cabinet magic data
+0       string    ToKyO\ CaBiNeT\n   Tokyo Cabinet
+>14     string    x                  \b (%s)
+>32     byte      0                  \b, Hash
+!:mime  application/x-tokyocabinet-hash
+>32     byte      1                  \b, B+ tree
+!:mime  application/x-tokyocabinet-btree
+>32     byte      2                  \b, Fixed-length
+!:mime  application/x-tokyocabinet-fixed
+>32     byte      3                  \b, Table
+!:mime  application/x-tokyocabinet-table
+>33     byte      &1                 \b, [open]
+>33     byte      &2                 \b, [fatal]
+>34     byte      x                  \b, apow=%d
+>35     byte      x                  \b, fpow=%d
+>36     byte      &1                 \b, [large]
+>36     byte      &2                 \b, [deflate]
+>36     byte      &4                 \b, [bzip]
+>36     byte      &8                 \b, [tcbs]
+>36     byte      &16                \b, [excodec]
+>40     lequad    x                  \b, bnum=%lld
+>48     lequad    x                  \b, rnum=%lld
+>56     lequad    x                  \b, fsiz=%lld
+
+ +
+ +

よく聞かれる質問

+ +
+
Q. : Tokyo CabinetはSQLをサポートしますか?
+
A. : Tokyo CabinetはSQLをサポートしません。Tokyo CabinetはRDBMS(関係データベース管理システム)ではありません。組み込みのRDBMSを求めるなら、SQLiteなどを利用するとよいでしょう。
+
Q. : Berkeley DBとどう違うのですか?
+
A. : 時間効率と空間効率の双方でTokyo Cabinetが優っています。
+
Q. : アプリケーションの良いサンプルコードはありますか?
+
A. : 各APIのコマンドのソースコードを参考にしてください。`tchmgr.c' と `tcbmgr.c' と `tcfmgr.c' が最も簡潔でしょう。
+
Q. : tchdbputkeep2とか、APIのシグネチャがわかりにくいんですけど、アンダースコア区切りとかCamelCaseとか使わないんですか?
+
A. : 使いません。UNIX(POSIX)にも、creatとかsbrkとかdup2とかwait3とかwait4とかsigprocmaskとかstrncasecmpとかgethostbyname2とか、あなた好みでないものが多くあります。逆に私はUNIXのようにハードボイルドでユーザに媚びない名前が好きなのです。
+
Q. : データベースが壊れたのですが、どうしてでしょうか?
+
A. : 大抵の場合、あなたのアプリケーションがきちんとデータベースを閉じていないのが原因です。デーモンプロセスであろうが、CGIスクリプトであろうが、アプリケーションが終了する際には必ずデータベースを閉じなければなりません。なお、CGIのプロセスはSIGPIPEやSIGTERMによって殺されることがあることにも留意しましょう。
+
Q. : データベースを壊れにくくするにはどうすればよいですか?
+
A. : トランザクションを使ってください。ディスクやファイルシステムが壊れなければデータベースが壊れないようにすることができます。
+
Q. : 壊れたデータベースを修復するにはどうすればよいですか?
+
A. : データベースファイルをロックなしオプション(HDBONOLCKかBDBONOLCK)をつけて開いて、最適化機能(tchdboptimizeかtcbdboptimize)を実行してください。コマンドラインで修復処理を行いたい場合、「tchmgr optimize -nl casket」もしくは「tcbmgr optimize -nl casket」を実行してください。
+
Q. : 2GBを越えるサイズのファイルを扱おうとするとエラーになるのですが、どうしてですか?
+
A. : 32ビットのファイルシステムでは、LFSなどの明示的な指定をしないと2GBを越えるサイズのファイルを作ることができません。32ビットOS上でXFSやFeiserFSなどの64ビットファイルシステムを利用する場合は2GBを越えるサイズのファイルを扱うことができますが、その際にはTokyo Cabinetを `--enable-off64' をつけた設定でビルドしておく必要があります。純粋な64ビット環境で利用する場合は特別な設定は必要ありません。なお、ulimitやquotaでファイルサイズの制限がかかっていないことも確認しておいてください。
+
Q. : RubyやJavaの言語バインディングで、なぜエラーを例外で処理しないのですか?
+
A. : 例外機構のない言語と共通のインターフェイスにするためです。例外処理を好む人は、独自のラッパーを書いてそれを使ってください。
+
Q. : データベースファイルの名前の拡張子として「.hdb」「.bdb」などを推奨しないのはなぜですか?
+
A. : 世の中にはTokyo Cabinet以外にもデータベースライブラリがたくさんあり、それらのハッシュデータベースやB+木データベースと区別がつかなくなるからです。代わりに「.tch」「.tcb」などを使ってください。
+
Q. : QDBMはもうメンテナンスしないのですか?
+
A. : メンテナンスは続けます。積極的な機能追加の予定はありませんが、もしバグが見つかれば対処します。
+
Q. : Windowsで利用できませんか?
+
A. : 残念ながらできません。今のところ対応予定もありません。
+
Q. : ライセンスをBSDLかMITLに変えてくれませんか?
+
A. : 嫌です。そうすることに特に利点を感じません。
+
Q. : 「Tokyo Cabinet」の名前の由来はなんですか?
+
A. : 作者が住んでいる街なので「tokyo」で、モノをしまうから「cabinet」です。略して「TC」と呼ぶのもよい考えです。「東京キャビネット」とか「とうきょうきゃびねっと」とかいう表記でも構いません。東京ディズニーランドや東京ラブストーリーや東京パフォーマンスドールとは一切関係ありません。識別子以外で「TokyoCabinet」とつなげて表記するのは推奨しません。
+
Q. : あなたは千葉県とどういう関係なのですか?
+
A. : 特に関係はありません。出身地は埼玉県です。落花生は好きです。
+
+ +
+ +

ライセンス

+ +

Tokyo Cabinetはフリーソフトウェアです。あなたは、Free Software Foundationが公表したGNU Lesser General Public Licenseのバージョン2.1あるいはそれ以降の各バージョンの中からいずれかを選択し、そのバージョンが定める条項に従ってTokyo Cabinetを再頒布または変更することができます。

+ +

Tokyo Cabinetは有用であると思われますが、頒布にあたっては、市場性及び特定目的適合性についての暗黙の保証を含めて、いかなる保証も行ないません。詳細についてはGNU Lesser General Public Licenseを読んでください。

+ +

あなたは、Tokyo Cabinetと一緒にGNU Lesser General Public Licenseの写しを受け取っているはずです(`COPYING' ファイルを参照してください)。そうでない場合は、Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA へ連絡してください。

+ +

Tokyo CabinetはFAL Labsが作成しました。作者と連絡をとるには、`info@fallabs.com' 宛に電子メールを送ってください。

+ +
+ + + + + + diff --git a/tcejdb/doc/tokyoproducts.pdf b/tcejdb/doc/tokyoproducts.pdf new file mode 100644 index 0000000000000000000000000000000000000000..597d7841baaa47c0f1699032f80e3c0ad5b16e1e GIT binary patch literal 299895 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 &T8gBnydJqZ(8c;#8rJNb>g&y66 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 z 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 z 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 literal 0 HcmV?d00001 diff --git a/tcejdb/ejdb.c b/tcejdb/ejdb.c new file mode 100644 index 0000000..a973728 --- /dev/null +++ b/tcejdb/ejdb.c @@ -0,0 +1,2816 @@ + +#include + +#include "ejdb_private.h" +#include "ejdbutl.h" + +/* private macros */ +#define JBLOCKMETHOD(JB_ejdb, JB_wr) \ + ((JB_ejdb)->mmtx ? _ejdblockmethod((JB_ejdb), (JB_wr)) : true) +#define JBUNLOCKMETHOD(JB_ejdb) \ + ((JB_ejdb)->mmtx ? _ejdbunlockmethod(JB_ejdb) : true) + +#define JBCLOCKMETHOD(JB_col, JB_wr) \ + ((JB_col)->mmtx ? _ejcollockmethod((JB_col), (JB_wr)) : true) +#define JBCUNLOCKMETHOD(JB_col) \ + ((JB_col)->mmtx ? _ejcollunlockmethod(JB_col) : true) + +#define JBISOPEN(JB_jb) ((JB_jb) && (JB_jb)->metadb && (JB_jb)->metadb->open) ? true : false + +#define JBISVALCOLNAME(JB_cname) ((JB_cname) && strlen(JB_cname) < JBMAXCOLNAMELEN && !index((JB_cname), '.')) + +#define JBENSUREOPENLOCK(JB_jb, JB_lock, JB_ret) \ + assert(JB_jb); \ + if (!JBLOCKMETHOD((JB_jb), (JB_lock))) return JB_ret; \ + if (!JBISOPEN(JB_jb)) { \ + _ejdbsetecode((JB_jb), TCEINVALID, __FILE__, __LINE__, __func__); \ + JBUNLOCKMETHOD(JB_jb); \ + return JB_ret; \ + } + +/* private function prototypes */ +typedef union { + int64_t inum; + double dnum; +} _ejdbnum; + + +static void _ejdbsetecode(EJDB *jb, int ecode, const char *filename, int line, const char *func); +static bool _ejdbsetmutex(EJDB *ejdb); +static bool _ejdblockmethod(EJDB *ejdb, bool wr); +static bool _ejdbunlockmethod(EJDB *ejdb); +static bool _ejdbcolsetmutex(EJCOLL *coll); +static bool _ejcollockmethod(EJCOLL *coll, bool wr); +static bool _ejcollunlockmethod(EJCOLL *coll); +static bool _bsonoidkey(bson* bs, bson_oid_t* oid); +static char* _bsonitstrval(bson_iterator *it, int *vsz, TCLIST *tokens); +static char* _bsonipathrowldr(TCLIST *tokens, const char *pkbuf, int pksz, const char *rowdata, int rowdatasz, + const char *ipath, int ipathsz, void *op, int *vsz); +static char* _bsonfpathrowldr(TCLIST *tokens, const char *rowdata, int rowdatasz, + const char *fpath, int fpathsz, void *op, int *vsz); +EJDB_INLINE bool _isvalidoidstr(const char* oid); +static void _ejdbclear(EJDB *jb); +static bool _createcoldb(const char* colname, EJDB *jb, EJCOLLOPTS *opts, TCTDB** res); +static bool _addcoldb0(const char* colname, EJDB *jb, EJCOLLOPTS *opts, EJCOLL **res); +static void _delcoldb(EJCOLL *cdb); +static void _delqfdata(const EJQ *q, const EJQF* ejqf); +static bool _updatebsonidx(EJCOLL *jcoll, const bson_oid_t *oid, const bson *bs, const void *obsdata, int obsdatasz); +static bool _metasetopts(EJDB *jb, const char *colname, EJCOLLOPTS *opts); +static bool _metagetopts(EJDB *jb, const char *colname, EJCOLLOPTS *opts); +static bson* _metagetbson(EJDB *jb, const char *colname, int colnamesz, const char* mkey); +static bson* _metagetbson2(EJCOLL *jcoll, const char* mkey); +static bool _metasetbson(EJDB *jb, const char *colname, int colnamesz, + const char *mkey, bson* val, bool merge, bool mergeoverwrt); +static bool _metasetbson2(EJCOLL *jcoll, const char *mkey, bson* val, bool merge, bool mergeoverwrt); +static bson* _imetaidx(EJCOLL *jcoll, const char *ipath); +static void _qrypreprocess(EJCOLL *jcoll, EJQ *ejq, EJQF **mqf); +static TCMAP* _parseqobj(EJDB *jb, bson *qspec); +static int _parse_qobj_impl(EJDB* jb, bson_iterator *it, TCMAP *qmap, TCLIST *pathStack, EJQF *pqf); +static int _ejdbsoncmp(const TCLISTDATUM* d1, const TCLISTDATUM* d2, void *opaque); +static bool _qrycondcheckstrand(const char *vbuf, const TCLIST *tokens); +static bool _qrycondcheckstror(const char *vbuf, const TCLIST *tokens); +static bool _qrybsvalmatch(const EJQF *qf, bson_iterator *it, bool expandarrays); +static bool _qrybsmatch(const EJQF *qf, const void *bsbuf, int bsbufsz); +static bool _qryormatch(EJCOLL *jcoll, EJQ* ejq, const void *pkbuf, int pkbufsz, const void *bsbuf, int bsbufsz); +static bool _qryallcondsmatch(bool onlycount, int anum, EJCOLL *jcoll, const EJQF **qfs, int qfsz, + const void *pkbuf, int pkbufsz, void **bsbuf, int *bsbufsz); +static void _qrydup(const EJQ *src, EJQ *target, uint32_t qflags); +static void _qrydel(EJQ *q, bool freequery); +static TCLIST* _qrysearch(EJCOLL *jcoll, const EJQ *q, uint32_t *count, int qflags, TCXSTR *log); +EJDB_INLINE void _nufetch(_ejdbnum *nu, const char *sval, bson_type bt); +EJDB_INLINE int _nucmp(_ejdbnum *nu, const char *sval, bson_type bt); +EJDB_INLINE int _nucmp2(_ejdbnum *nu1, _ejdbnum *nu2, bson_type bt); +static EJCOLL* _getcoll(EJDB *jb, const char* colname); + +EJDB_EXPORT const char* ejdberrmsg(int ecode) { + switch (ecode) { + case JDBEINVALIDCOLNAME: return "invalid collection name"; + case JDBEINVALIDBSON: return "invalid bson object"; + case JDBEQINVALIDQCONTROL: return "invalid query control field starting with '$'"; + case JDBEQINOPNOTARRAY: return "$strand, $stror, $in, $nin, $bt keys requires not empty array value"; + case JDBEMETANVALID: return "inconsistent database metadata"; + case JDBEFPATHINVALID: return "invalid JSON field path value"; + case JDBEQINVALIDQRX: return "invalid query regexp value"; + case JDBEQRSSORTING: return "result set sorting error"; + case JDBQINVALID: return "query generic error"; + default: return tcerrmsg(ecode); + } +} + +/* Get the last happened error code of a database object. */ +EJDB_EXPORT int ejdbecode(EJDB *jb) { + assert(jb && jb->metadb); + return tctdbecode(jb->metadb); +} + +EJDB_EXPORT EJDB* ejdbnew(void) { + EJDB *jb; + TCMALLOC(jb, sizeof (*jb)); + _ejdbclear(jb); + jb->metadb = tctdbnew(); +#ifdef _DEBUG + tchdbsetdbgfd(jb->metadb->hdb, fileno(stderr)); +#endif + if (!_ejdbsetmutex(jb)) { + tctdbdel(jb->metadb); + TCFREE(jb); + return NULL; + } + return jb; +} + +EJDB_EXPORT void ejdbdel(EJDB *jb) { + assert(jb && jb->metadb); + if (JBISOPEN(jb)) ejdbclose(jb); + for (int i = 0; i < jb->cdbsnum; ++i) { + _delcoldb(jb->cdbs + i); + } + TCFREE(jb->cdbs); + jb->cdbsnum = 0; + if (jb->mmtx) { + pthread_rwlock_destroy(jb->mmtx); + TCFREE(jb->mmtx); + } + tctdbdel(jb->metadb); + TCFREE(jb); +} + +EJDB_EXPORT bool ejdbclose(EJDB *jb) { + JBENSUREOPENLOCK(jb, true, false); + bool rv = true; + for (int i = 0; i < jb->cdbsnum; ++i) { + if (!tctdbclose(jb->cdbs[i].tdb)) { + rv = false; + } + } + if (!tctdbclose(jb->metadb)) { + rv = false; + } + JBUNLOCKMETHOD(jb); + return rv; +} + +EJDB_EXPORT bool ejdbopen(EJDB *jb, const char *path, int mode) { + assert(jb && path); + assert(jb->metadb); + if (!JBLOCKMETHOD(jb, true)) return false; + if (JBISOPEN(jb)) { + _ejdbsetecode(jb, TCEINVALID, __FILE__, __LINE__, __func__); + JBUNLOCKMETHOD(jb); + return false; + } + bool rv = tctdbopen(jb->metadb, path, mode); + if (!rv) { + goto finish; + } + TCMALLOC(jb->cdbs, 1); + jb->cdbsnum = 0; + TCTDB *mdb = jb->metadb; + rv = tctdbiterinit(mdb); + if (!rv) { + goto finish; + } + char* colname = NULL; + for (int i = 0; i < mdb->hdb->rnum && (colname = tctdbiternext2(mdb)) != NULL; ++i) { + EJCOLL *cdb; + EJCOLLOPTS opts; + _metagetopts(jb, colname, &opts); + _addcoldb0(colname, jb, &opts, &cdb); + TCFREE(colname); + } +finish: + JBUNLOCKMETHOD(jb); + return rv; +} + +EJDB_EXPORT EJCOLL* ejdbgetcoll(EJDB *jb, const char* colname) { + assert(colname); + EJCOLL *coll = NULL; + JBENSUREOPENLOCK(jb, false, NULL); + coll = _getcoll(jb, colname); + JBUNLOCKMETHOD(jb); + return coll; +} + +EJDB_EXPORT EJCOLL* ejdbcreatecoll(EJDB *jb, const char* colname, EJCOLLOPTS *opts) { + assert(colname); + EJCOLL *coll = ejdbgetcoll(jb, colname); + if (coll) { + return coll; + } + if (!JBISVALCOLNAME(colname)) { + _ejdbsetecode(jb, JDBEINVALIDCOLNAME, __FILE__, __LINE__, __func__); + return NULL; + } + JBENSUREOPENLOCK(jb, true, NULL); + TCTDB* meta = jb->metadb; + char *row = tcsprintf("name\t%s", colname); + if (!tctdbput3(meta, colname, row)) { + goto finish; + } + if (!_addcoldb0(colname, jb, opts, &coll)) { + tctdbout2(meta, colname); //cleaning + goto finish; + } + _metasetopts(jb, colname, opts); +finish: + JBUNLOCKMETHOD(jb); + if (row) { + TCFREE(row); + } + return coll; +} + +EJDB_EXPORT bool ejdbrmcoll(EJDB *jb, const char *colname, bool unlinkfile) { + assert(colname); + JBENSUREOPENLOCK(jb, true, false); + bool rv = true; + EJCOLL *coll = _getcoll(jb, colname); + if (!coll) { + goto finish; + } + EJCOLL *cdbs = jb->cdbs; + for (int i = 0; i < jb->cdbsnum; ++i) { + coll = jb->cdbs + i; + if (!strcmp(colname, coll->cname)) { + if (!JBCLOCKMETHOD(coll, true)) return false; + jb->cdbsnum--; + memmove(cdbs + i, cdbs + i + 1, sizeof (*cdbs) * (jb->cdbsnum - i)); + tctdbout2(jb->metadb, colname); + tctdbvanish(coll->tdb); + TCLIST *paths = tclistnew2(10); + tclistpush2(paths, coll->tdb->hdb->path); + for (int j = 0; j < coll->tdb->inum; ++j) { + TDBIDX *idx = coll->tdb->idxs + j; + const char *ipath = tcbdbpath(idx->db); + if (ipath) { + tclistpush2(paths, ipath); + } + } + tctdbclose(coll->tdb); + if (unlinkfile) { + for (int j = 0; j < TCLISTNUM(paths); ++j) { + unlink(tclistval2(paths, j)); + } + } + tclistdel(paths); + JBCUNLOCKMETHOD(coll); + _delcoldb(coll); + break; + } + } +finish: + JBUNLOCKMETHOD(jb); + return rv; +} + +/* Save/Update BSON into 'coll' */ +EJDB_EXPORT bool ejdbsavebson(EJCOLL *jcoll, bson *bs, bson_oid_t *oid) { + assert(jcoll && bs); + if (!JBISOPEN(jcoll->jb)) { + _ejdbsetecode(jcoll->jb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + if (!JBCLOCKMETHOD(jcoll, true)) return false; + bool rv = false; + bson *nbs = NULL; + if (!_bsonoidkey(bs, oid)) { //no _id regenerate new BSON with _id primary key + bson_oid_gen(oid); + nbs = bson_create(); + bson_init_size(nbs, bson_size(bs) + (strlen(JDBIDKEYNAME) + 1/*key*/ + 1/*type*/ + sizeof (*oid))); + bson_append_oid(nbs, JDBIDKEYNAME, oid); + bson_ensure_space(nbs, bson_size(bs) - 4); + bson_append(nbs, bson_data(bs) + 4, bson_size(bs) - (4 + 1/*BSON_EOO*/)); + bson_finish(nbs); + bs = nbs; + } + TCTDB *tdb = jcoll->tdb; + TCMAP *rowm = (tdb->hdb->rnum > 0) ? tctdbget(tdb, oid, sizeof (*oid)) : NULL; + char *obsdata = NULL; //Old bson + int obsdatasz = 0; + if (rowm) { //Save the copy of old bson data + const void* tmp = tcmapget(rowm, JDBCOLBSON, JDBCOLBSONL, &obsdatasz); + if (tmp && obsdatasz > 0) { + TCMALLOC(obsdata, obsdatasz); + memmove(obsdata, tmp, obsdatasz); + } + } else { + rowm = tcmapnew2(128); + } + tcmapput(rowm, JDBCOLBSON, JDBCOLBSONL, bson_data(bs), bson_size(bs)); + if (!tctdbput(tdb, oid, sizeof (*oid), rowm)) { + goto finish; + } + //Update indexes + rv = _updatebsonidx(jcoll, oid, bs, obsdata, obsdatasz); +finish: + JBCUNLOCKMETHOD(jcoll); + if (rowm) { + tcmapdel(rowm); + } + if (obsdata) { + TCFREE(obsdata); + } + if (nbs) { + bson_del(nbs); + } + return rv; +} + +EJDB_EXPORT bool ejdbrmbson(EJCOLL* jcoll, bson_oid_t* oid) { + assert(jcoll && oid); + if (!JBISOPEN(jcoll->jb)) { + _ejdbsetecode(jcoll->jb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + JBCLOCKMETHOD(jcoll, true); + bool rv = true; + const void *olddata; + int olddatasz = 0; + TCMAP *rmap = tctdbget(jcoll->tdb, oid, sizeof (*oid)); + if (!rmap) { + goto finish; + } + olddata = tcmapget3(rmap, JDBCOLBSON, JDBCOLBSONL, &olddatasz); + if (!_updatebsonidx(jcoll, oid, NULL, olddata, olddatasz)) { + rv = false; + } + if (!tctdbout(jcoll->tdb, oid, sizeof (*oid))) { + rv = false; + } +finish: + JBCUNLOCKMETHOD(jcoll); + if (rmap) { + tcmapdel(rmap); + } + return rv; +} + +EJDB_EXPORT bson* ejdbloadbson(EJCOLL *jcoll, const bson_oid_t *oid) { + assert(jcoll && oid); + if (!JBISOPEN(jcoll->jb)) { + _ejdbsetecode(jcoll->jb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + JBCLOCKMETHOD(jcoll, false); + bson *ret = NULL; + int datasz; + void *cdata = tchdbget(jcoll->tdb->hdb, oid, sizeof (*oid), &datasz); + if (!cdata) { + goto finish; + } + void *bsdata = tcmaploadone(cdata, datasz, JDBCOLBSON, JDBCOLBSONL, &datasz); + if (!bsdata) { + goto finish; + } + if (datasz <= 4) { + TCFREE(bsdata); + goto finish; + } + ret = bson_create(); + bson_init_finished_data(ret, bsdata); +finish: + JBCUNLOCKMETHOD(jcoll); + if (cdata) { + TCFREE(cdata); + } + return ret; +} + +EJDB_EXPORT EJQ* ejdbcreatequery(EJDB *jb, bson *qobj, bson *orqobjs, int orqobjsnum, bson *hints) { + EJQ *q; + TCCALLOC(q, 1, sizeof (*q)); + if (qobj) { + q->qobjmap = _parseqobj(jb, qobj); + if (!q->qobjmap) { + goto error; + } + } + if (orqobjs && orqobjsnum) { + q->orqobjsnum = orqobjsnum; + TCMALLOC(q->orqobjs, sizeof (*(q->orqobjs)) * q->orqobjsnum); + for (int i = 0; i < orqobjsnum; ++i) { + bson *oqb = (orqobjs + i); + assert(oqb); + EJQ *oq = ejdbcreatequery(jb, oqb, NULL, 0, NULL); + if (oq == NULL) { + goto error; + } + q->orqobjs[i] = *oq; //copy struct + TCFREE(oq); + } + } + if (hints) { + q->hints = bson_create(); + if (bson_copy(q->hints, hints)) { + goto error; + } + } + return q; +error: + ejdbquerydel(q); + return NULL; +} + +EJDB_EXPORT void ejdbquerydel(EJQ *q) { + _qrydel(q, true); +} + +/** Set index */ +EJDB_EXPORT bool ejdbsetindex(EJCOLL *jcoll, const char *fpath, int flags) { + assert(jcoll && fpath); + bool rv = true; + bson *imeta = NULL; + bson_iterator it; + int tcitype = 0; //TCDB index type + int oldiflags = 0; //Old index flags stored in meta + bool ibld = (flags & JDIDXREBLD); + if (ibld) { + flags &= ~JDIDXREBLD; + } + bool idrop = (flags & JDIDXDROP); + if (idrop) { + flags &= ~JDIDXDROP; + } + bool idropall = (flags & JDIDXDROPALL); + if (idropall) { + idrop = true; + flags &= ~JDIDXDROPALL; + } + bool iop = (flags & JDIDXOP); + if (iop) { + flags &= ~JDIDXOP; + } + + char ipath[BSON_MAX_FPATH_LEN + 2]; //add 2 bytes for one char prefix and '\0'term + char ikey[BSON_MAX_FPATH_LEN + 2]; //add 2 bytes for one char prefix and '\0'term + int fpathlen = strlen(fpath); + if (fpathlen > BSON_MAX_FPATH_LEN) { + _ejdbsetecode(jcoll->jb, JDBEFPATHINVALID, __FILE__, __LINE__, __func__); + rv = false; + goto finish; + } + memmove(ikey + 1, fpath, fpathlen + 1); + ikey[0] = 'i'; + memmove(ipath + 1, fpath, fpathlen + 1); + ipath[0] = 's'; //string index type + + JBENSUREOPENLOCK(jcoll->jb, true, false); + imeta = _imetaidx(jcoll, fpath); + if (!imeta) { + if (idrop) { //Cannot drop/optimize not existent index; + JBUNLOCKMETHOD(jcoll->jb); + goto finish; + } + if (iop) { + iop = false; //New index will be created + } + imeta = bson_create(); + bson_init(imeta); + bson_append_string(imeta, "ipath", fpath); + bson_append_int(imeta, "iflags", flags); + bson_finish(imeta); + rv = _metasetbson2(jcoll, ikey, imeta, false, false); + if (!rv) { + JBUNLOCKMETHOD(jcoll->jb); + goto finish; + } + } else { + if (bson_find(&it, imeta, "iflags") != BSON_EOO) { + oldiflags = bson_iterator_int(&it); + } + if (!idrop && oldiflags != flags) { //Update index meta + bson imetadelta; + bson_init(&imetadelta); + bson_append_int(&imetadelta, "iflags", flags); + bson_finish(&imetadelta); + rv = _metasetbson2(jcoll, ikey, &imetadelta, true, true); + bson_destroy(&imetadelta); + if (!rv) { + JBUNLOCKMETHOD(jcoll->jb); + goto finish; + } + } + } + JBUNLOCKMETHOD(jcoll->jb); + + if (idrop) { + tcitype = TDBITVOID; + if (idropall && oldiflags) { + flags = oldiflags; //Drop index only for existing types + } + } else if (iop) { + tcitype = TDBITOPT; + if (oldiflags) { + flags = oldiflags; //Optimize index for all existing types + } + } + + if (!JBCLOCKMETHOD(jcoll, true)) { + rv = false; + goto finish; + } + if (tcitype) { + if (flags & JDIDXSTR) { + ipath[0] = 's'; + rv = tctdbsetindexrldr(jcoll->tdb, ipath, tcitype, _bsonipathrowldr, NULL); + } + if (rv && (flags & JDIDXNUM)) { + ipath[0] = 'n'; + rv = tctdbsetindexrldr(jcoll->tdb, ipath, tcitype, _bsonipathrowldr, NULL); + } + if (rv && (flags & JDIDXARR)) { + ipath[0] = 'a'; + rv = tctdbsetindexrldr(jcoll->tdb, ipath, tcitype, _bsonipathrowldr, NULL); + } + if (idrop) { //Update index meta on drop + oldiflags &= ~flags; + if (oldiflags) { //Index dropped only for some types + bson imetadelta; + bson_init(&imetadelta); + bson_append_int(&imetadelta, "iflags", oldiflags); + bson_finish(&imetadelta); + rv = _metasetbson2(jcoll, ikey, &imetadelta, true, true); + bson_destroy(&imetadelta); + } else { //Index dropped completely + rv = _metasetbson2(jcoll, ikey, NULL, false, false); + } + } + } else { + if ((flags & JDIDXSTR) && (ibld || !(oldiflags & JDIDXSTR))) { + ipath[0] = 's'; + rv = tctdbsetindexrldr(jcoll->tdb, ipath, TDBITLEXICAL, _bsonipathrowldr, NULL); + } + if (rv && (flags & JDIDXNUM) && (ibld || !(oldiflags & JDIDXNUM))) { + ipath[0] = 'n'; + rv = tctdbsetindexrldr(jcoll->tdb, ipath, TDBITDECIMAL, _bsonipathrowldr, NULL); + } + if (rv && (flags & JDIDXARR) && (ibld || !(oldiflags & JDIDXARR))) { + ipath[0] = 'a'; + rv = tctdbsetindexrldr(jcoll->tdb, ipath, TDBITTOKEN, _bsonipathrowldr, NULL); + } + } + JBCUNLOCKMETHOD(jcoll); +finish: + if (imeta) { + bson_del(imeta); + } + return rv; +} + +EJDB_EXPORT TCLIST* ejdbqrysearch(EJCOLL *jcoll, const EJQ *q, uint32_t *count, int qflags, TCXSTR *log) { + assert(jcoll && q && q->qobjmap); + if (!JBISOPEN(jcoll->jb)) { + _ejdbsetecode(jcoll->jb, TCEINVALID, __FILE__, __LINE__, __func__); + return NULL; + } + JBCLOCKMETHOD(jcoll, false); + TCLIST *res; + res = _qrysearch(jcoll, q, count, qflags, log); + JBCUNLOCKMETHOD(jcoll); + return res; +} + +EJDB_EXPORT bool ejdbsyncoll(EJCOLL *jcoll) { + assert(jcoll); + if (!JBISOPEN(jcoll->jb)) { + _ejdbsetecode(jcoll->jb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + bool rv = false; + if (!JBCLOCKMETHOD(jcoll, true)) return false; + rv = tctdbsync(jcoll->tdb); + JBCUNLOCKMETHOD(jcoll); + return rv; +} + +EJDB_EXPORT bool ejdbtranbegin(EJCOLL *jcoll) { + assert(jcoll); + if (!JBISOPEN(jcoll->jb)) { + _ejdbsetecode(jcoll->jb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + for (double wsec = 1.0 / sysconf(_SC_CLK_TCK); true; wsec *= 2) { + if (!JBCLOCKMETHOD(jcoll, true)) return false; + if (!jcoll->tdb->open || !jcoll->tdb->wmode) { + _ejdbsetecode(jcoll->jb, TCEINVALID, __FILE__, __LINE__, __func__); + JBCUNLOCKMETHOD(jcoll); + return false; + } + if (!jcoll->tdb->tran) break; + JBCUNLOCKMETHOD(jcoll); + if (wsec > 1.0) wsec = 1.0; + tcsleep(wsec); + } + if (!tctdbtranbeginimpl(jcoll->tdb)) { + JBCUNLOCKMETHOD(jcoll); + return false; + } + jcoll->tdb->tran = true; + JBCUNLOCKMETHOD(jcoll); + return true; +} + +EJDB_EXPORT bool ejdbtrancommit(EJCOLL *jcoll) { + assert(jcoll); + if (!JBISOPEN(jcoll->jb)) { + _ejdbsetecode(jcoll->jb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + if (!JBCLOCKMETHOD(jcoll, true)) return false; + if (!jcoll->tdb->open || !jcoll->tdb->wmode || !jcoll->tdb->tran) { + _ejdbsetecode(jcoll->jb, TCEINVALID, __FILE__, __LINE__, __func__); + JBCUNLOCKMETHOD(jcoll); + return false; + } + jcoll->tdb->tran = false; + bool err = false; + if (!tctdbtrancommitimpl(jcoll->tdb)) err = true; + JBCUNLOCKMETHOD(jcoll); + return !err; +} + +EJDB_EXPORT bool ejdbtranabort(EJCOLL *jcoll) { + assert(jcoll); + if (!JBISOPEN(jcoll->jb)) { + _ejdbsetecode(jcoll->jb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + if (!JBCLOCKMETHOD(jcoll, true)) return false; + if (!jcoll->tdb->open || !jcoll->tdb->wmode || !jcoll->tdb->tran) { + _ejdbsetecode(jcoll->jb, TCEINVALID, __FILE__, __LINE__, __func__); + JBCUNLOCKMETHOD(jcoll); + return false; + } + jcoll->tdb->tran = false; + bool err = false; + if (!tctdbtranabortimpl(jcoll->tdb)) err = true; + JBCUNLOCKMETHOD(jcoll); + return !err; +} + +/************************************************************************************************* + * private features + *************************************************************************************************/ + +/* Set the error code of a table database object. */ +static void _ejdbsetecode(EJDB *jb, int ecode, const char *filename, int line, const char *func) { + assert(jb && filename && line >= 1 && func); + tctdbsetecode(jb->metadb, ecode, filename, line, func); +} + +static EJCOLL* _getcoll(EJDB *jb, const char* colname) { + assert(colname); + EJCOLL *coll = NULL; + //check if collection exists + for (int i = 0; i < jb->cdbsnum; ++i) { + coll = jb->cdbs + i; + assert(coll); + if (!strcmp(colname, coll->cname)) { + break; + } else { + coll = NULL; + } + } + return coll; +} + +/* Set mutual exclusion control of a table database object for threading. */ +static bool _ejdbsetmutex(EJDB *ejdb) { + assert(ejdb); + if (!TCUSEPTHREAD) return true; + if (ejdb->mmtx || JBISOPEN(ejdb)) { + _ejdbsetecode(ejdb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + TCMALLOC(ejdb->mmtx, sizeof (pthread_rwlock_t)); + bool err = false; + if (pthread_rwlock_init(ejdb->mmtx, NULL) != 0) err = true; + if (err) { + TCFREE(ejdb->mmtx); + ejdb->mmtx = NULL; + return false; + } + return true; +} + +/* Lock a method of the table database object. + `tdb' specifies the table database object. + `wr' specifies whether the lock is writer or not. + If successful, the return value is true, else, it is false. */ +static bool _ejdblockmethod(EJDB *ejdb, bool wr) { + assert(ejdb); + if (wr ? pthread_rwlock_wrlock(ejdb->mmtx) != 0 : pthread_rwlock_rdlock(ejdb->mmtx) != 0) { + _ejdbsetecode(ejdb, TCETHREAD, __FILE__, __LINE__, __func__); + return false; + } + TCTESTYIELD(); + return true; +} + +/* Unlock a method of the table database object. + `tdb' specifies the table database object. + If successful, the return value is true, else, it is false. */ +static bool _ejdbunlockmethod(EJDB *ejdb) { + assert(ejdb); + if (pthread_rwlock_unlock(ejdb->mmtx) != 0) { + _ejdbsetecode(ejdb, TCETHREAD, __FILE__, __LINE__, __func__); + return false; + } + TCTESTYIELD(); + return true; +} + +static bool _ejdbcolsetmutex(EJCOLL *coll) { + assert(coll && coll->jb); + if (!TCUSEPTHREAD) return true; + if (coll->mmtx) { + _ejdbsetecode(coll->jb, TCEINVALID, __FILE__, __LINE__, __func__); + return false; + } + TCMALLOC(coll->mmtx, sizeof (pthread_rwlock_t)); + bool err = false; + if (pthread_rwlock_init(coll->mmtx, NULL) != 0) err = true; + if (err) { + TCFREE(coll->mmtx); + coll->mmtx = NULL; + return false; + } + return true; +} + +static bool _ejcollockmethod(EJCOLL *coll, bool wr) { + assert(coll && coll->jb); + if (wr ? pthread_rwlock_wrlock(coll->mmtx) != 0 : pthread_rwlock_rdlock(coll->mmtx) != 0) { + _ejdbsetecode(coll->jb, TCETHREAD, __FILE__, __LINE__, __func__); + return false; + } + TCTESTYIELD(); + return (coll->tdb && coll->tdb->open); +} + +static bool _ejcollunlockmethod(EJCOLL *coll) { + assert(coll && coll->jb); + if (pthread_rwlock_unlock(coll->mmtx) != 0) { + _ejdbsetecode(coll->jb, TCETHREAD, __FILE__, __LINE__, __func__); + return false; + } + TCTESTYIELD(); + return true; +} + +static void _qrydel(EJQ *q, bool freequery) { + if (!q) { + return; + } + const EJQF *qf = NULL; + if (q->qobjmap) { + int ksz; + const char* kbuf; + tcmapiterinit(q->qobjmap); + while ((kbuf = tcmapiternext(q->qobjmap, &ksz)) != NULL) { + int vsz; + qf = tcmapiterval(kbuf, &vsz); + assert(qf); + assert(vsz == sizeof (*qf)); + _delqfdata(q, qf); + } + tcmapdel(q->qobjmap); + q->qobjmap = NULL; + } + if (q->orqobjsnum) { + for (int i = 0; i < q->orqobjsnum; ++i) { + _qrydel(q->orqobjs + i, false); + } + TCFREE(q->orqobjs); + q->orqobjsnum = 0; + q->orqobjs = NULL; + } + if (q->hints) { + bson_del(q->hints); + q->hints = NULL; + } + if (freequery) { + TCFREE(q); + } +} + +static bool _qrybsvalmatch(const EJQF *qf, bson_iterator *it, bool expandarrays) { + if (qf->tcop == TDBQTRUE) { + return (true == !qf->negate); + } + bool rv = false; + bson_type bt = bson_iterator_type(it); + const char *expr = qf->expr; + int exprsz = qf->exprsz; + + if (bt == BSON_ARRAY && expandarrays) { //Iterate over array + bson_iterator sit; + bson_iterator_subiterator(it, &sit); + while ((bt = bson_iterator_next(&sit)) != BSON_EOO) { + if (_qrybsvalmatch(qf, &sit, false)) { + return true; + } + } + return (false == !qf->negate); + } + switch (qf->tcop) { + case TDBQCSTREQ: + { + int fvalsz = (bt == BSON_STRING) ? bson_iterator_string_len(it) : 0; + const char *fval = (bt == BSON_STRING) ? bson_iterator_string(it) : ""; + rv = (exprsz == fvalsz - 1) && !memcmp(expr, fval, exprsz); + break; + } + case TDBQCSTRINC: + { + int fvalsz = (bt == BSON_STRING) ? bson_iterator_string_len(it) : 0; + const char *fval = (bt == BSON_STRING) ? bson_iterator_string(it) : ""; + rv = (exprsz < fvalsz) && strstr(fval, expr); + break; + } + case TDBQCSTRBW: + { + const char *fval = bson_iterator_string(it); + rv = tcstrfwm(fval, expr); + break; + } + case TDBQCSTREW: + { + const char *fval = bson_iterator_string(it); + rv = tcstrbwm(fval, expr); + break; + } + case TDBQCSTRAND: + { + TCLIST *tokens = qf->exprlist; + assert(tokens); + const char *fval = bson_iterator_string(it); + rv = _qrycondcheckstrand(fval, tokens); + break; + } + case TDBQCSTROR: + { + TCLIST *tokens = qf->exprlist; + assert(tokens); + const char *fval = bson_iterator_string(it); + rv = _qrycondcheckstror(fval, tokens); + break; + } + case TDBQCSTROREQ: + { + int fvalsz = (bt == BSON_STRING) ? bson_iterator_string_len(it) : 0; + TCLIST *tokens = qf->exprlist; + assert(tokens); + const char *fval = (bt == BSON_STRING) ? bson_iterator_string(it) : ""; + for (int i = 0; i < TCLISTNUM(tokens); ++i) { + const char* token = TCLISTVALPTR(tokens, i); + int tokensz = TCLISTVALSIZ(tokens, i); + if (TCLISTVALSIZ(tokens, i) == (fvalsz - 1) && !strncmp(token, fval, tokensz)) { + rv = true; + break; + } + } + break; + } + case TDBQCSTRRX: + { + const char *fval = bson_iterator_string(it); + rv = qf->regex && (regexec(qf->regex, fval, 0, NULL, 0) == 0); + break; + } + case TDBQCNUMEQ: + { + if (bt == BSON_DOUBLE) { + rv = (qf->exprdblval == bson_iterator_double_raw(it)); + } else if (bt == BSON_INT || bt == BSON_LONG) { + rv = (qf->exprlongval == bson_iterator_long(it)); + } else { + rv = false; + } + break; + } + case TDBQCNUMGT: + { + if (bt == BSON_DOUBLE) { + rv = (qf->exprdblval < bson_iterator_double_raw(it)); + } else if (bt == BSON_INT || bt == BSON_LONG) { + rv = (qf->exprlongval < bson_iterator_long(it)); + } else { + rv = false; + } + break; + } + case TDBQCNUMGE: + { + if (bt == BSON_DOUBLE) { + rv = (qf->exprdblval <= bson_iterator_double_raw(it)); + } else if (bt == BSON_INT || bt == BSON_LONG) { + rv = (qf->exprlongval <= bson_iterator_long(it)); + } else { + rv = false; + } + break; + } + case TDBQCNUMLT: + { + if (bt == BSON_DOUBLE) { + rv = (qf->exprdblval > bson_iterator_double_raw(it)); + } else if (bt == BSON_INT || bt == BSON_LONG) { + rv = (qf->exprlongval > bson_iterator_long(it)); + } else { + rv = false; + } + break; + } + case TDBQCNUMLE: + { + if (bt == BSON_DOUBLE) { + rv = (qf->exprdblval >= bson_iterator_double_raw(it)); + } else if (bt == BSON_INT || bt == BSON_LONG) { + rv = (qf->exprlongval >= bson_iterator_long(it)); + } else { + rv = false; + } + break; + } + case TDBQCNUMBT: + { + assert(qf->ftype == BSON_ARRAY); + TCLIST *tokens = qf->exprlist; + assert(tokens); + assert(TCLISTNUM(tokens) == 2); + if (bson_iterator_type(it) == BSON_DOUBLE) { + double v1 = tctdbatof2(tclistval2(tokens, 0)); + double v2 = tctdbatof2(tclistval2(tokens, 1)); + double val = bson_iterator_double(it); + rv = (v2 > v1) ? (v2 >= val && v1 <= val) : (v2 <= val && v1 >= val); + } else { + int64_t v1 = tctdbatoi(tclistval2(tokens, 0)); + int64_t v2 = tctdbatoi(tclistval2(tokens, 1)); + int64_t val = bson_iterator_long(it); + rv = (v2 > v1) ? (v2 >= val && v1 <= val) : (v2 <= val && v1 >= val); + } + break; + } + case TDBQCNUMOREQ: + { + TCLIST *tokens = qf->exprlist; + assert(tokens); + if (bt == BSON_DOUBLE) { + double nval = bson_iterator_double_raw(it); + for (int i = 0; i < TCLISTNUM(tokens); ++i) { + if (tctdbatof2(TCLISTVALPTR(tokens, i)) == nval) { + rv = true; + break; + } + } + } else if (bt == BSON_INT || bt == BSON_LONG) { + int64_t nval = bson_iterator_long(it); + for (int i = 0; i < TCLISTNUM(tokens); ++i) { + if (tctdbatoi(TCLISTVALPTR(tokens, i)) == nval) { + rv = true; + break; + } + } + } + break; + } + } + return (rv == !qf->negate); +} + +static bool _qrybsmatch(const EJQF *qf, const void *bsbuf, int bsbufsz) { + if (qf->tcop == TDBQTRUE) { + return !qf->negate; + } + bson_iterator it; + bson_iterator_from_buffer(&it, bsbuf); + bson_type bt = bson_find_fieldpath_value2(qf->fpath, qf->fpathsz, &it); + if (bt == BSON_EOO || bt == BSON_UNDEFINED || bt == BSON_NULL) { + return qf->negate; //Field missing + } else if (qf->tcop == TDBQCEXIST) { + return !qf->negate; + } + return _qrybsvalmatch(qf, &it, true); +} + +static bool _qryormatch(EJCOLL *jcoll, + EJQ* ejq, + const void *pkbuf, int pkbufsz, + const void *bsbuf, int bsbufsz) { + if (ejq->orqobjsnum <= 0 || !ejq->orqobjs) { + return true; + } + bool rv = false; + void *nbsbuf = NULL; + const void *kbuf = NULL; + int kbufsz; + if (bsbuf == NULL) { + int cbufsz; + int lbsbufsz; + char *cbuf = tchdbget(jcoll->tdb->hdb, pkbuf, pkbufsz, &cbufsz); + if (!cbuf) { + return false; + } + nbsbuf = tcmaploadone(cbuf, cbufsz, JDBCOLBSON, JDBCOLBSONL, &lbsbufsz); //BSON buffer + if (!nbsbuf) { + TCFREE(cbuf); + return false; + } + bsbufsz = lbsbufsz; + bsbuf = nbsbuf; + } + if (ejq->lastmatchedorqf && _qrybsmatch(ejq->lastmatchedorqf, bsbuf, bsbufsz)) { + rv = true; + goto finish; + } + for (int i = 0; i < ejq->orqobjsnum; ++i) { + const EJQ *oq = (ejq->orqobjs + i); + const EJQF *qf; + int qfsz; + TCMAP *qobjmap = oq->qobjmap; + assert(qobjmap); + tcmapiterinit(qobjmap); + while ((kbuf = tcmapiternext(qobjmap, &kbufsz)) != NULL) { + qf = tcmapiterval(kbuf, &qfsz); + if (qf == ejq->lastmatchedorqf) { + continue; + } + assert(qfsz == sizeof (EJQF)); + if (_qrybsmatch(qf, bsbuf, bsbufsz)) { + ejq->lastmatchedorqf = qf; + rv = true; + goto finish; + } + } + } +finish: + if (nbsbuf) { //BSON Buffer created by me + TCFREE(nbsbuf); + } + return rv; +} + +/** Caller must TCFREE(*bsbuf) if it return TRUE */ +static bool _qryallcondsmatch( + bool onlycount, int anum, + EJCOLL *jcoll, const EJQF **qfs, int qfsz, + const void *pkbuf, int pkbufsz, + void **bsbuf, int *bsbufsz) { + if (onlycount && anum < 1) { + *bsbuf = NULL; + *bsbufsz = 0; + return true; + } + bool rv = true; + //Load BSON + int cbufsz; + char *cbuf = tchdbget(jcoll->tdb->hdb, pkbuf, pkbufsz, &cbufsz); + if (!cbuf) { + *bsbuf = NULL; + *bsbufsz = 0; + return false; + } + *bsbuf = tcmaploadone(cbuf, cbufsz, JDBCOLBSON, JDBCOLBSONL, bsbufsz); //BSON buffer + if (!*bsbuf) { + TCFREE(cbuf); + *bsbufsz = 0; + return false; + } + if (anum < 1) { + TCFREE(cbuf); + return true; + } + for (int i = 0; i < qfsz; ++i) { + const EJQF *qf = qfs[i]; + if (qf->flags & EJFEXCLUDED) continue; + if (!_qrybsmatch(qf, *bsbuf, *bsbufsz)) { + rv = false; + break; + } + } + TCFREE(cbuf); + if (!rv) { + TCFREE(*bsbuf); + *bsbuf = NULL; + *bsbufsz = 0; + } + return rv; +} + +typedef struct { + const EJQF **ofs; + int ofsz; +} _ejbsortctx; + +/* RS sorting comparison func */ +static int _ejdbsoncmp(const TCLISTDATUM* d1, const TCLISTDATUM* d2, void *opaque) { + _ejbsortctx *ctx = opaque; + assert(ctx); + int res = 0; + for (int i = 0; !res && i < ctx->ofsz; ++i) { + const EJQF *qf = ctx->ofs[i]; + if (qf->flags & EJFORDERUSED) { + continue; + } + res = bson_compare(d1->ptr, d2->ptr, qf->fpath, qf->fpathsz) * (qf->order >= 0 ? 1 : -1); + } + return res; +} + +EJDB_INLINE void _nufetch(_ejdbnum *nu, const char *sval, bson_type bt) { + if (bt == BSON_INT || bt == BSON_LONG) { + nu->inum = tctdbatoi(sval); + } else if (bt == BSON_DOUBLE) { + nu->dnum = tctdbatof2(sval); + } else { + nu->inum = 0; + assert(0); + } +} + +EJDB_INLINE int _nucmp(_ejdbnum *nu, const char *sval, bson_type bt) { + if (bt == BSON_INT || bt == BSON_LONG) { + int64_t v = tctdbatoi(sval); + return (nu->inum > v) ? 1 : (nu->inum < v ? -1 : 0); + } else if (bt == BSON_DOUBLE) { + double v = tctdbatof2(sval); + return (nu->dnum > v) ? 1 : (nu->dnum < v ? -1 : 0); + } else { + assert(0); + } + return 0; +} + +EJDB_INLINE int _nucmp2(_ejdbnum *nu1, _ejdbnum *nu2, bson_type bt) { + if (bt == BSON_INT || bt == BSON_LONG) { + return (nu1->inum > nu2->inum) ? 1 : (nu1->inum < nu2->inum ? -1 : 0); + } else if (bt == BSON_DOUBLE) { + return (nu1->dnum > nu2->dnum) ? 1 : (nu1->dnum < nu2->dnum ? -1 : 0); + } else { + assert(0); + } + return 0; +} + +static void _qryfieldup(const EJQF *src, EJQF *target, uint32_t qflags) { + assert(src && target); + memset(target, 0, sizeof (*target)); + target->exprdblval = src->exprdblval; + target->exprlongval = src->exprlongval; + target->flags = src->flags; + target->ftype = src->ftype; + target->negate = src->negate; + target->order = src->order; + target->orderseq = src->orderseq; + target->tcop = src->tcop; + if (src->expr) { + TCMEMDUP(target->expr, src->expr, src->exprsz); + target->exprsz = src->exprsz; + } + if (src->fpath) { + TCMEMDUP(target->fpath, src->fpath, src->fpathsz); + target->fpathsz = src->fpathsz; + } + if (src->regex && (EJQINTERNAL & qflags)) { + //We cannot do deep copy of regex_t so do shallow copy only for internal query objects + target->regex = src->regex; + } + if (src->exprlist) { + target->exprlist = tclistdup(src->exprlist); + } +} + +/* Clone query object */ +static void _qrydup(const EJQ *src, EJQ *target, uint32_t qflags) { + assert(src && target); + memset(target, 0, sizeof (*target)); + target->flags = src->flags | qflags; + target->max = src->max; + target->skip = src->skip; + target->orqobjsnum = src->orqobjsnum; + target->orqobjs = NULL; + const void *kbuf; + int ksz, vsz; + if (src->qobjmap) { + target->qobjmap = tcmapnew2(TCMAPRNUM(src->qobjmap)); + tcmapiterinit(src->qobjmap); + while ((kbuf = tcmapiternext(src->qobjmap, &ksz)) != NULL) { + EJQF qf; + _qryfieldup(tcmapiterval(kbuf, &vsz), &qf, qflags); + tcmapput(target->qobjmap, kbuf, ksz, &qf, sizeof (qf)); + } + } + if (src->hints) { + target->hints = bson_dup(src->hints); + } + if (src->orqobjsnum > 0 && src->orqobjs) { + TCMALLOC(target->orqobjs, sizeof (*(target->orqobjs)) * src->orqobjsnum); + for (int i = 0; i < src->orqobjsnum; ++i) { + _qrydup(src->orqobjs + i, target->orqobjs + i, qflags); + } + } +} + +/** Query */ +static TCLIST* _qrysearch(EJCOLL *jcoll, const EJQ *q, uint32_t *outcount, int qflags, TCXSTR *log) { + //Clone the query object + EJQ *ejq; + TCMALLOC(ejq, sizeof (*ejq)); + _qrydup(q, ejq, EJQINTERNAL); + + *outcount = 0; + bool onlycount = (qflags & EJQRYCOUNT); //quering only for result set count + bool all = false; //need all records + EJQF *mqf = NULL; //main indexed query condition if exists + + TCLIST *res = onlycount ? NULL : tclistnew2(4096); + _qrypreprocess(jcoll, ejq, &mqf); + + + int anum = 0; //number of active conditions + const EJQF **ofs = NULL; //order fields + int ofsz = 0; //order fields count + int aofsz = 0; //active order fields count + const EJQF **qfs = NULL; //condition fields array + const int qfsz = TCMAPRNUM(ejq->qobjmap); //number of all condition fields + if (qfsz > 0) { + TCMALLOC(qfs, qfsz * sizeof (EJQF*)); + } + + const void *kbuf; + int kbufsz; + const void *vbuf; + int vbufsz; + void *bsbuf; //BSON buffer + int bsbufsz; //BSON buffer size + + uint32_t count = 0; //current count + uint32_t max = (ejq->max > 0) ? ejq->max : UINT_MAX; + uint32_t skip = ejq->skip; + const TDBIDX *midx = mqf ? mqf->idx : NULL; + if (midx) { //Main index used for ordering + if (mqf->orderseq == 1 && + !(mqf->tcop == TDBQCSTRAND || mqf->tcop == TDBQCSTROR || mqf->tcop == TDBQCSTRNUMOR)) { + mqf->flags |= EJFORDERUSED; + } + } + tcmapiterinit(ejq->qobjmap); + for (int i = 0; i < qfsz; ++i) { + kbuf = tcmapiternext(ejq->qobjmap, &kbufsz); + EJQF *qf = (EJQF*) tcmapget(ejq->qobjmap, kbuf, kbufsz, &vbufsz); + assert(qf && vbufsz == sizeof (EJQF)); + qfs[i] = qf; + if (qf->fpathsz > 0 && !(qf->flags & EJFEXCLUDED)) { + anum++; + } + if (qf->orderseq) { + ofsz++; + if (onlycount) { + qf->flags |= EJFORDERUSED; + } + } + } + if (ofsz > 0) { //Collect order fields array + TCMALLOC(ofs, ofsz * sizeof (EJQF*)); + for (int i = 0; i < ofsz; ++i) { + for (int j = 0; j < qfsz; ++j) { + if (qfs[j]->orderseq == i + 1) { //orderseq starts with 1 + ofs[i] = qfs[j]; + if (!(ofs[i]->flags & EJFORDERUSED)) aofsz++; + break; + } + } + } + for (int i = 0; i < ofsz; ++i) assert(ofs[i] != NULL); + } + + if (!onlycount && aofsz > 0 && (!midx || mqf->orderseq != 1)) { //Main index is not main order field + all = true; //Need all records for ordering for some other fields + } + + if (log) { + tcxstrprintf(log, "MAX: %u\n", max); + tcxstrprintf(log, "SKIP: %u\n", skip); + tcxstrprintf(log, "COUNT ONLY: %s\n", onlycount ? "YES" : "NO"); + tcxstrprintf(log, "MAIN IDX: '%s'\n", midx ? midx->name : "NONE"); + tcxstrprintf(log, "ORDER FIELDS: %d\n", ofsz); + tcxstrprintf(log, "ACTIVE CONDITIONS: %d\n", anum); + tcxstrprintf(log, "$OR QUERIES: %d\n", ejq->orqobjsnum); + tcxstrprintf(log, "FETCH ALL: %s\n", all ? "TRUE" : "FALSE"); + } + if (max < UINT_MAX - skip) { + max += skip; + } + if (max == 0) { + goto finish; + } + if (!midx) { //Missing main index + goto fullscan; + } + if (log) { + tcxstrprintf(log, "MAIN IDX TCOP: %d\n", mqf->tcop); + } + +#define JBQREGREC(_bsbuf, _bsbufsz) \ + ++count; \ + if (!onlycount && count > skip) { \ + TCLISTPUSH(res, _bsbuf, _bsbufsz); \ + } \ + if (_bsbuf) { \ + TCFREE(_bsbuf); \ + } + + bool trim = (*midx->name != '\0'); + if (anum > 0 && !(mqf->flags & EJFEXCLUDED)) { + anum--; + mqf->flags |= EJFEXCLUDED; + } + if (mqf->tcop == TDBQTRUE) { + BDBCUR *cur = tcbdbcurnew(midx->db); + if (mqf->order >= 0) { + tcbdbcurfirst(cur); + } else { + tcbdbcurlast(cur); + } + while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) { + if (trim) kbufsz -= 3; + vbuf = tcbdbcurval3(cur, &vbufsz); + if (_qryallcondsmatch(onlycount, anum, jcoll, qfs, qfsz, vbuf, vbufsz, &bsbuf, &bsbufsz)) { + JBQREGREC(bsbuf, bsbufsz); + } + if (mqf->order >= 0) { + tcbdbcurnext(cur); + } else { + tcbdbcurprev(cur); + } + } + tcbdbcurdel(cur); + } else if (mqf->tcop == TDBQCSTREQ) { /* string is equal to */ + assert(midx->type == TDBITLEXICAL); + char* expr = mqf->expr; + int exprsz = mqf->exprsz; + BDBCUR *cur = tcbdbcurnew(midx->db); + tcbdbcurjump(cur, expr, exprsz + trim); + while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) { + if (trim) kbufsz -= 3; + if (kbufsz == exprsz && !memcmp(kbuf, expr, exprsz)) { + vbuf = tcbdbcurval3(cur, &vbufsz); + if (_qryallcondsmatch(onlycount, anum, jcoll, qfs, qfsz, vbuf, vbufsz, &bsbuf, &bsbufsz) && + (ejq->orqobjsnum == 0 || _qryormatch(jcoll, ejq, vbuf, vbufsz, bsbuf, bsbufsz))) { + JBQREGREC(bsbuf, bsbufsz); + } + } else { + break; + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + } else if (mqf->tcop == TDBQCSTRBW) { /* string begins with */ + assert(midx->type == TDBITLEXICAL); + char* expr = mqf->expr; + int exprsz = mqf->exprsz; + BDBCUR *cur = tcbdbcurnew(midx->db); + tcbdbcurjump(cur, expr, exprsz + trim); + while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) { + if (trim) kbufsz -= 3; + if (kbufsz >= exprsz && !memcmp(kbuf, expr, exprsz)) { + vbuf = tcbdbcurval3(cur, &vbufsz); + if (_qryallcondsmatch(onlycount, anum, jcoll, qfs, qfsz, vbuf, vbufsz, &bsbuf, &bsbufsz) && + (ejq->orqobjsnum == 0 || _qryormatch(jcoll, ejq, vbuf, vbufsz, bsbuf, bsbufsz))) { + JBQREGREC(bsbuf, bsbufsz); + } + } else { + break; + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + } else if (mqf->tcop == TDBQCSTROREQ) { /* string is equal to at least one token in */ + assert(mqf->ftype == BSON_ARRAY); + assert(midx->type == TDBITLEXICAL); + BDBCUR *cur = tcbdbcurnew(midx->db); + TCLIST *tokens = mqf->exprlist; + assert(tokens); + tclistsort(tokens); + for (int i = 1; i < TCLISTNUM(tokens); i++) { + if (!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))) { + TCFREE(tclistremove2(tokens, i)); + i--; + } + } + if (mqf->order < 0 && (mqf->flags & EJFORDERUSED)) { + tclistinvert(tokens); + } + int tnum = TCLISTNUM(tokens); + for (int i = 0; (all || count < max) && i < tnum; i++) { + const char *token; + int tsiz; + TCLISTVAL(token, tokens, i, tsiz); + if (tsiz < 1) continue; + tcbdbcurjump(cur, token, tsiz + trim); + while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) { + if (trim) kbufsz -= 3; + if (kbufsz == tsiz && !memcmp(kbuf, token, tsiz)) { + vbuf = tcbdbcurval3(cur, &vbufsz); + if (_qryallcondsmatch(onlycount, anum, jcoll, qfs, qfsz, vbuf, vbufsz, &bsbuf, &bsbufsz) && + (ejq->orqobjsnum == 0 || _qryormatch(jcoll, ejq, vbuf, vbufsz, bsbuf, bsbufsz))) { + JBQREGREC(bsbuf, bsbufsz); + } + } else { + break; + } + tcbdbcurnext(cur); + } + } + tcbdbcurdel(cur); + } else if (mqf->tcop == TDBQCNUMEQ) { /* number is equal to */ + assert(midx->type == TDBITDECIMAL); + char* expr = mqf->expr; + int exprsz = mqf->exprsz; + BDBCUR *cur = tcbdbcurnew(midx->db); + _ejdbnum num; + _nufetch(&num, expr, mqf->ftype); + tctdbqryidxcurjumpnum(cur, expr, exprsz, true); + while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) { + if (_nucmp(&num, kbuf, mqf->ftype) == 0) { + vbuf = tcbdbcurval3(cur, &vbufsz); + if (_qryallcondsmatch(onlycount, anum, jcoll, qfs, qfsz, vbuf, vbufsz, &bsbuf, &bsbufsz) && + (ejq->orqobjsnum == 0 || _qryormatch(jcoll, ejq, vbuf, vbufsz, bsbuf, bsbufsz))) { + JBQREGREC(bsbuf, bsbufsz); + } + } else { + break; + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + } else if (mqf->tcop == TDBQCNUMGT || mqf->tcop == TDBQCNUMGE) { + /* number is greater than | number is greater than or equal to */ + assert(midx->type == TDBITDECIMAL); + char* expr = mqf->expr; + int exprsz = mqf->exprsz; + BDBCUR *cur = tcbdbcurnew(midx->db); + _ejdbnum xnum; + _nufetch(&xnum, expr, mqf->ftype); + if (mqf->order < 0 && (mqf->flags & EJFORDERUSED)) { //DESC + tcbdbcurlast(cur); + while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) { + _ejdbnum knum; + _nufetch(&knum, kbuf, mqf->ftype); + int cmp = _nucmp2(&knum, &xnum, mqf->ftype); + if (cmp < 0) break; + if (cmp > 0 || (mqf->tcop == TDBQCNUMGE && cmp >= 0)) { + vbuf = tcbdbcurval3(cur, &vbufsz); + if (_qryallcondsmatch(onlycount, anum, jcoll, qfs, qfsz, vbuf, vbufsz, &bsbuf, &bsbufsz) && + (ejq->orqobjsnum == 0 || _qryormatch(jcoll, ejq, vbuf, vbufsz, bsbuf, bsbufsz))) { + JBQREGREC(bsbuf, bsbufsz); + } + } + tcbdbcurprev(cur); + } + } else { //ASC + tctdbqryidxcurjumpnum(cur, expr, exprsz, true); + while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) { + _ejdbnum knum; + _nufetch(&knum, kbuf, mqf->ftype); + int cmp = _nucmp2(&knum, &xnum, mqf->ftype); + if (cmp > 0 || (mqf->tcop == TDBQCNUMGE && cmp >= 0)) { + vbuf = tcbdbcurval3(cur, &vbufsz); + if (_qryallcondsmatch(onlycount, anum, jcoll, qfs, qfsz, vbuf, vbufsz, &bsbuf, &bsbufsz) && + (ejq->orqobjsnum == 0 || _qryormatch(jcoll, ejq, vbuf, vbufsz, bsbuf, bsbufsz))) { + JBQREGREC(bsbuf, bsbufsz); + } + } + tcbdbcurnext(cur); + } + } + tcbdbcurdel(cur); + } else if (mqf->tcop == TDBQCNUMLT || mqf->tcop == TDBQCNUMLE) { + /* number is less than | number is less than or equal to */ + assert(midx->type == TDBITDECIMAL); + char* expr = mqf->expr; + int exprsz = mqf->exprsz; + BDBCUR *cur = tcbdbcurnew(midx->db); + _ejdbnum xnum; + _nufetch(&xnum, expr, mqf->ftype); + if (mqf->order >= 0) { //ASC + tcbdbcurfirst(cur); + while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) { + _ejdbnum knum; + _nufetch(&knum, kbuf, mqf->ftype); + int cmp = _nucmp2(&knum, &xnum, mqf->ftype); + if (cmp > 0) break; + if (cmp < 0 || (cmp <= 0 && mqf->tcop == TDBQCNUMLE)) { + vbuf = tcbdbcurval3(cur, &vbufsz); + if (_qryallcondsmatch(onlycount, anum, jcoll, qfs, qfsz, vbuf, vbufsz, &bsbuf, &bsbufsz) && + (ejq->orqobjsnum == 0 || _qryormatch(jcoll, ejq, vbuf, vbufsz, bsbuf, bsbufsz))) { + JBQREGREC(bsbuf, bsbufsz); + } + } + tcbdbcurnext(cur); + } + } else { + tctdbqryidxcurjumpnum(cur, expr, exprsz, false); + while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) { + _ejdbnum knum; + _nufetch(&knum, kbuf, mqf->ftype); + int cmp = _nucmp2(&knum, &xnum, mqf->ftype); + if (cmp < 0 || (cmp <= 0 && mqf->tcop == TDBQCNUMLE)) { + vbuf = tcbdbcurval3(cur, &vbufsz); + if (_qryallcondsmatch(onlycount, anum, jcoll, qfs, qfsz, vbuf, vbufsz, &bsbuf, &bsbufsz) && + (ejq->orqobjsnum == 0 || _qryormatch(jcoll, ejq, vbuf, vbufsz, bsbuf, bsbufsz))) { + JBQREGREC(bsbuf, bsbufsz); + } + } + tcbdbcurprev(cur); + } + } + tcbdbcurdel(cur); + } else if (mqf->tcop == TDBQCNUMBT) { /* number is between two tokens of */ + assert(mqf->ftype == BSON_ARRAY); + assert(midx->type == TDBITDECIMAL); + assert(mqf->exprlist); + TCLIST *tokens = mqf->exprlist; + assert(TCLISTNUM(tokens) == 2); + const char* expr; + int exprsz; + long double lower = tctdbatof(tclistval2(tokens, 0)); + long double upper = tctdbatof(tclistval2(tokens, 1)); + expr = tclistval2(tokens, (lower > upper) ? 1 : 0); + exprsz = strlen(expr); + if (lower > upper) { + long double swap = lower; + lower = upper; + upper = swap; + } + BDBCUR *cur = tcbdbcurnew(midx->db); + tctdbqryidxcurjumpnum(cur, expr, exprsz, true); + while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) { + if (tctdbatof(kbuf) > upper) break; + vbuf = tcbdbcurval3(cur, &vbufsz); + if (_qryallcondsmatch(onlycount, anum, jcoll, qfs, qfsz, vbuf, vbufsz, &bsbuf, &bsbufsz) && + (ejq->orqobjsnum == 0 || _qryormatch(jcoll, ejq, vbuf, vbufsz, bsbuf, bsbufsz))) { + JBQREGREC(bsbuf, bsbufsz); + } + tcbdbcurnext(cur); + } + tcbdbcurdel(cur); + if (!all && !onlycount && mqf->order < 0 && (mqf->flags & EJFORDERUSED)) { //DESC + tclistinvert(res); + } + } else if (mqf->tcop == TDBQCNUMOREQ) { /* number is equal to at least one token in */ + assert(mqf->ftype == BSON_ARRAY); + assert(midx->type == TDBITDECIMAL); + BDBCUR *cur = tcbdbcurnew(midx->db); + TCLIST *tokens = mqf->exprlist; + assert(tokens); + tclistsortex(tokens, tdbcmppkeynumasc); + for (int i = 1; i < TCLISTNUM(tokens); i++) { + if (tctdbatof(TCLISTVALPTR(tokens, i)) == tctdbatof(TCLISTVALPTR(tokens, i - 1))) { + TCFREE(tclistremove2(tokens, i)); + i--; + } + } + if (mqf->order < 0 && (mqf->flags & EJFORDERUSED)) { + tclistinvert(tokens); + } + int tnum = TCLISTNUM(tokens); + for (int i = 0; (all || count < max) && i < tnum; i++) { + const char *token; + int tsiz; + TCLISTVAL(token, tokens, i, tsiz); + if (tsiz < 1) continue; + long double xnum = tctdbatof(token); + tctdbqryidxcurjumpnum(cur, token, tsiz, true); + while ((all || count < max) && (kbuf = tcbdbcurkey3(cur, &kbufsz)) != NULL) { + if (tctdbatof(kbuf) == xnum) { + vbuf = tcbdbcurval3(cur, &vbufsz); + if (_qryallcondsmatch(onlycount, anum, jcoll, qfs, qfsz, vbuf, vbufsz, &bsbuf, &bsbufsz) && + (ejq->orqobjsnum == 0 || _qryormatch(jcoll, ejq, vbuf, vbufsz, bsbuf, bsbufsz))) { + JBQREGREC(bsbuf, bsbufsz); + } + } else { + break; + } + tcbdbcurnext(cur); + } + } + tcbdbcurdel(cur); + } else if (mqf->tcop == TDBQCSTRAND || mqf->tcop == TDBQCSTROR || mqf->tcop == TDBQCSTRNUMOR) { + /* string includes all tokens in | string includes at least one token in */ + assert(midx->type == TDBITTOKEN); + assert(mqf->ftype == BSON_ARRAY); + TCLIST *tokens = mqf->exprlist; + assert(tokens); + if (mqf->tcop == TDBQCSTRNUMOR) { + tclistsortex(tokens, tdbcmppkeynumasc); + for (int i = 1; i < TCLISTNUM(tokens); i++) { + if (tctdbatof(TCLISTVALPTR(tokens, i)) == tctdbatof(TCLISTVALPTR(tokens, i - 1))) { + TCFREE(tclistremove2(tokens, i)); + i--; + } + } + } else { + tclistsort(tokens); + for (int i = 1; i < TCLISTNUM(tokens); i++) { + if (!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))) { + TCFREE(tclistremove2(tokens, i)); + i--; + } + } + } + TCMAP *tres = tctdbidxgetbytokens(jcoll->tdb, midx, tokens, mqf->tcop, log); + tcmapiterinit(tres); + while ((all || count < max) && (kbuf = tcmapiternext(tres, &kbufsz)) != NULL) { + if (_qryallcondsmatch(onlycount, anum, jcoll, qfs, qfsz, kbuf, kbufsz, &bsbuf, &bsbufsz) && + (ejq->orqobjsnum == 0 || _qryormatch(jcoll, ejq, kbuf, kbufsz, bsbuf, bsbufsz))) { + JBQREGREC(bsbuf, bsbufsz); + } + } + tcmapdel(tres); + } + + if (onlycount) { + goto finish; + } else { + goto sorting; + } + +fullscan: /* Full scan */ + assert(count == 0); + assert(!res || TCLISTNUM(res) == 0); + if (log) { + tcxstrprintf(log, "RUN FULLSCAN\n"); + } + char *pkbuf; + int pkbufsz; + char *lkbuf = NULL; + int lksiz = 0; + const char *cbuf; + int cbufsz; + TCHDB *hdb = jcoll->tdb->hdb; + assert(hdb); + while ((all || count < max) && (pkbuf = tchdbgetnext3(hdb, lkbuf, lksiz, &pkbufsz, &cbuf, &cbufsz)) != NULL) { + void *bsbuf = tcmaploadone(cbuf, cbufsz, JDBCOLBSON, JDBCOLBSONL, &bsbufsz); + if (!bsbuf) continue; + bool matched = true; + for (int i = 0; i < qfsz; ++i) { + const EJQF *qf = qfs[i]; + if (qf->flags & EJFEXCLUDED) continue; + if (!_qrybsmatch(qf, bsbuf, bsbufsz)) { + matched = false; + break; + } + } + if (matched && (ejq->orqobjsnum == 0 || _qryormatch(jcoll, ejq, pkbuf, pkbufsz, bsbuf, bsbufsz))) { + JBQREGREC(bsbuf, bsbufsz); + } else { + TCFREE(bsbuf); + } + if (lkbuf) TCFREE(lkbuf); + lkbuf = pkbuf; + lksiz = pkbufsz; + } + if (lkbuf) TCFREE(lkbuf); + +sorting: /* Sorting resultset */ + if (!res || aofsz <= 0) { //No sorting needed + goto finish; + } + _ejbsortctx sctx; //sorting context + sctx.ofs = ofs; + sctx.ofsz = ofsz; + if (ejdbtimsortlist(res, _ejdbsoncmp, &sctx)) { + _ejdbsetecode(jcoll->jb, JDBEQRSSORTING, __FILE__, __LINE__, __func__); + } +finish: + //revert max + if (max < UINT_MAX && max > skip) { + max = max - skip; + } + if (res && TCLISTNUM(res) > max) { //truncate resulting list if max specified + int end = res->start + res->num; + TCLISTDATUM *array = res->array; + for (int i = (res->start + max); i < end; i++) { + TCFREE(array[i].ptr); + --(res->num); + } + } + count = (skip < count) ? count - skip : 0; + if (count > max) { + count = max; + } + *outcount = count; + if (log) { + tcxstrprintf(log, "RS COUNT: %d\n", count); + tcxstrprintf(log, "RS SIZE: %d\n", (res ? TCLISTNUM(res) : 0)); + tcxstrprintf(log, "FINAL SORTING: %s\n", (onlycount || aofsz <= 0) ? "FALSE" : "TRUE"); + } + if (qfs) { + TCFREE(qfs); + } + if (ofs) { + TCFREE(ofs); + } + ejdbquerydel(ejq); +#undef JBQREGREC + return res; +} + +static TDBIDX* _qryfindidx(EJCOLL *jcoll, EJQF *qf, bson *idxmeta) { + TCTDB *tdb = jcoll->tdb; + char p = '\0'; + switch (qf->tcop) { + case TDBQCSTREQ: + case TDBQCSTRBW: + case TDBQCSTROREQ: + p = 's'; //lexical string index + break; + case TDBQCNUMEQ: + case TDBQCNUMGT: + case TDBQCNUMGE: + case TDBQCNUMLT: + case TDBQCNUMLE: + case TDBQCNUMBT: + case TDBQCNUMOREQ: + p = 'n'; //number index + break; + case TDBQCSTRAND: + case TDBQCSTROR: + p = 'a'; //token index + break; + case TDBQTRUE: + p = 'o'; //take first appropriate index + break; + } + if (p == '\0' || !qf->fpath || !qf->fpathsz) { + return NULL; + } + for (int i = 0; i < tdb->inum; ++i) { + TDBIDX *idx = tdb->idxs + i; + assert(idx); + if (p == 'o') { + if (*idx->name == 'a') { + continue; + } + } else if (*idx->name != p) { + continue; + } + if (!strcmp(qf->fpath, idx->name + 1)) { + return idx; + } + } + //No direct operation index. Any alternatives? + if (idxmeta && (qf->tcop == TDBQCSTREQ || qf->tcop == TDBQCSTROREQ || qf->tcop == TDBQCNUMOREQ)) { + bson_iterator it; + bson_type bt = bson_find(&it, idxmeta, "iflags"); + if (bt != BSON_INT) { + return NULL; + } + int iflags = bson_iterator_int(&it); + if (iflags & JDIDXARR) { //array token index exists so convert qf into TDBQCSTROR + for (int i = 0; i < tdb->inum; ++i) { + TDBIDX *idx = tdb->idxs + i; + if (!strcmp(qf->fpath, idx->name + 1)) { + if (qf->tcop == TDBQCSTREQ) { + qf->tcop = TDBQCSTROR; + qf->exprlist = tclistnew2(1); + TCLISTPUSH(qf->exprlist, qf->expr, qf->exprsz); + if (qf->expr) TCFREE(qf->expr); + qf->expr = tclistdump(qf->exprlist, &qf->exprsz); + qf->ftype = BSON_ARRAY; + return idx; + } else if (qf->tcop == TDBQCSTROREQ) { + assert(qf->ftype == BSON_ARRAY); + qf->tcop = TDBQCSTROR; + return idx; + } else if (qf->tcop == TDBQCNUMOREQ) { + assert(qf->ftype == BSON_ARRAY); + qf->tcop = TDBQCSTRNUMOR; + return idx; + } + } + } + } + } + return NULL; +} + +static void _qrypreprocess(EJCOLL *jcoll, EJQ *ejq, EJQF **mqf) { + assert(jcoll && ejq && ejq->qobjmap && mqf); + + *mqf = NULL; + EJQF *oqf = NULL; //Order condition + + //Iterate over query fiels + TCMAP *qmap = ejq->qobjmap; + tcmapiterinit(qmap); + const void *mkey; + int mkeysz; + int mvalsz; + bson_iterator it; + bson_type bt; + + if (ejq->hints) { + bson_type bt; + bson_iterator it, sit; + //Process $orderby + bt = bson_find(&it, ejq->hints, "$orderby"); + if (bt == BSON_OBJECT) { + int orderseq = 1; + bson_iterator_subiterator(&it, &sit); + while ((bt = bson_iterator_next(&sit)) != BSON_EOO) { + if (!BSON_IS_NUM_TYPE(bt)) { + continue; + } + const char *ofield = bson_iterator_key(&sit); + int odir = bson_iterator_int(&sit); + odir = (odir > 0) ? 1 : (odir < 0 ? -1 : 0); + if (!odir) { + continue; + } + EJQF nqf; + EJQF *qf = (EJQF*) tcmapget(qmap, ofield, strlen(ofield), &mvalsz); //discard const + if (qf == NULL) { //Create syntetic query field for orderby ops + memset(&nqf, 0, sizeof (EJQF)); + nqf.fpath = tcstrdup(ofield); + nqf.fpathsz = strlen(nqf.fpath); + nqf.expr = tcstrdup(""); + nqf.exprsz = 0; + nqf.tcop = TDBQTRUE; //disable any TC matching operation + nqf.ftype = BSON_OBJECT; + nqf.orderseq = orderseq++; + nqf.order = odir; + nqf.flags |= EJFEXCLUDED; //field excluded from matching + qf = &nqf; + tcmapput(qmap, ofield, strlen(ofield), qf, sizeof (*qf)); + } else { + qf->orderseq = orderseq++; + qf->order = odir; + } + } + } + bt = bson_find(&it, ejq->hints, "$skip"); + if (BSON_IS_NUM_TYPE(bt)) { + int64_t v = bson_iterator_long(&it); + ejq->skip = (uint32_t) ((v < 0) ? 0 : v); + } + bt = bson_find(&it, ejq->hints, "$max"); + if (BSON_IS_NUM_TYPE(bt)) { + int64_t v = bson_iterator_long(&it); + ejq->max = (uint32_t) ((v < 0) ? 0 : v); + } + } + + const int scoreexact = 100; + const int scoregtlt = 50; + int maxiscore = 0; //Maximum index score + int maxselectivity = 0; + + while ((mkey = tcmapiternext(qmap, &mkeysz)) != NULL) { + int iscore = 0; + EJQF *qf = (EJQF*) tcmapget(qmap, mkey, mkeysz, &mvalsz); //discard const + assert(mvalsz == sizeof (EJQF)); + assert(qf->fpath); + bool firstorderqf = false; + qf->idxmeta = _imetaidx(jcoll, qf->fpath); + qf->idx = _qryfindidx(jcoll, qf, qf->idxmeta); + if (qf->order && qf->orderseq == 1) { //Index for first 'orderby' exists + oqf = qf; + firstorderqf = true; + } + if (!qf->idx || !qf->idxmeta) { + qf->idx = NULL; + qf->idxmeta = NULL; + continue; + } + if (qf->tcop == TDBQTRUE || qf->negate) { + continue; + } + int avgreclen = -1; + int selectivity = -1; + bt = bson_find(&it, qf->idxmeta, "selectivity"); + if (bt == BSON_DOUBLE) { + selectivity = (int) ((double) bson_iterator_double(&it) * 100); //Selectivity percent + } + bt = bson_find(&it, qf->idxmeta, "avgreclen"); + if (bt == BSON_DOUBLE) { + avgreclen = (int) bson_iterator_double(&it); + } + if (selectivity > 0) { + if (selectivity <= 20) { //Not using index at all if selectivity lesser than 20% + continue; + } + iscore += selectivity; + } + if (firstorderqf) { + iscore += (maxselectivity - selectivity) / 2; + } + if (selectivity > maxselectivity) { + maxselectivity = selectivity; + } + switch (qf->tcop) { + case TDBQCSTREQ: + case TDBQCSTROR: + case TDBQCNUMEQ: + case TDBQCNUMBT: + iscore += scoreexact; + break; + case TDBQCSTRBW: + case TDBQCSTREW: + if (avgreclen > 0 && qf->exprsz > avgreclen) { + iscore += scoreexact; + } + break; + case TDBQCNUMGT: + case TDBQCNUMGE: + case TDBQCNUMLT: + case TDBQCNUMLE: + if (firstorderqf) { + iscore += scoreexact; + } else { + iscore += scoregtlt; + } + break; + } + if (iscore >= maxiscore) { + *mqf = qf; + maxiscore = iscore; + } + } + + if (*mqf == NULL && (oqf && oqf->idx && !oqf->negate)) { + *mqf = oqf; + } +} + +static bool _metasetopts(EJDB *jb, const char *colname, EJCOLLOPTS *opts) { + bool rv = true; + if (!opts) { + return _metasetbson(jb, colname, strlen(colname), "opts", NULL, false, false); + } + bson *bsopts = bson_create(); + bson_init(bsopts); + bson_append_bool(bsopts, "compressed", opts->compressed); + bson_append_bool(bsopts, "large", opts->large); + bson_append_int(bsopts, "cachedrecords", opts->cachedrecords); + bson_append_int(bsopts, "records", opts->records); + bson_finish(bsopts); + rv = _metasetbson(jb, colname, strlen(colname), "opts", bsopts, false, false); + bson_del(bsopts); + return rv; +} + +static bool _metagetopts(EJDB *jb, const char *colname, EJCOLLOPTS *opts) { + assert(opts); + bool rv = true; + memset(opts, 0, sizeof (*opts)); + bson *bsopts = _metagetbson(jb, colname, strlen(colname), "opts"); + if (!bsopts) { + return true; + } + bson_iterator it; + bson_type bt = bson_find(&it, bsopts, "compressed"); + if (bt == BSON_BOOL) { + opts->compressed = bson_iterator_bool(&it); + } + bt = bson_find(&it, bsopts, "large"); + if (bt == BSON_BOOL) { + opts->large = bson_iterator_bool(&it); + } + bt = bson_find(&it, bsopts, "cachedrecords"); + if (BSON_IS_NUM_TYPE(bt)) { + opts->cachedrecords = bson_iterator_long(&it); + } + bt = bson_find(&it, bsopts, "records"); + if (BSON_IS_NUM_TYPE(bt)) { + opts->records = bson_iterator_long(&it); + } + return rv; +} + +static bool _metasetbson(EJDB *jb, const char *colname, int colnamesz, + const char *mkey, bson* val, bool merge, bool mergeoverwrt) { + assert(jb && colname && mkey); + bool rv = true; + bson *bsave = NULL; + bson *oldval = NULL; + bson mresult; + + TCMAP *cmeta = tctdbget(jb->metadb, colname, colnamesz); + if (!cmeta) { + _ejdbsetecode(jb, JDBEMETANVALID, __FILE__, __LINE__, __func__); + rv = false; + goto finish; + } + if (!val) { + if (tcmapout2(cmeta, mkey)) { + rv = tctdbput(jb->metadb, colname, colnamesz, cmeta); + } + goto finish; + } + assert(val); + if (merge) { //Merged + oldval = _metagetbson(jb, colname, colnamesz, mkey); + if (oldval) { + bson_init(&mresult); + bson_merge(oldval, val, mergeoverwrt, &mresult); + bson_finish(&mresult); + bsave = &mresult; + } else { + bsave = val; + } + } else { //Rewrited + bsave = val; + } + + assert(bsave); + tcmapput(cmeta, mkey, strlen(mkey), bson_data(bsave), bson_size(bsave)); + rv = tctdbput(jb->metadb, colname, colnamesz, cmeta); +finish: + if (oldval) { + if (merge) { + bson_destroy(bsave); + } + bson_del(oldval); + } + if (cmeta) { + tcmapdel(cmeta); + } + tctdbsync(jb->metadb); + return rv; +} + +static bool _metasetbson2(EJCOLL *jcoll, const char *mkey, bson* val, bool merge, bool mergeoverwrt) { + assert(jcoll); + return _metasetbson(jcoll->jb, jcoll->cname, jcoll->cnamesz, mkey, val, merge, mergeoverwrt); +} + +/**Returned meta BSON data must be freed by 'bson_del' */ +static bson* _metagetbson(EJDB *jb, const char *colname, int colnamesz, const char* mkey) { + assert(jb && colname && mkey); + bson *rv = NULL; + TCMAP *cmeta = tctdbget(jb->metadb, colname, colnamesz); + if (!cmeta) { + _ejdbsetecode(jb, JDBEMETANVALID, __FILE__, __LINE__, __func__); + return NULL; + } + int bsz; + const void* raw = tcmapget(cmeta, mkey, strlen(mkey), &bsz); + if (!raw || bsz == 0) { + goto finish; + } + rv = bson_create(); + bson_init_size(rv, bsz); + bson_ensure_space(rv, bsz - 4); + bson_append(rv, ((char*) raw) + 4, bsz - (4 + 1/*BSON_EOO*/)); + bson_finish(rv); +finish: + tcmapdel(cmeta); + return rv; +} + +static bson* _metagetbson2(EJCOLL *jcoll, const char *mkey) { + assert(jcoll); + return _metagetbson(jcoll->jb, jcoll->cname, jcoll->cnamesz, mkey); +} + +/**Returned index meta if not NULL it must be freed by 'bson_del' */ +static bson* _imetaidx(EJCOLL *jcoll, const char *ipath) { + assert(jcoll && ipath); + if (*ipath == '\0') { + return NULL; + } + bson *rv = NULL; + char fpathkey[BSON_MAX_FPATH_LEN + 1]; + TCMAP *cmeta = tctdbget(jcoll->jb->metadb, jcoll->cname, jcoll->cnamesz); + if (!cmeta) { + _ejdbsetecode(jcoll->jb, JDBEMETANVALID, __FILE__, __LINE__, __func__); + goto finish; + } + int klen = snprintf(fpathkey, BSON_MAX_FPATH_LEN + 1, "i%s", ipath); //'i' prefix for all columns with index meta + if (klen > BSON_MAX_FPATH_LEN) { + _ejdbsetecode(jcoll->jb, JDBEFPATHINVALID, __FILE__, __LINE__, __func__); + goto finish; + } + int bsz; + const void* bsdata = tcmapget(cmeta, fpathkey, klen, &bsz); + if (bsdata) { + rv = bson_create(); + bson_init_size(rv, bsz); + bson_ensure_space(rv, bsz - 4); + bson_append(rv, ((char*) bsdata) + 4, bsz - (4 + 1)); + bson_finish(rv); + } +finish: + if (cmeta) { + tcmapdel(cmeta); + } + return rv; +} + +/** Free EJQF field **/ +static void _delqfdata(const EJQ *q, const EJQF *qf) { + assert(q && qf); + if (qf->expr) { + TCFREE(qf->expr); + } + if (qf->fpath) { + TCFREE(qf->fpath); + } + if (qf->idxmeta) { + bson_del(qf->idxmeta); + } + if (qf->regex && !(EJQINTERNAL & q->flags)) { + //We do not clear regex_t data because it not deep copy in internal queries + regfree(qf->regex); + TCFREE(qf->regex); + } + if (qf->exprlist) { + tclistdel(qf->exprlist); + } +} + +/** + * Copy BSON array into new TCLIST. TCLIST must be freed by 'tclistdel'. + * @param it BSON iterator + * @param type[out] Detected BSON type of last element + */ +static TCLIST* _fetch_bson_str_array(bson_iterator *it, bson_type *type) { + TCLIST *res = tclistnew(); + *type = BSON_EOO; + bson_type ftype; + for (int i = 0; (ftype = bson_iterator_next(it)) != BSON_EOO; ++i) { + switch (ftype) { + case BSON_SYMBOL: + case BSON_STRING: + *type = ftype; + tclistpush2(res, bson_iterator_string(it)); + break; + case BSON_INT: + case BSON_LONG: + *type = ftype; + tclistprintf(res, "%ld", bson_iterator_long(it)); + break; + case BSON_DOUBLE: + *type = ftype; + tclistprintf(res, "%f", bson_iterator_double(it)); + break; + default: + break; + } + } + return res; +} + +/** result must be freed by TCFREE */ +static char* _fetch_bson_str_array2(bson_iterator *it, bson_type *type) { + TCLIST *res = _fetch_bson_str_array(it, type); + char* tokens = tcstrjoin(res, ','); + tclistdel(res); + return tokens; +} + +static int _parse_qobj_impl(EJDB* jb, bson_iterator *it, TCMAP *qmap, TCLIST *pathStack, EJQF *pqf) { + assert(it && qmap && pathStack); + int ret = 0; + bson_type ftype; + while ((ftype = bson_iterator_next(it)) != BSON_EOO) { + + const char* fkey = bson_iterator_key(it); + bool isckey = (*fkey == '$'); //Key is a control key: $in, $nin, $not, $all + + if (!isckey && pqf && pqf->ftype == BSON_ARRAY) { + //All index keys in array are prefixed with '*'. Eg: 'store.*1.item', 'store.*2.item', ... + char akey[TCNUMBUFSIZ]; + assert(strlen(fkey) <= TCNUMBUFSIZ - 2); + akey[0] = '*'; + memmove(akey + 1, fkey, strlen(fkey) + 1); + fkey = akey; + } + + EJQF qf; + memset(&qf, 0, sizeof (qf)); + + if (!isckey) { + //Push key on top of path stack + tclistpush2(pathStack, fkey); + qf.ftype = ftype; + } else { + if (!pqf) { //Require parent query object + ret = JDBQINVALID; + break; + } + qf = *pqf; + if (!strcmp("$not", fkey)) { + qf.negate = !qf.negate; + } else if (!strcmp("$gt", fkey)) { + qf.flags |= EJCOMPGT; + } else if (!strcmp("$gte", fkey)) { + qf.flags |= EJCOMPGTE; + } else if (!strcmp("$lt", fkey)) { + qf.flags |= EJCOMPLT; + } else if (!strcmp("$lte", fkey)) { + qf.flags |= EJCOMPLTE; + } else if (!strcmp("$begin", fkey)) { + qf.flags |= EJCONDSTARTWITH; + } + } + + switch (ftype) { + case BSON_ARRAY: + { + if (isckey) { + if (strcmp("$in", fkey) && + strcmp("$nin", fkey) && + strcmp("$bt", fkey) && + strcmp("$strand", fkey) && + strcmp("$stror", fkey)) { + ret = JDBEQINVALIDQCONTROL; + break; + } + bson_iterator sit; + bson_iterator_subiterator(it, &sit); + bson_type atype = 0; + TCLIST *tokens = _fetch_bson_str_array(&sit, &atype); + if (atype == 0) { + ret = JDBEQINOPNOTARRAY; + tclistdel(tokens); + break; + } + assert(!qf.expr); + assert(!qf.fpath); + qf.ftype = BSON_ARRAY; + qf.expr = tclistdump(tokens, &qf.exprsz); + qf.exprlist = tokens; + if (!strcmp("$in", fkey) || !strcmp("$nin", fkey)) { + if (!strcmp("$nin", fkey)) { + qf.negate = true; + } + if (BSON_IS_NUM_TYPE(atype)) { + qf.tcop = TDBQCNUMOREQ; + } else { + qf.tcop = TDBQCSTROREQ; + } + } else if (!strcmp("$bt", fkey)) { //between + qf.tcop = TDBQCNUMBT; + if (TCLISTNUM(tokens) != 2) { + ret = JDBEQINOPNOTARRAY; + TCFREE(qf.expr); + tclistdel(qf.exprlist); + break; + } + } else if (!strcmp("$strand", fkey)) { //$strand + qf.tcop = TDBQCSTRAND; + } else { //$stror + qf.tcop = TDBQCSTROR; + } + qf.fpath = tcstrjoin(pathStack, '.'); + qf.fpathsz = strlen(qf.fpath); + tcmapputkeep(qmap, qf.fpath, strlen(qf.fpath), &qf, sizeof (qf)); + break; + } else { + bson_iterator sit; + bson_iterator_subiterator(it, &sit); + ret = _parse_qobj_impl(jb, &sit, qmap, pathStack, &qf); + break; + } + } + + case BSON_OBJECT: + { + bson_iterator sit; + bson_iterator_subiterator(it, &sit); + if (isckey) { + + } + ret = _parse_qobj_impl(jb, &sit, qmap, pathStack, &qf); + break; + } + case BSON_SYMBOL: + case BSON_STRING: + { + assert(!qf.fpath && !qf.expr); + qf.ftype = ftype; + qf.expr = tcstrdup(bson_iterator_string(it)); + qf.exprsz = strlen(qf.expr); + qf.fpath = tcstrjoin(pathStack, '.'); + qf.fpathsz = strlen(qf.fpath); + if (qf.flags & EJCONDSTARTWITH) { + qf.tcop = TDBQCSTRBW; + } else { + qf.tcop = TDBQCSTREQ; + } + tcmapputkeep(qmap, qf.fpath, strlen(qf.fpath), &qf, sizeof (qf)); + break; + } + case BSON_LONG: + case BSON_DOUBLE: + case BSON_INT: + { + assert(!qf.fpath && !qf.expr); + qf.ftype = ftype; + qf.fpath = tcstrjoin(pathStack, '.'); + qf.fpathsz = strlen(qf.fpath); + if (ftype == BSON_LONG || ftype == BSON_INT) { + qf.exprlongval = bson_iterator_long(it); + qf.exprdblval = qf.exprlongval; + qf.expr = tcsprintf("%ld", qf.exprlongval); + } else { + qf.exprdblval = bson_iterator_double(it); + qf.exprlongval = (int64_t) qf.exprdblval; + qf.expr = tcsprintf("%f", qf.exprdblval); + } + qf.exprsz = strlen(qf.expr); + if (qf.flags & EJCOMPGT) { + qf.tcop = TDBQCNUMGT; + } else if (qf.flags & EJCOMPGTE) { + qf.tcop = TDBQCNUMGE; + } else if (qf.flags & EJCOMPLT) { + qf.tcop = TDBQCNUMLT; + } else if (qf.flags & EJCOMPLTE) { + qf.tcop = TDBQCNUMLE; + } else { + qf.tcop = TDBQCNUMEQ; + } + tcmapputkeep(qmap, qf.fpath, strlen(qf.fpath), &qf, sizeof (qf)); + break; + } + case BSON_REGEX: + { + assert(!qf.fpath && !qf.expr); + qf.ftype = ftype; + qf.tcop = TDBQCSTRRX; + char* re = tcstrdup(bson_iterator_regex(it)); + const char* opts = bson_iterator_regex_opts(it); + qf.fpath = tcstrjoin(pathStack, '.'); + qf.fpathsz = strlen(qf.fpath); + qf.expr = re; + qf.exprsz = strlen(qf.expr); + const char *rxstr = qf.expr; + regex_t rxbuf; + int rxopt = REG_EXTENDED | REG_NOSUB; + if (strchr(opts, 'i')) { + rxopt |= REG_ICASE; + } + if (regcomp(&rxbuf, rxstr, rxopt) == 0) { + TCMALLOC(qf.regex, sizeof (rxbuf)); + memcpy(qf.regex, &rxbuf, sizeof (rxbuf)); + } else { + ret = JDBEQINVALIDQRX; + TCFREE(qf.fpath); + TCFREE(qf.expr); + break; + } + tcmapputkeep(qmap, qf.fpath, strlen(qf.fpath), &qf, sizeof (qf)); + break; + } + case BSON_NULL: + case BSON_UNDEFINED: + qf.ftype = ftype; + qf.tcop = TDBQCEXIST; + qf.negate = !qf.negate; + qf.expr = tcstrdup(""); //Empty string as expr + qf.exprsz = 0; + tcmapputkeep(qmap, qf.fpath, strlen(qf.fpath), &qf, sizeof (qf)); + break; + default: + break; + }; + + if (!isckey) { + assert(pathStack->num > 0); + TCFREE(tclistpop2(pathStack)); + } + if (ret) { //cleanup on error condition + //TODO better error reporting + _ejdbsetecode(jb, ret, __FILE__, __LINE__, __func__); + break; + } + } + + return ret; +} + +/** + * Convert bson query spec into field path -> EJQF instance. + * Created map instance must be freed `tcmapdel`. + * Each element of map must be freed by TODO + */ +static TCMAP* _parseqobj(EJDB* jb, bson *qspec) { + assert(qspec); + int rv = 0; + TCMAP *res = tcmapnew(); + bson_iterator it; + bson_iterator_init(&it, qspec); + TCLIST *pathStack = tclistnew(); + rv = _parse_qobj_impl(jb, &it, res, pathStack, NULL); + if (rv) { + tcmapdel(res); + res = NULL; + } + assert(!pathStack->num); + tclistdel(pathStack); + return res; +} + +EJDB_INLINE bool _isvalidoidstr(const char *oid) { + if (!oid) { + return false; + } + int i = 0; + for (; oid[i] != '\0' && + ((oid[i] >= 0x30 && oid[i] <= 0x39) || /* 1 - 9 */ + (oid[i] >= 0x61 && oid[i] <= 0x66)); ++i); /* a - f */ + return (i == 24); +} + +/** + * Get OID value from the '_id' field of specified bson object. + * @param bson[in] BSON object + * @param oid[out] Pointer to OID type + * @return True if OID value is found int _id field of bson object otherwise False. + */ +static bool _bsonoidkey(bson *bs, bson_oid_t *oid) { + bool rv = false; + bson_iterator it; + bson_type t = bson_find(&it, bs, JDBIDKEYNAME); + if (t != BSON_STRING && t != BSON_OID) { + goto finish; + } + if (t == BSON_STRING) { + const char* oidstr = bson_iterator_string(&it); + if (!_isvalidoidstr(oidstr)) { + goto finish; + } + } + *oid = *bson_iterator_oid(&it); + rv = true; +finish: + return rv; +} + +/** + * Return string value representation of value pointed by 'it'. + * Resulting value size stored into 'vsz'. + * If returned value is not NULL it must be freed by TCFREE. + */ +static char* _bsonitstrval(bson_iterator *it, int *vsz, TCLIST *tokens) { + char *ret = NULL; + bson_type btype = bson_iterator_type(it); + if (btype == BSON_STRING) { + if (tokens) { //split string into tokens and push it into 'tokens' list + const unsigned char *sp = (unsigned char *) bson_iterator_string(it); + while (*sp != '\0') { + while ((*sp != '\0' && *sp <= ' ') || *sp == ',') { + sp++; + } + const unsigned char *ep = sp; + while (*ep > ' ' && *ep != ',') { + ep++; + } + if (ep > sp) TCLISTPUSH(tokens, sp, ep - sp); + sp = ep; + } + *vsz = 0; + ret = NULL; + } else { + *vsz = bson_iterator_string_len(it) - 1; + ret = (*vsz > 0) ? tcmemdup(bson_iterator_string(it), *vsz) : NULL; + } + } else if (BSON_IS_NUM_TYPE(btype)) { + char nbuff[TCNUMBUFSIZ]; + if (btype == BSON_INT || btype == BSON_LONG) { + *vsz = bson_numstrn(nbuff, TCNUMBUFSIZ, bson_iterator_long(it)); + if (*vsz >= TCNUMBUFSIZ) { + *vsz = TCNUMBUFSIZ - 1; + nbuff[TCNUMBUFSIZ - 1] = '\0'; + } + ret = tcmemdup(nbuff, *vsz); + } else if (btype == BSON_DOUBLE) { + *vsz = snprintf(nbuff, TCNUMBUFSIZ, "%lf", bson_iterator_double(it)); + if (*vsz >= TCNUMBUFSIZ) { + *vsz = TCNUMBUFSIZ - 1; + nbuff[TCNUMBUFSIZ - 1] = '\0'; + } + ret = tcmemdup(nbuff, *vsz); + } + } else if (btype == BSON_ARRAY) { + bson_type eltype; //last element bson type + bson_iterator sit; + bson_iterator_subiterator(it, &sit); + if (tokens) { + while ((eltype = bson_iterator_next(&sit)) != BSON_EOO) { + int vz = 0; + char *v = _bsonitstrval(&sit, &vz, NULL); + if (v) { + TCLISTPUSH(tokens, v, vz); + TCFREE(v); + } + } + ret = NULL; + *vsz = 0; + } else { + //Array elements are joined with ',' delimeter. + ret = _fetch_bson_str_array2(&sit, &eltype); + *vsz = strlen(ret); + } + } else { + *vsz = 0; + } + return ret; +} + +static char* _bsonipathrowldr( + TCLIST *tokens, + const char *pkbuf, int pksz, + const char *rowdata, int rowdatasz, + const char *ipath, int ipathsz, void *op, int *vsz) { + char* res = NULL; + if (ipath && *ipath == '\0') { //PK + if (tokens) { + const unsigned char *sp = (unsigned char *) pkbuf; + while (*sp != '\0') { + while ((*sp != '\0' && *sp <= ' ') || *sp == ',') { + sp++; + } + const unsigned char *ep = sp; + while (*ep > ' ' && *ep != ',') { + ep++; + } + if (ep > sp) TCLISTPUSH(tokens, sp, ep - sp); + sp = ep; + } + *vsz = 0; + return NULL; + } else { + TCMEMDUP(res, pkbuf, pksz); + *vsz = pksz; + return res; + } + } + if (!ipath || ipathsz < 2 || *(ipath + 1) == '\0' || (*ipath != 's' && *ipath != 'n' && *ipath != 'a')) { + return NULL; + } + //skip index type prefix char with (fpath + 1) + return _bsonfpathrowldr(tokens, rowdata, rowdatasz, ipath + 1, ipathsz - 1, op, vsz); +} + +static char* _bsonfpathrowldr(TCLIST *tokens, const char *rowdata, int rowdatasz, + const char *fpath, int fpathsz, void *op, int *vsz) { + char *ret = NULL; + int bsize; + bson_iterator it; + char *bsdata = tcmaploadone(rowdata, rowdatasz, JDBCOLBSON, JDBCOLBSONL, &bsize); + if (!bsdata) { + *vsz = 0; + return NULL; + } + bson_iterator_from_buffer(&it, bsdata); + bson_find_fieldpath_value2(fpath, fpathsz, &it); + ret = _bsonitstrval(&it, vsz, tokens); + TCFREE(bsdata); + return ret; +} + +static bool _updatebsonidx(EJCOLL *jcoll, const bson_oid_t *oid, const bson *bs, + const void *obsdata, int obsdatasz) { + bool rv = true; + TCMAP *cmeta = tctdbget(jcoll->jb->metadb, jcoll->cname, jcoll->cnamesz); + if (!cmeta) { + _ejdbsetecode(jcoll->jb, JDBEMETANVALID, __FILE__, __LINE__, __func__); + return false; + } + TCMAP *imap = NULL; //New index map + TCMAP *rimap = NULL; //Remove index map + bson_type mt = BSON_EOO; + bson_type ft = BSON_EOO; + bson_type oft = BSON_EOO; + bson_iterator fit, oit, mit; + int bsz; + char ikey[BSON_MAX_FPATH_LEN + 2]; + const char *mkey; + int mkeysz; + + tcmapiterinit(cmeta); + while ((mkey = tcmapiternext(cmeta, &mkeysz)) != NULL && mkeysz > 0) { + if (*mkey != 'i' || mkeysz > BSON_MAX_FPATH_LEN + 1) { + continue; + } + const void* mraw = tcmapget(cmeta, mkey, mkeysz, &bsz); + if (!mraw || !bsz || (mt = bson_find_from_buffer(&mit, mraw, "iflags")) != BSON_INT) { + continue; + } + int iflags = bson_iterator_int(&mit); + //OK then process index keys + memcpy(ikey + 1, mkey + 1, mkeysz - 1); + ikey[mkeysz] = '\0'; + + int fvaluesz = 0; + char *fvalue = NULL; + int ofvaluesz = 0; + char *ofvalue = NULL; + + if (obsdata && obsdatasz > 0) { + bson_iterator_from_buffer(&oit, obsdata); + oft = bson_find_fieldpath_value2(mkey + 1, mkeysz - 1, &oit); + TCLIST *tokens = (oft == BSON_ARRAY) ? tclistnew() : NULL; + ofvalue = BSON_IS_IDXSUPPORTED_TYPE(oft) ? _bsonitstrval(&oit, &ofvaluesz, tokens) : NULL; + if (tokens) { + ofvalue = tclistdump(tokens, &ofvaluesz); + tclistdel(tokens); + } + } + if (bs) { + bson_iterator_init(&fit, bs); + ft = bson_find_fieldpath_value2(mkey + 1, mkeysz - 1, &fit); + TCLIST *tokens = (ft == BSON_ARRAY) ? tclistnew() : NULL; + fvalue = BSON_IS_IDXSUPPORTED_TYPE(ft) ? _bsonitstrval(&fit, &fvaluesz, tokens) : NULL; + if (tokens) { + fvalue = tclistdump(tokens, &fvaluesz); + tclistdel(tokens); + } + } + if (!fvalue && !ofvalue) { + continue; + } + if (imap == NULL) { + imap = tcmapnew2(16); + rimap = tcmapnew2(16); + } + for (int i = 4; i <= 6; ++i) { /* JDIDXNUM, JDIDXSTR, JDIDXARR */ + bool rm = false; + if ((1 << i) == JDIDXNUM && (JDIDXNUM & iflags)) { + ikey[0] = 'n'; + } else if ((1 << i) == JDIDXSTR && (JDIDXSTR & iflags)) { + ikey[0] = 's'; + } else if ((1 << i) == JDIDXARR && (JDIDXARR & iflags)) { + ikey[0] = 'a'; + if (ofvalue && oft == BSON_ARRAY && + (!fvalue || ft != oft || fvaluesz != ofvaluesz || memcmp(fvalue, ofvalue, fvaluesz))) { + tcmapput(rimap, ikey, mkeysz, ofvalue, ofvaluesz); + rm = true; + } + if (fvalue && ft == BSON_ARRAY && (!ofvalue || rm)) { + tcmapput(imap, ikey, mkeysz, fvalue, fvaluesz); + } + continue; + } else { + continue; + } + if (ofvalue && oft != BSON_ARRAY && + (!fvalue || ft != oft || fvaluesz != ofvaluesz || memcmp(fvalue, ofvalue, fvaluesz))) { + tcmapput(rimap, ikey, mkeysz, ofvalue, ofvaluesz); + rm = true; + } + if (fvalue && ft != BSON_ARRAY && (!ofvalue || rm)) { + tcmapput(imap, ikey, mkeysz, fvalue, fvaluesz); + } + } + if (fvalue) { + TCFREE(fvalue); + } + if (ofvalue) { + TCFREE(ofvalue); + } + } + tcmapdel(cmeta); + if (rimap && !tctdbidxout2(jcoll->tdb, oid, sizeof (*oid), rimap)) rv = false; + if (imap && !tctdbidxput2(jcoll->tdb, oid, sizeof (*oid), imap)) rv = false; + if (imap) tcmapdel(imap); + if (rimap) tcmapdel(rimap); + return rv; +} + +static void _delcoldb(EJCOLL *jcoll) { + assert(jcoll); + tctdbdel(jcoll->tdb); + jcoll->tdb = NULL; + jcoll->jb = NULL; + jcoll->cnamesz = 0; + TCFREE(jcoll->cname); + if (jcoll->mmtx) { + pthread_rwlock_destroy(jcoll->mmtx); + TCFREE(jcoll->mmtx); + } +} + +static bool _addcoldb0(const char* cname, EJDB *jb, EJCOLLOPTS *opts, EJCOLL **res) { + bool rv = true; + TCTDB* cdb; + rv = _createcoldb(cname, jb, opts, &cdb); + if (!rv) { + *res = NULL; + return rv; + } + TCREALLOC(jb->cdbs, jb->cdbs, sizeof (jb->cdbs[0]) * (++jb->cdbsnum)); + EJCOLL *jcoll = jb->cdbs + jb->cdbsnum - 1; + jcoll->cname = tcstrdup(cname); + jcoll->cnamesz = strlen(cname); + jcoll->tdb = cdb; + jcoll->jb = jb; + jcoll->mmtx = NULL; + _ejdbcolsetmutex(jcoll); + *res = jcoll; + return rv; +} + +static bool _createcoldb(const char* colname, EJDB *jb, EJCOLLOPTS *opts, TCTDB **res) { + assert(jb && jb->metadb); + if (!JBISVALCOLNAME(colname)) { + _ejdbsetecode(jb, JDBEINVALIDCOLNAME, __FILE__, __LINE__, __func__); + *res = NULL; + return false; + } + bool rv = true; + TCTDB *cdb = tctdbnew(); + if (opts) { + if (opts->cachedrecords > 0) { + tctdbsetcache(cdb, opts->cachedrecords, 0, 0); + } + int bnum = 0; + uint8_t tflags = 0; + if (opts->records > 0) { + bnum = tclmax(opts->records * 2 + 1, TDBDEFBNUM); + } + if (opts->large) { + tflags |= TDBTLARGE; + } + if (opts->compressed) { + tflags |= TDBTDEFLATE; + } + tctdbtune(cdb, bnum, 0, 0, tflags); + } + const char* mdbpath = jb->metadb->hdb->path; + assert(mdbpath); + TCXSTR *cxpath = tcxstrnew2(mdbpath); + tcxstrcat2(cxpath, "_"); + tcxstrcat2(cxpath, colname); + rv = tctdbopen(cdb, tcxstrptr(cxpath), jb->metadb->hdb->omode); + *res = rv ? cdb : NULL; + tcxstrdel(cxpath); + return rv; +} + +static void _ejdbclear(EJDB *jb) { + assert(jb); + jb->cdbs = NULL; + jb->cdbsnum = 0; + jb->metadb = NULL; + jb->mmtx = NULL; +} + +/* Check whether a string includes all tokens in another string.*/ +static bool _qrycondcheckstrand(const char *vbuf, const TCLIST *tokens) { + assert(vbuf && tokens); + for (int i = 0; i < TCLISTNUM(tokens); ++i) { + const char* token = TCLISTVALPTR(tokens, i); + int tokensz = TCLISTVALSIZ(tokens, i); + bool found = false; + const char *str = vbuf; + while (true) { + const char *sp = str; + while (*str != '\0' && !strchr(", ", *str)) { + str++; + } + if (tokensz == (str - sp) && !strncmp(token, sp, tokensz)) { //Token matched + found = true; + break; + } + if (*str == '\0') break; + str++; + } + if (!found) { + return false; + } + } + return true; +} + +/* Check whether a string includes at least one tokens in another string.*/ +static bool _qrycondcheckstror(const char *vbuf, const TCLIST *tokens) { + for (int i = 0; i < TCLISTNUM(tokens); ++i) { + const char* token = TCLISTVALPTR(tokens, i); + int tokensz = TCLISTVALSIZ(tokens, i); + bool found = false; + const char *str = vbuf; + while (true) { + const char *sp = str; + while (*str != '\0' && !strchr(", ", *str)) { + str++; + } + if (tokensz == (str - sp) && !strncmp(token, sp, tokensz)) { //Token matched + found = true; + break; + } + if (*str == '\0') break; + str++; + } + if (found) { + return true; + } + } + return false; +} diff --git a/tcejdb/ejdb.h b/tcejdb/ejdb.h new file mode 100644 index 0000000..5944cc2 --- /dev/null +++ b/tcejdb/ejdb.h @@ -0,0 +1,311 @@ +/* + * File: ejdb.h + * Author: adam + * + * Created on September 8, 2012, 10:09 PM + */ + +#ifndef EJDB_H +#define EJDB_H + +#include "myconf.h" +#include "bson.h" +#include "tcutil.h" + +EJDB_EXTERN_C_START + +struct EJDB; /**> EJDB database object. */ +typedef struct EJDB EJDB; + +struct EJCOLL; /**> EJDB collection handle. */ +typedef struct EJCOLL EJCOLL; + +struct EJQ; /**< EJDB query. */ +typedef struct EJQ EJQ; + +typedef struct { /**< EJDB collection tuning options. */ + bool large; /**< Large collection. It can be larger than 2GB. Default false */ + bool compressed; /**< Collection records will be compressed with DEFLATE compression. Default: false */ + int64_t records; /**< Expected records number in the collection. Default: 128K */ + int cachedrecords; /**< Maximum number of cached records. Default: 0 */ +} EJCOLLOPTS; + + +#define JBMAXCOLNAMELEN 128 + +enum { /** Error codes */ + JDBEINVALIDCOLNAME = 9000, /**< Invalid collection name. */ + JDBEINVALIDBSON = 9001, /**< Invalid bson object. */ + JDBEQINVALIDQCONTROL = 9002, /**< Invalid query control field starting with '$'. */ + JDBEQINOPNOTARRAY = 9003, /**< $strand, $stror, $in, $nin, $bt keys requires not empty array value. */ + JDBEMETANVALID = 9004, /**< Inconsistent database metadata. */ + JDBEFPATHINVALID = 9005, /**< Invalid field path value. */ + JDBEQINVALIDQRX = 9006, /**< Invalid query regexp value. */ + JDBEQRSSORTING = 9007, /**< Result set sorting error. */ + JDBQINVALID = 9008 /**< Query generic error. */ +}; + +enum { /** Database open modes */ + JDBOREADER = 1 << 0, /**< Open as a reader. */ + JDBOWRITER = 1 << 1, /**< Open as a writer. */ + JDBOCREAT = 1 << 2, /**< Create if db file not exists. */ + JDBOTRUNC = 1 << 3, /**< Truncate db. */ + JDBONOLCK = 1 << 4, /**< Open without locking. */ + JDBOLCKNB = 1 << 5, /**< Lock without blocking. */ + JDBOTSYNC = 1 << 6 /**< Synchronize every transaction. */ +}; + +enum { /** Index modes, index types. */ + JDIDXDROP = 1 << 0, /**< Drop index. */ + JDIDXDROPALL = 1 << 1, /**< Drop index for all types. */ + JDIDXOP = 1 << 2, /**< Optimize index. */ + JDIDXREBLD = 1 << 3, /**< Rebuild index. */ + JDIDXNUM = 1 << 4, /**< Number index. */ + JDIDXSTR = 1 << 5, /**< String index.*/ + JDIDXARR = 1 << 6, /**< Array token index. */ +}; + +enum { /**> Query search mode flags in ejdbqrysearch() */ + EJQRYCOUNT = 1 /**> Query only count(*) */ +}; + +/** + * Get the message string corresponding to an error code. + * @param ecode `ecode' specifies the error code. + * @return The return value is the message string of the error code. + */ +EJDB_EXPORT const char *ejdberrmsg(int ecode); + +/** + * Get the last happened error code of a EJDB database object. + * @param jb + * @return The return value is the last happened error code. + */ +EJDB_EXPORT int ejdbecode(EJDB *jb); + +/** + * Create a EJDB database object. On error returns NULL. + * Created pointer must be freed by ejdbdel() + * @return The return value is the new EJDB database object or NULL if error. + */ +EJDB_EXPORT EJDB* ejdbnew(void); + +/** + * Delete database object. If the database is not closed, it is closed implicitly. + * Note that the deleted object and its derivatives can not be used anymore + * @param jb + */ +EJDB_EXPORT void ejdbdel(EJDB *jb); + +/** + * Close a table database object. If a writer opens a database but does not close it appropriately, + * the database will be broken. + * @param jb EJDB handle. + * @return If successful return true, otherwise return false. + */ +EJDB_EXPORT bool ejdbclose(EJDB *jb); + +/** + * Opens EJDB database. + * @param jb Database object created with `ejdbnew' + * @param path Path to the database file. + * @param mode Open mode bitmask flags: + * `JDBOREADER` Open as a reader. + * `JDBOWRITER` Open as a writer. + * `JDBOCREAT` Create db if it not exists + * `JDBOTRUNC` Truncate db. + * `JDBONOLCK` Open without locking. + * `JDBOLCKNB` Lock without blocking. + * `JDBOTSYNC` Synchronize every transaction. + * @return + */ +EJDB_EXPORT bool ejdbopen(EJDB *jb, const char *path, int mode); + +/** + * Retrieve collection handle for collection specified `collname`. + * If collection with specified name does't exists it will return NULL. + * @param jb EJDB handle. + * @param colname Name of collection. + * @return If error NULL will be returned. + */ +EJDB_EXPORT EJCOLL* ejdbgetcoll(EJDB *jb, const char* colname); + +/** + * Same as ejdbgetcoll() but automatically creates new collection + * if it does't exists. + * + * @param jb EJDB handle. + * @param colname Name of collection. + * @param opts Options to be applied for newly created collection. + * @return Collection handle or NULL if error. + */ +EJDB_EXPORT EJCOLL* ejdbcreatecoll(EJDB *jb, const char* colname, EJCOLLOPTS *opts); + +/** + * Removes collections specified by `colname` + * @param jb EJDB handle. + * @param colname Name of collection. + * @param unlink It true the collection db file and all of its index files will be removed. + * @return If successful return true, otherwise return false. + */ +EJDB_EXPORT bool ejdbrmcoll(EJDB *jb, const char* colname, bool unlinkfile); + +/** + * Persist BSON object in the collection. + * If saved bson does't have _id primary key then `oid` will be set to generated bson _id, + * otherwise `oid` will be set to the current bson's _id field. + * + * @param coll JSON collection handle. + * @param bson BSON object id pointer. + * @return If successful return true, otherwise return false. + */ +EJDB_EXPORT bool ejdbsavebson(EJCOLL* coll, bson* bs, bson_oid_t* oid); + +/** + * Remove BSON object from collection. + * The `oid` argument should points the primary key (_id) + * of the bson record to be removed. + * @param coll JSON collection ref. + * @param oid BSON object id pointer. + * @return + */ +EJDB_EXPORT bool ejdbrmbson(EJCOLL* coll, bson_oid_t* oid); + +/** + * Load BSON object with specified 'oid'. + * If loaded bson is not NULL it must be freed by bson_del(). + * @param coll + * @param oid + * @return BSON object if exists otherwise return NULL. + */ +EJDB_EXPORT bson* ejdbloadbson(EJCOLL* coll, const bson_oid_t* oid); + +/** + * Create query object. + * Sucessfully created queries must be destroyed with ejdbquerydel(). + * + * EJDB queries inspired by MongoDB (mongodb.org) and follows same philosophy. + * + * - Supported queries: + * - Simple matching of String OR Number OR Array value: + * - {'bson.field.path' : 'val', ...} + * - $not Negate operation. + * - {'json.field.path' : {'$not' : val}} //Field not equal to val + * - {'json.field.path' : {'$not' : {'$begin' : prefix}}} //Field not begins with val + * - $begin String starts with prefix + * - {'json.field.path' : {'$begin' : prefix}} + * - $gt, $gte (>, >=) and $lt, $lte for number types: + * - {'json.field.path' : {'$gt' : number}, ...} + * - $bt Between for number types: + * - {'json.field.path' : {'$bt' : [num1, num2]}} + * - $in String OR Number OR Array val matches to value in specified array: + * - {'json.field.path' : {'$in' : [val1, val2, val3]}} + * - $nin - Not IN + * - $strand String tokens OR String array val matches all tokens in specified array: + * - {'json.field.path' : {'$strand' : [val1, val2, val3]}} + * - $stror String tokens OR String array val matches any token in specified array: + * - {'json.field.path' : {'$stror' : [val1, val2, val3]}} + * + * NOTE: Negate operations: $not and $nin not using indexes + * so they can be slow in comparison to other matching operations. + * + * NOTE: Only one index can be used in search query operation. + * + * QUERY HINTS (specified by `hints` argument): + * - $max Maximum number in the result set + * - $skip Number of skipped results in the result set + * - $orderby Sorting order of query fields. + * Eg: ORDER BY field1 ASC, field2 DESC + * hints: { + * "$orderby" : { + * "field1" : 1, + * "field2" : -1 + * } + * } + * + * Many query examples can be found in `testejdb/t2.c` test case. + * + * @param EJDB database handle. + * @param qobj Main BSON query object. + * @param orqobjs Array of additional OR query objects (joined with OR predicate). + * @param orqobjsnum Number of OR query objects. + * @param hints BSON object with query hints. + * @return On success return query handle. On error returns NULL. + */ +EJDB_EXPORT EJQ* ejdbcreatequery(EJDB *jb, bson *qobj, bson *orqobjs, int orqobjsnum, bson *hints); + +/** + * Destroy query object created with ejdbcreatequery(). + * @param q + */ +EJDB_EXPORT void ejdbquerydel(EJQ* q); + +/** + * Set index for JSON field in EJDB collection. + * + * - Available index types: + * - `JDIDXSTR` String index for JSON string values. + * - `JDIDXNUM` Index for JSON number values. + * - `JDIDXARR` Token index for JSON arrays and string values. + * + * - One JSON field can have several indexes for different types. + * + * - Available index operations: + * - `JDIDXDROP` Drop index of specified type. + * - Eg: flag = JDIDXDROP | JDIDXNUM (Drop number index) + * - `JDIDXDROPALL` Drop index for all types. + * - `JDIDXREBLD` Rebuild index of specified type. + * - `JDIDXOP` Optimize index of specified type. + * + * Examples: + * - Set index for JSON path `addressbook.number` for strings and numbers: + * `ejdbsetindex(ccoll, "album.number", JDIDXSTR | JDIDXNUM)` + * - Set index for array: + * `ejdbsetindex(ccoll, "album.tags", JDIDXARR)` + * - Rebuild previous index: + * `ejdbsetindex(ccoll, "album.tags", JDIDXARR | JDIDXREBLD)` + * + * Many index examples can be found in `testejdb/t2.c` test case. + * + * @param coll Collection handle. + * @param ipath BSON field path. + * @param flags Index flags. + * @return + */ +EJDB_EXPORT bool ejdbsetindex(EJCOLL *coll, const char *ipath, int flags); + +/** + * Execute query against EJDB collection. + * + * @param jcoll EJDB database + * @param q Query handle created with ejdbcreatequery() + * @param count Output count pointer. Result set size will be stored into it. + * @param qflags Execution flag. If EJQRYCOUNT is set the only count of matching records will be computed + * without resultset, this operation is analog of count(*) in SQL and can be faster than operations with resultsets. + * @param log Optional extended string to collect debug information during query execution, can be NULL. + * @return TCLIST with matched bson records data. + * If (qflags & EJQRYCOUNT) then NULL will be returned + * and only count reported. + */ +EJDB_EXPORT TCLIST* ejdbqrysearch(EJCOLL *jcoll, const EJQ *q, uint32_t *count, int qflags, TCXSTR *log); + +/** + * Synchronize content of a EJDB collection database with the file on device. + * @param jcoll EJDB collection. + * @return On success return true. + */ +EJDB_EXPORT bool ejdbsyncoll(EJCOLL *jcoll); + +/** Begin transaction for EJDB collection. */ +EJDB_EXPORT bool ejdbtranbegin(EJCOLL *coll); + +/** Commit transaction for EJDB collection. */ +EJDB_EXPORT bool ejdbtrancommit(EJCOLL *coll); + +/** Abort transaction for EJDB collection. */ +EJDB_EXPORT bool ejdbtranabort(EJCOLL *coll); + +EJDB_EXTERN_C_END + +#endif /* EJDB_H */ + diff --git a/tcejdb/ejdb_private.h b/tcejdb/ejdb_private.h new file mode 100644 index 0000000..aa6fe45 --- /dev/null +++ b/tcejdb/ejdb_private.h @@ -0,0 +1,104 @@ +/* + * File: ejdb_private.h + * Author: adam + * + * Created on September 28, 2012, 11:43 AM + */ + +#ifndef EJDB_PRIVATE_H +#define EJDB_PRIVATE_H + +#include "ejdb.h" + +#include "myconf.h" +#include "tcutil.h" +#include "tctdb.h" +#include "tchdb.h" + +#include +#include + +EJDB_EXTERN_C_START + +#define BSON_IS_IDXSUPPORTED_TYPE(atype) (atype == BSON_STRING || \ + atype == BSON_INT || atype == BSON_LONG || atype == BSON_DOUBLE || \ + atype == BSON_SYMBOL || \ + atype == BSON_ARRAY) + +struct EJDB { + EJCOLL *cdbs; /*> Collection DBs for JSON collections. */ + int cdbsnum; /*> Count of collection DB. */ + TCTDB *metadb; /*> Metadata DB. */ + void *mmtx; /*> Mutex for method */ +}; + +struct EJCOLL { /**> EJDB Collection. */ + char *cname; /**> Collection name. */ + int cnamesz; /**> Collection name length. */ + TCTDB *tdb; /**> Collection TCTDB. */ + EJDB *jb; /**> Database handle. */ + void *mmtx; /*> Mutex for method */ +}; + +enum { //Query field flags + // Comparison flags + EJCOMPGT = 1, //Comparison GT + EJCOMPGTE = 1 << 1, //Comparison GTE + EJCOMPLT = 1 << 2, //Comparison LT + EJCOMPLTE = 1 << 3, //Comparison LTE + EJCONDSTARTWITH = 1 << 4, //Starts with + + EJFEXCLUDED = 1 << 5, //If query field excluded from matching + EJFNOINDEX = 1 << 6, //Do not use index for field + EJFORDERUSED = 1 << 7 //This ordering field was used +}; + + +enum { //Query flags + EJQINTERNAL = 1 //Internal query object used in _ejdbqrysearch +}; + +struct EJQF { /**> Matching field and status */ + char *fpath; /**>JSON field path */ + int fpathsz; /**>JSON field path size */ + char *expr; /**> Query operand expression, string or TCLIST data */ + int exprsz; /**> Size of query operand expression */ + int64_t exprlongval; /** Integer value represeintation */ + double exprdblval; /** Double value representation */ + TCLIST *exprlist; /** List representation of expression */ + regex_t *regex; /**> Regular expression object */ + int tcop; /**> Matching operation eg. TDBQCSTREQ */ + bool negate; /**> Negate expression */ + uint32_t flags; /**> Various field matching|status flags */ + int order; /**> 0 no order, 1 ASC, -1 DESC */ + int orderseq; /**> Seq number for order fields */ + bson_type ftype; /**> BSON field type */ + const TDBIDX *idx; /**> Column index for this field if exists */ + bson *idxmeta; /**> Index metainfo */ +}; +typedef struct EJQF EJQF; + +struct EJQ { /**> Query object. */ + TCMAP *qobjmap; /**> Mapping of field path (char*) -> *EJQF */ + EJQ *orqobjs; /** OR Query objects */ + int orqobjsnum; /** Number of OR query objects */ + bson *hints; /**> Hints bson object */ + uint32_t skip; /**> Number of records to skip. */ + uint32_t max; /**> Max number of results */ + uint32_t flags; /**> Control flags */ + const EJQF *lastmatchedorqf; /**> Reference to the last matched or query field */ +}; + + +#define JDBIDKEYNAME "_id" /**> Name of PK _id field in BSONs */ +#define JDBIDKEYNAMEL 3 + +#define JDBCOLBSON "$" /**> TCDB colname with BSON byte data */ +#define JDBCOLBSONL 1 /**> TCDB colname with BSON byte data columen len */ + + + +EJDB_EXTERN_C_END + +#endif /* EJDB_PRIVATE_H */ + diff --git a/tcejdb/ejdbutl.h b/tcejdb/ejdbutl.h new file mode 100644 index 0000000..195d339 --- /dev/null +++ b/tcejdb/ejdbutl.h @@ -0,0 +1,76 @@ +/* + * File: ejdbutl.h + * Author: adam + * + * Created on October 15, 2012, 1:26 PM + */ + +#ifndef EJDBUTL_H +#define EJDBUTL_H + +#include "myconf.h" +#include "tcutil.h" + +EJDB_EXTERN_C_START + +/** + * A stable, adaptive, iterative mergesort that requires far fewer than + * n lg(n) comparisons when running on partially sorted arrays, while + * offering performance comparable to a traditional mergesort when run + * on random arrays. Like all proper mergesorts, this sort is stable and + * runs O(n log n) time (worst case). In the worst case, this sort requires + * temporary storage space for n/2 object references; in the best case, + * it requires only a small constant amount of space. + * + * This implementation was adapted from Josh Bloch's Java implementation of + * Tim Peters's list sort for Python, which is described in detail here: + * + * http://svn.python.org/projects/python/trunk/Objects/listsort.txt + * + * Tim's C code may be found here: + * + * http://svn.python.org/projects/python/trunk/Objects/listobject.c + * + * Josh's (Apache 2.0 Licenced) Java code may be found here: + * + * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/TimSort.java?view=co + * + * The underlying techniques are described in this paper (and may have + * even earlier origins): + * + * "Optimistic Sorting and Information Theoretic Complexity" + * Peter McIlroy + * SODA (Fourth Annual ACM-SIAM Symposium on Discrete Algorithms), + * pp 467-474, Austin, Texas, 25-27 January 1993. + * + * While the API to this class consists solely of static methods, it is + * (privately) instantiable; a TimSort instance holds the state of an ongoing + * sort, assuming the input array is large enough to warrant the full-blown + * TimSort. Small arrays are sorted in place, using a binary insertion sort. + * + * C implementation: + * https://github.com/patperry/timsort + * + * + * @param a the array to be sorted + * @param nel the length of the array + * @param c the comparator to determine the order of the sort + * @param width the element width + * @param opaque data for the comparator function + * @param opaque data for the comparator function + * + * @author Josh Bloch + * @author Patrick O. Perry + */ + +int ejdbtimsort(void *a, size_t nel, size_t width, + int (*c) (const void *, const void *, void *opaque), void *opaque); + +int ejdbtimsortlist(TCLIST *list, + int (*compar) (const TCLISTDATUM*, const TCLISTDATUM*, void *opaque), void *opaque); + + +EJDB_EXTERN_C_END + +#endif /* EJDBUTL_H */ + diff --git a/tcejdb/encoding.c b/tcejdb/encoding.c new file mode 100644 index 0000000..4bf405f --- /dev/null +++ b/tcejdb/encoding.c @@ -0,0 +1,165 @@ +/* + * Copyright 2009-2012 10gen, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Portions Copyright 2001 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + + +#include "bson.h" +#include "encoding.h" + +/* + * Index into the table below with the first byte of a UTF-8 sequence to + * get the number of trailing bytes that are supposed to follow it. + */ +static const char trailingBytesForUTF8[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 +}; + +/* --------------------------------------------------------------------- */ + +/* + * Utility routine to tell whether a sequence of bytes is legal UTF-8. + * This must be called with the length pre-determined by the first byte. + * The length can be set by: + * length = trailingBytesForUTF8[*source]+1; + * and the sequence is illegal right away if there aren't that many bytes + * available. + * If presented with a length > 4, this returns 0. The Unicode + * definition of UTF-8 goes up to 4-byte sequences. + */ +static int isLegalUTF8(const unsigned char *source, int length) { + unsigned char a; + const unsigned char *srcptr = source + length; + switch (length) { + default: + return 0; + /* Everything else falls through when "true"... */ + case 4: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; + case 3: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; + case 2: + if ((a = (*--srcptr)) > 0xBF) return 0; + switch (*source) { + /* no fall-through in this inner switch */ + case 0xE0: + if (a < 0xA0) return 0; + break; + case 0xF0: + if (a < 0x90) return 0; + break; + case 0xF4: + if (a > 0x8F) return 0; + break; + default: + if (a < 0x80) return 0; + } + case 1: + if (*source >= 0x80 && *source < 0xC2) return 0; + if (*source > 0xF4) return 0; + } + return 1; +} + +/* If the name is part of a db ref ($ref, $db, or $id), then return true. */ +static int bson_string_is_db_ref(const unsigned char *string, const int length) { + int result = 0; + + if (length >= 4) { + if (string[1] == 'r' && string[2] == 'e' && string[3] == 'f') + result = 1; + } else if (length >= 3) { + if (string[1] == 'i' && string[2] == 'd') + result = 1; + else if (string[1] == 'd' && string[2] == 'b') + result = 1; + } + + return result; +} + +static int bson_validate_string(bson *b, const unsigned char *string, + const int length, const char check_utf8, const char check_dot, + const char check_dollar) { + + int position = 0; + int sequence_length = 1; + + if (check_dollar && string[0] == '$') { + if (!bson_string_is_db_ref(string, length)) + b->err |= BSON_FIELD_INIT_DOLLAR; + } + + while (position < length) { + if (check_dot && *(string + position) == '.') { + b->err |= BSON_FIELD_HAS_DOT; + } + + if (check_utf8) { + sequence_length = trailingBytesForUTF8[*(string + position)] + 1; + if ((position + sequence_length) > length) { + b->err |= BSON_NOT_UTF8; + return BSON_ERROR; + } + if (!isLegalUTF8(string + position, sequence_length)) { + b->err |= BSON_NOT_UTF8; + return BSON_ERROR; + } + } + position += sequence_length; + } + + return BSON_OK; +} + +int bson_check_string(bson *b, const char *string, + const int length) { + + return bson_validate_string(b, (const unsigned char *) string, length, 1, 0, 0); +} + +int bson_check_field_name(bson *b, const char *string, + const int length, int check_dot, int check_dollar) { + + return bson_validate_string(b, (const unsigned char *) string, length, 1, check_dot, check_dollar); +} diff --git a/tcejdb/encoding.h b/tcejdb/encoding.h new file mode 100644 index 0000000..bfc4127 --- /dev/null +++ b/tcejdb/encoding.h @@ -0,0 +1,54 @@ +/* + * Copyright 2009-2012 10gen, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BSON_ENCODING_H_ +#define BSON_ENCODING_H_ + +EJDB_EXTERN_C_START + +/** + * Check that a field name is valid UTF8, does not start with a '$', + * and contains no '.' characters. Set bson bit field appropriately. + * Note that we don't need to check for '\0' because we're using + * strlen(3), which stops at '\0'. + * + * @param b The bson object to which field name will be appended. + * @param string The field name as char*. + * @param length The length of the field name. + * + * @return BSON_OK if valid UTF8 and BSON_ERROR if not. All BSON strings must be + * valid UTF8. This function will also check whether the string + * contains '.' or starts with '$', since the validity of this depends on context. + * Set the value of b->err appropriately. + */ +int bson_check_field_name(bson *b, const char *string, + const int length, int check_dot, int check_dollar); + +/** + * Check that a string is valid UTF8. Sets the buffer bit field appropriately. + * + * @param b The bson object to which string will be appended. + * @param string The string to check. + * @param length The length of the string. + * + * @return BSON_OK if valid UTF-8; otherwise, BSON_ERROR. + * Sets b->err on error. + */ +bson_bool_t bson_check_string(bson *b, const char *string, + const int length); + +EJDB_EXTERN_C_END +#endif diff --git a/tcejdb/lab/calccomp b/tcejdb/lab/calccomp new file mode 100755 index 0000000..9780cf8 --- /dev/null +++ b/tcejdb/lab/calccomp @@ -0,0 +1,118 @@ +#! /usr/bin/perl + +#================================================================ +# calccomp +# Measure elapsed time and database size of compression algos +#================================================================ + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); + +use constant { + WORDFILE => '/usr/share/dict/words', + TSVFILE => 'words.tsv', + NULLFILE => '/dev/null', + BNUM => 262144, + LCNUM => 8192, + TESTCOUNT => 20, + REMOVETOP => 2, + REMOVEBOTTOM => 8, +}; + +open(IN, '<' . WORDFILE) or die(WORDFILE . ' could not be opened'); +open(OUT, '>' . TSVFILE) or die(TSVFILE . ' could not be opened'); +while(defined(my $word = )){ + $word =~ s/[\t\r\n]//g; + next if(length($word) < 1); + printf OUT ("%s\t%d\n", $word, int(rand(10000))); +} +close(OUT); +close(IN); + +my @commands = ( + './tchmgr create casket-hash ' . BNUM . ' ; ./tchmgr importtsv casket-hash ' . TSVFILE, + './tchmgr list casket-hash >' . NULLFILE, + './tcbmgr create casket-btree ' . LCNUM . ' ; ./tcbmgr importtsv casket-btree ' . TSVFILE, + './tcbmgr list casket-btree >' . NULLFILE, + './tcbmgr create -td casket-bt-td ' . LCNUM . ' ; ./tcbmgr importtsv casket-bt-td ' . TSVFILE, + './tcbmgr list casket-bt-td >' . NULLFILE, + './tcbmgr create -tb casket-bt-tb ' . LCNUM . ' ; ./tcbmgr importtsv casket-bt-tb ' . TSVFILE, + './tcbmgr list casket-bt-tb >' . NULLFILE, + './tcbmgr create -tt casket-bt-tt ' . LCNUM . ' ; ./tcbmgr importtsv casket-bt-tt ' . TSVFILE, + './tcbmgr list casket-bt-tt >' . NULLFILE, + './tcbmgr create -tx casket-bt-tx ' . LCNUM . ' ; ./tcbmgr importtsv casket-bt-tx ' . TSVFILE, + './tcbmgr list casket-bt-tx >' . NULLFILE, + ); + +my @names = ( + 'casket-hash', + 'casket-btree', + 'casket-bt-td', + 'casket-bt-tb', + 'casket-bt-tt', + 'casket-bt-tx', + ); + +foreach my $name (@names){ + my @paths = glob("$name*"); + foreach my $path (@paths){ + unlink($path); + } +} + +my @table; +foreach my $command (@commands){ + system('sync ; sync'); + $ENV{'HIDEPRGR'} = 1; + my @result; + for(my $i = 0; $i < TESTCOUNT; $i++){ + my $stime = gettimeofday(); + system("$command >/dev/null 2>&1"); + $stime = gettimeofday() - $stime; + printf("%s\t%d\t%0.5f\n", $command, $i + 1, $stime); + push(@result, $stime); + } + @result = sort { $a <=> $b } @result; + for(my $i = 0; $i < REMOVETOP; $i++){ + shift(@result); + } + for(my $i = 0; $i < REMOVEBOTTOM; $i++){ + pop(@result); + } + my $sum = 0; + foreach my $result (@result){ + $sum += $result; + } + my $avg = $sum / scalar(@result); + push(@table, [$command, $avg]); +} + +printf("\n\nRESULT\n\n"); +foreach my $row (@table){ + printf("%s\t%0.5f\n", $$row[0], $$row[1]); +} +printf("\n"); + +my @sizes; +foreach my $name (@names){ + my @sbuf = stat($name); + my $size = $sbuf[7]; + printf("%s\t%s\n", $name, $size); + push(@sizes, $size); +} +my @sbuf = stat(TSVFILE); +my $size = $sbuf[7]; +printf("\n"); + +printf("%s,%.5f,%.5f,%d\n", "ORIGINAL", 0, 0, $size); +printf("%s,%.5f,%.5f,%d\n", "HASH", $table[0][1], $table[1][1], $sizes[0]); +printf("%s,%.5f,%.5f,%d\n", "BTREE", $table[2][1], $table[3][1], $sizes[1]); +printf("%s,%.5f,%.5f,%d\n", "BTREE-DEFLATE", $table[4][1], $table[5][1], $sizes[2]); +printf("%s,%.5f,%.5f,%d\n", "BTREE-BZIP2", $table[6][1], $table[7][1], $sizes[3]); +printf("%s,%.5f,%.5f,%d\n", "BTREE-TCBS", $table[8][1], $table[9][1], $sizes[4]); +printf("%s,%.5f,%.5f,%d\n", "BTREE-EXCODEC", $table[10][1], $table[11][1], $sizes[5]); + + + +# END OF FILE diff --git a/tcejdb/lab/datechange b/tcejdb/lab/datechange new file mode 100755 index 0000000..4fd0491 --- /dev/null +++ b/tcejdb/lab/datechange @@ -0,0 +1,56 @@ +#! /bin/sh + +#================================================================ +# datechange +# Replace date expressions +#================================================================ + + +# set variables +LANG=C +LC_ALL=C +export LANG LC_ALL +year=`date '+%Y'` +date=`date '+%Y-%m-%d'` +fulldate=`date -R` +regexsrc='\.(h|c|cc|cpp|cxx|java|pl|pm|pod|xs|rb|rd|py|lua|idl)$' +regexman='\.[0-9]$' +regexhtml='\.html$' + + +# edit source files +find . -type f | egrep "$regexsrc" | +while read file +do + echo "$file" + sed "/opyright/ s/\\(20[0-9][0-9]\\)-\\(20[0-9][0-9]\\)/\\1-$year/" "$file" > "$file.tmp" + mv -f "$file.tmp" "$file" +done + + +# edit manual files +find . -type f | egrep "$regexman" | +while read file +do + echo "$file" + sed "/\\.TH/ s/\\(20[0-9][0-9]\\)-\\([0-9][0-9]\\)-\\([0-9][0-9]\\)/$date/" "$file" > "$file.tmp" + mv -f "$file.tmp" "$file" +done + + +# edit HTML files +find . -type f | egrep "$regexhtml" | +while read file +do + echo "$file" + sed -e "/opyright/ s/\\(20[0-9][0-9]\\)-\\(20[0-9][0-9]\\)/\\1-$year/" -e "/

/ s/Last Update: *\\([A-Z][a-z][a-z], [0-9][0-9] [A-Z][a-z][a-z] 20[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] +[0-9][0-9]*\\)/Last Update: $fulldate/" "$file" > "$file.tmp" + mv -f "$file.tmp" "$file" +done + + +# exit normally +exit 0 + + + +# END OF FILE diff --git a/tcejdb/lab/diffcheck b/tcejdb/lab/diffcheck new file mode 100755 index 0000000..b84284e --- /dev/null +++ b/tcejdb/lab/diffcheck @@ -0,0 +1,45 @@ +#! /bin/sh + +#================================================================ +# diffcheck +# List files different from ones of another version +#================================================================ + + +# set variables +LANG=C +LC_ALL=C +export LANG LC_ALL +regex='\.(h|c|cc|cpp|cxx|java|pl|pm|pod|rb|rd|lua|[1-9]|html|txt)$' + + +# check arguments +if [ $# != 1 ] +then + printf 'diffcheck: usage: diffcheck directory_of_oldversion\n' 1>&2 + exit 1 +fi + + +# diff files +find . -type f | egrep $regex | +while read file +do + old=`printf '%s\n' "$file" | sed 's/^\.\///'` + printf 'Checking %s and %s ... ' "$file" "$1/$old" + res=`diff -q "$file" "$1/$old"` + if [ -z "$res" ] + then + printf 'same\n' + else + printf '### !!! DIFFERENT !!! ###\n' + fi +done + + +# exit normally +exit 0 + + + +# END OF FILE diff --git a/tcejdb/lab/htmltotsv b/tcejdb/lab/htmltotsv new file mode 100755 index 0000000..3221a84 --- /dev/null +++ b/tcejdb/lab/htmltotsv @@ -0,0 +1,102 @@ +#! /usr/bin/perl + +#================================================================ +# htmldoctotsv +# Generate TSV data from HTML documents +#================================================================ + +use strict; +use warnings; +use Encode; +use Cwd 'realpath'; + +my $path = '.'; +my $mode_kv = 0; +my $mode_hex = 0; + +for(my $i = 0; $i < scalar(@ARGV); $i++){ + my $arg = $ARGV[$i]; + if($arg =~ /^-/){ + if($arg eq '-kv'){ + $mode_kv = 1; + } elsif($arg eq '-x'){ + $mode_hex = 1; + } + } else { + $path = $arg; + } +} + +sub trimhtml { + my $text = shift; + $text =~ s/<[^>]*>/ /g; + $text =~ s/<//g; + $text =~ s/"/"/g; + $text =~ s/ / /g; + $text =~ s/&/\&/g; + $text =~ s/\s+/ /g; + $text =~ s/^ *//; + $text =~ s/ *$//; + return $text; +} + +$ENV{LANG} = "C"; +$ENV{LC_ALL} = "C"; +open(my $lfh, "find $path -type f -iregex '.*\.html?' -print | sort |") || die("could not open"); +my $id = 0; +while(defined($path = <$lfh>)){ + chomp($path); + $path = realpath($path); + next if(!defined($path)); + my @stat = stat($path); + next if(scalar(@stat) < 10); + my $mtime = $stat[9]; + open(my $ifh, "<$path") || next; + my $encname = "UTF-8"; + my @lines; + while(defined(my $line = <$ifh>)){ + push(@lines, $line); + if($line =~ / 0); + } + } + my $text = join('', @lines); + $text = encode("UTF-8", decode($encname, $text)) if($encname ne "UTF-8"); + $text =~ s///is; + my $title = ""; + if($text =~ /]*>[^<]*<\/title>/i){ + $title = $text; + $title =~ s/.*]*>([^<]*)<\/title>.*/$1/is; + $title = trimhtml($title); + } + $text =~ s/.*]*>(.*)<\/body>.*/$1/is; + $text =~ s/]*>.*?<\/style>//is; + $text =~ s/]*>.*?<\/script>//is; + $text = trimhtml($text); + next if(length($title) < 1 && length($text) < 1); + $id++; + my $key = $mode_hex ? sprintf("%X", $id) : $id; + printf STDERR ("%d: saving: %s\n", $id, $path); + if($mode_kv){ + $text = $title . " " . $text if(length($title) > 0); + printf("%s\t%s\n", $key, $text); + } else { + printf("%s", $key); + printf("\turl\t%s", $path); + printf("\tsize\t%s", length($text)); + printf("\tmtime\t%s", $mtime); + printf("\ttitle\t%s", $title) if(length($title) > 0); + printf("\tbody\t%s", $text); + printf("\n"); + } + close($ifh); +} +close($lfh); + + + +# END OF FILE diff --git a/tcejdb/lab/magic b/tcejdb/lab/magic new file mode 100644 index 0000000..e65091a --- /dev/null +++ b/tcejdb/lab/magic @@ -0,0 +1,19 @@ +# Tokyo Cabinet magic data +0 string ToKyO\ CaBiNeT\n Tokyo Cabinet +>14 string x \b (%s) +>32 byte 0 \b, Hash +>32 byte 1 \b, B+ tree +>32 byte 2 \b, Fixed-length +>32 byte 3 \b, Table +>33 byte &1 \b, [open] +>33 byte &2 \b, [fatal] +>34 byte x \b, apow=%d +>35 byte x \b, fpow=%d +>36 byte &1 \b, [large] +>36 byte &2 \b, [deflate] +>36 byte &4 \b, [bzip] +>36 byte &8 \b, [tcbs] +>36 byte &16 \b, [excodec] +>40 lelong x \b, bnum=%d +>48 lelong x \b, rnum=%d +>56 lelong x \b, fsiz=%d diff --git a/tcejdb/lab/printenv.cgi b/tcejdb/lab/printenv.cgi new file mode 100755 index 0000000..6f8f3c9 --- /dev/null +++ b/tcejdb/lab/printenv.cgi @@ -0,0 +1,27 @@ +#! /bin/sh + +#================================================================ +# printenv.cgi +# Print CGI environment variables +#================================================================ + + +# set variables +LANG=C +LC_ALL=C +export LANG LC_ALL + + +# output the result +printf 'Content-Type: text/plain\r\n' +printf '\r\n' + +printenv | sort + + +# exit normally +exit 0 + + + +# END OF FILE diff --git a/tcejdb/lab/stepcount b/tcejdb/lab/stepcount new file mode 100755 index 0000000..a0092f3 --- /dev/null +++ b/tcejdb/lab/stepcount @@ -0,0 +1,26 @@ +#! /bin/sh + +#================================================================ +# stepcount +# Find files including dispensable tab characters +#================================================================ + + +# set variables +LANG=C +LC_ALL=C +export LANG LC_ALL +regex='(\.h|\.c|\.cc|\.java|\.pl|\.pm|\.xs|\.rb|\.js)$' + + +# count steps +files=`find . -type f | egrep $regex` +wc -l $files | sort -n + + +# exit normally +exit 0 + + + +# END OF FILE diff --git a/tcejdb/lab/stopwatch b/tcejdb/lab/stopwatch new file mode 100755 index 0000000..a583425 --- /dev/null +++ b/tcejdb/lab/stopwatch @@ -0,0 +1,61 @@ +#! /usr/bin/perl + +#================================================================ +# stopwatch +# Measure elapsed time of some test commands. +#================================================================ + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); + +use constant { + TESTCOUNT => 20, + REMOVETOP => 2, + REMOVEBOTTOM => 8, +}; + +my @commands = ( + "./tchtest write casket 1000000 1000000", + "./tchtest read casket", + "./tchtest rcat casket 1000000", + "./tcbtest write casket 1000000", + "./tcbtest read casket", + "./tcbtest rcat -lc 256 -nc 128 casket 100000", + ); + +my @table; +foreach my $command (@commands){ + system("sync ; sync"); + my @result; + for(my $i = 0; $i < TESTCOUNT; $i++){ + my $stime = gettimeofday(); + system("$command >/dev/null 2>&1"); + $stime = gettimeofday() - $stime; + printf("%s\t%d\t%0.5f\n", $command, $i + 1, $stime); + push(@result, $stime); + } + @result = sort { $a <=> $b } @result; + for(my $i = 0; $i < REMOVETOP; $i++){ + shift(@result); + } + for(my $i = 0; $i < REMOVEBOTTOM; $i++){ + pop(@result); + } + my $sum = 0; + foreach my $result (@result){ + $sum += $result; + } + my $avg = $sum / scalar(@result); + push(@table, [$command, $avg]); +} + +printf("\n\nRESULT\n"); +foreach my $row (@table){ + printf("%s\t%0.5f\n", $$row[0], $$row[1]); +} +printf("\n"); + + + +# END OF FILE diff --git a/tcejdb/lab/tabcheck b/tcejdb/lab/tabcheck new file mode 100755 index 0000000..ba6698e --- /dev/null +++ b/tcejdb/lab/tabcheck @@ -0,0 +1,43 @@ +#! /bin/sh + +#================================================================ +# tabcheck +# Find files including dispensable tab and space characters +#================================================================ + + +# set variables +LANG=C +LC_ALL=C +export LANG LC_ALL +regex='\.(h|c|cc|cpp|cxx|java|pl|pm|pod|rb|rd|lua)$' +tabcode=`printf '\t'` + + +# find tab +find . -type f | egrep $regex | +while read file +do + printf 'Checking %s ... ' $file + err=0 + if grep "$tabcode" $file > /dev/null + then + printf '### !!! TAB FOUND !!! ###' + err=1 + fi + if grep ' $' $file > /dev/null + then + printf '### !!! TAILING SPACE FOUND !!! ###' + err=1 + fi + [ "$err" = 0 ] && printf 'ok' + printf '\n' +done + + +# exit normally +exit 0 + + + +# END OF FILE diff --git a/tcejdb/lab/wgettsv b/tcejdb/lab/wgettsv new file mode 100755 index 0000000..21b718a --- /dev/null +++ b/tcejdb/lab/wgettsv @@ -0,0 +1,239 @@ +#! /usr/bin/ruby -w + +#================================================================ +# wgettsv +# Collect WWW resources and generate TSV data +#================================================================ + +require 'open-uri' +require 'iconv' +require 'kconv' +require 'date' +require 'time' +require 'cgi' + +def main + seeds = [] + hist = {} + filters = [] + max = 1 << 30 + lim = 1 << 20 + wait = 0 + ndf = false + i = 0 + while i < ARGV.length + if seeds.length < 1 && ARGV[i] =~ /^-/ + if ARGV[i] == '-allow' + usage if (i += 1) >= ARGV.length + regex = Regexp::new(ARGV[i]) + filters.push([true, regex]) if regex + elsif ARGV[i] == '-deny' + usage if (i += 1) >= ARGV.length + regex = Regexp::new(ARGV[i]) + filters.push([false, regex]) if regex + elsif ARGV[i] == '-max' + usage if (i += 1) >= ARGV.length + max = ARGV[i].to_i + elsif ARGV[i] == '-lim' + usage if (i += 1) >= ARGV.length + lim = ARGV[i].to_i + elsif ARGV[i] == '-wait' + usage if (i += 1) >= ARGV.length + wait = ARGV[i].to_f + elsif ARGV[i] == '-ndf' + ndf = true + else + usage + end + else + if ARGV[i] =~ /^http:\/\//i + seeds.push(ARGV[i]) + hist[ARGV[i]] = true + else + usage + end + end + i += 1 + end + usage if seeds.length < 1 + if !ndf + filters.push([false, /\.(txt|text|asc|c|cc|cxx|cpp|h|hxx|hpp|in)$/i]) + filters.push([false, /\.(css|js|csv|tsv|log|md5|crc|conf|ini|inf|lnk|sys|tmp|bak)$/i]) + filters.push([false, /\.(xml|xsl|xslt|rdf|rss|dtd|sgml|sgm)$/i]) + filters.push([false, /\.(pgp|sig|cer|csr|pem|key|b64|uu|uue|[0-9])$/i]) + filters.push([false, /\.(rtf|pdf|ps|eps|ai|doc|xls|ppt|sxw|sxc|sxi|xdw|jtd|oas|swf)$/i]) + filters.push([false, /\.(zip|tar|tgz|gz|bz2|tbz2|z|lha|lzh)?$/i]) + filters.push([false, /\.(7z|lzo|lzma|cpio|shar|cab|rar|sit|ace|hqx)?$/i]) + filters.push([false, /\.(bin|o|a|so|exe|dll|lib|obj|ocx|class|jar|war)?$/i]) + filters.push([false, /\.(rpm|deb|qdb|qdb|dbx|dbf|dat|msi|bat|com|iso)?$/i]) + filters.push([false, /\.(png|gif|jpg|jpeg|tif|tiff|bmp|ico|pbm|pgm|ppm|xbm|xpm|dvi)$/i]) + filters.push([false, /\.(au|snd|mid|midi|kar|smf|mp2|mp3|m3u|wav|wma|wmp|asx|at3|aif)$/i]) + filters.push([false, /\.(mpg|mpeg|qt|mov|avi|wmv|wvx|asf|ram|rm)$/i]) + filters.push([false, /\.(tch|tdb|tdf|tct)$/i]) + filters.push([false, /\.idx\./i]) + filters.push([false, /(core|casket|Makefile|README|NEWS|COPYING|LISENCE)($|\/)/i]) + end + return proc(seeds, hist, filters, max, lim, wait) ? 0 : 1 +end + +def usage + STDERR.printf("%s: collect WWW resources and generate TSV data\n", $progname) + STDERR.printf("\n") + STDERR.printf("usage:\n") + STDERR.printf(" %s [-allow regex] [-deny regex] [-max num] [-lim num] [-wait num]" + + " url ...\n", $progname) + STDERR.printf("\n") + exit(1) +end + +def proc(seeds, hist, filters, max, lim, wait) + cnt = 0 + while (url = seeds.shift) && cnt < max + STDERR.printf("%d: getting: %s\n", cnt + 1, url) + begin + opts = {} + OpenURI.open_uri(url, 0, 0, opts) do |sio| + baseuri = sio.base_uri + if baseuri && baseuri.to_s != url + url = baseuri.to_s + hist[url] = true + end + size = sio.size + raise "invalid size" if size > lim || size < 3 + type = sio.content_type + type = "text/plain" if !type + str = sio.read + head = str[0,2048] + if (head[0] == 0xfe && head[1] == 0xff) || (head[0] == 0xff && head[1] == 0xfe) + str = Kconv::kconv(str, Kconv::UTF8, Kconv::UTF16) + charset = "UTF-8" + elsif str.include?(0) + raise "binary data" + end + raise "not HTML" if type != "text/html" && head !~ / 0 + if charset !~ /^UTF-?8$/i + begin + nstr = Iconv.conv("UTF-8", charset, str) + str = nstr if nstr && nstr.length > 0 + rescue + str = str.toutf8 + end + end + else + str = str.toutf8 + end + body = str.gsub(/.*]*>/im, "") + body = body.gsub(/<\/body>.*/im, "") + body = htmltotext(body) + if str =~ /]*>[^<]*<\/title>/im + title = str.gsub(/.*]*>([^<]*)<\/title>.*/im, '\1') + title = htmltotext(title) + end + title = "" if !title + title = title[0,128] if title.length > 128 + mtime = sio.last_modified + if mtime + mtime = Time::parse(mtime.to_s).to_i + else + mtime = 0 + end + printf("%d", cnt + 1) + printf("\turl\t%s", url) + printf("\tsize\t%s", size) if size > 0 + printf("\tmtime\t%s", mtime) if mtime > 0 + printf("\ttitle\t%s", title) if title.length > 0 + printf("\tbody\t%s", body) if body.length > 0 + printf("\n") + str.gsub(/]*>/im) do |tag| + if tag =~ /href=["']?[^"'>]+["']?/ + href = tag.gsub(/.*href=["']?([^"'>]+)["']?.*/, '\1') + href = URI::join(url, href).to_s + href = href.gsub(/#.*/, "") + if !hist[href] && checkurl(href, filters) + seeds.push(href) + hist[href] = true + end + end + end + end + cnt += 1 + rescue + STDERR.printf("%d: failed: %s: %s\n", cnt + 1, url, $!) + end + sleep(wait) if wait > 0 + end + return 0 +end + +def htmltotext(str) + str = str.gsub(/]*>.*?<\/style>/im, " ") + str = str.gsub(/]*>.*?<\/script>/im, " ") + str = str.gsub(/<\/?(p|br|div|h1|h2|h3|h4|h5|h6|ul|ol|dl|li|dd|dt|td|th|pre)[^>]*>/im, " ") + str = str.gsub(/<[^>]*>/, "") + str = str.gsub(/&(nbsp|#160|#0160|#xa0|#x00a0);/i, " ") + hexrx = Regexp::new("^&#x[0-9a-zA-Z]+;") + decrx = Regexp::new("^&#[0-9]+;") + str = str.gsub(/&#?[A-Za-z0-9]+;/) do |pat| + case pat + when "<" + pat = '<' + when ">" + pat = '>' + when """ + pat = '"' + when "'" + pat = "'" + when " " + pat = " " + else + begin + if pat =~ hexrx + pat = [ pat.gsub(/&#x([A-Za-z0-9]+);/i, '\1').hex ].pack("n") + pat = Iconv.conv("UTF-8", "UTF-16BE", pat) + elsif pat =~ decrx + pat = [ pat.gsub(/&#([A-Za-z0-9]+);/i, '\1').to_i ].pack("n") + pat = Iconv.conv("UTF-8", "UTF-16BE", pat) + else + pat = " " + end + rescue + pat = "" + end + end + pat + end + str = str.gsub(/[\x00-\x20]/, " ") + str = str.gsub(/\xe3\x80\x80/, " ") + str = str.gsub(/ +/, " ") + str = str.gsub(/^ */, "") + str = str.gsub(/ *$/, "") + return str +end + +def checkurl(url, filters) + return false if url !~ /^http:\/\//i; + return true if filters.length < 1 + ok = !filters[0][0] + filters.each do |filter| + ok = filter[0] if url =~ filter[1] + end + return ok +end + +STDOUT.sync = true +$progname = $0.dup +$progname.gsub!(/.*\//, "") +srand +exit(main) + + + +# END OF FILE diff --git a/tcejdb/lab/widthcheck b/tcejdb/lab/widthcheck new file mode 100755 index 0000000..829c8f1 --- /dev/null +++ b/tcejdb/lab/widthcheck @@ -0,0 +1,57 @@ +#! /usr/bin/ruby -w + +#================================================================ +# widthcheck +# Find files including too wide lines +#================================================================ + + +LIMITWIDTH = 97 + +def checkfile(path) + printf("Checking: %s\n", path) + ok = true + open(path, "r") do |file| + num = 0 + file.each do |line| + num += 1 + line.chomp! + if line.length > LIMITWIDTH && !line.index("/*") && !line.index("*/") + printf("FOUND: %s: %d: %d: %s\n", path, num, line.length, line); + ok = false + end + end + end + return ok +end + +ok = true +list = Array::new(ARGV) +list.push(".") if list.empty? +while !list.empty? + path = list.shift + begin + if File::ftype(path) == "directory" + Dir.entries(path).each do |cpath| + if cpath != "." and cpath != ".." + list.push(path + "/" + cpath) + end + end + else + ok = false if !checkfile(path) + end + rescue + end +end + +if ok + printf("ALL OK\n") + exit(0) +else + printf("ERROR\n") + exit(1) +end + + + +# END OF FILE diff --git a/tcejdb/man/htmltoman b/tcejdb/man/htmltoman new file mode 100755 index 0000000..34301bd --- /dev/null +++ b/tcejdb/man/htmltoman @@ -0,0 +1,104 @@ +#! /usr/bin/awk -f + +function strip(text){ + gsub("^ *<[a-zA-Z0-9]*[^>]*>", "", text) + gsub(" *$", "", text) + return text +} + +function unescape(text){ + gsub("<", "<", text) + gsub(">", ">", text) + gsub(""", "\"", text) + gsub("&", "\\&", text) + gsub("-", "\\-", text) + return text +} + +BEGIN { + date = strftime("%Y-%m-%d") + printf(".TH \"%s\" %d \"%s\" \"%s\" \"%s\"\n\n", "INTRO", 3, date, "Man Page", "Tokyo Cabinet") +} + +/ *]*>.*<\/h[1-3]> *$/ { + text = $0 + text = strip(text) + text = unescape(text) + text = toupper(text) + printf("\n") + printf(".SH %s\n", text) +} + +/ *

]*>.*<\/p> *$/ { + text = $0 + text = strip(text) + text = gensub("]*>([^<]*)", "\\\\fB\\1\\\\fR", "g", text) + text = gensub("]*>([^<]*)", "\\\\fI\\1\\\\fR", "g", text) + gsub("<[^>]*>", "", text) + text = unescape(text) + printf(".PP\n") + printf("%s\n", text) +} + +/ *

]*> *$/ { + printf(".PP\n") + printf(".RS\n") +} +/ *<\/dl> *$/ { + printf(".RE\n") +} +/ *
]*>.*<\/dt> *$/ { + text = $0 + text = strip(text) + text = gensub("]*>([^<]*)", "\\\\fI\\1\\\\fB", "g", text) + gsub("<[^>]*>", "", text) + gsub("[\\||\\[|\\]]", "\\fR&\\fB", text) + text = unescape(text) + printf(".br\n") + printf("\\fB%s\\fR\n", text) +} +/ *
]*>.*<\/dd> *$/ { + text = $0 + text = strip(text) + text = gensub("]*>([^<]*)", "\\\\fB\\1\\\\fR", "g", text) + text = gensub("]*>([^<]*)", "\\\\fI\\1\\\\fR", "g", text) + gsub("<[^>]*>", "", text) + text = unescape(text) + printf(".RS\n") + printf("%s\n", text) + printf(".RE\n") +} + +/ *