tokyocabinet-1.4.48/0000755000175000017500000000000012013574446013313 5ustar mikiomikiotokyocabinet-1.4.48/tctmgr.c0000644000175000017500000010453312013574446014765 0ustar mikiomikio/************************************************************************************************* * The command line utility of the table database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" /* global variables */ const char *g_progname; // program name int g_dbgfd; // debugging output /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void printerr(TCTDB *tdb); static int printdata(const char *ptr, int size, bool px); static char *mygetline(FILE *ifp); static int runcreate(int argc, char **argv); static int runinform(int argc, char **argv); static int runput(int argc, char **argv); static int runout(int argc, char **argv); static int runget(int argc, char **argv); static int runlist(int argc, char **argv); static int runsearch(int argc, char **argv); static int runoptimize(int argc, char **argv); static int runsetindex(int argc, char **argv); static int runimporttsv(int argc, char **argv); static int runversion(int argc, char **argv); static int proccreate(const char *path, int bnum, int apow, int fpow, int opts); static int procinform(const char *path, int omode); static int procput(const char *path, const char *pkbuf, int pksiz, TCMAP *cols, int omode, int dmode); static int procout(const char *path, const char *pkbuf, int pksiz, int omode); static int procget(const char *path, const char *pkbuf, int pksiz, int omode, bool px, bool pz); static int proclist(const char *path, int omode, int max, bool pv, bool px, const char *fmstr); static int procsearch(const char *path, TCLIST *conds, const char *oname, const char *otype, int omode, int max, int skip, bool pv, bool px, bool kw, bool ph, int bt, bool rm, const char *mtype); static int procoptimize(const char *path, int bnum, int apow, int fpow, int opts, int omode, bool df); static int procsetindex(const char *path, const char *name, int omode, int type); static int procimporttsv(const char *path, const char *file, int omode, bool sc); static int procversion(void); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; g_dbgfd = -1; const char *ebuf = getenv("TCDBGFD"); if(ebuf) g_dbgfd = tcatoix(ebuf); if(argc < 2) usage(); int rv = 0; if(!strcmp(argv[1], "create")){ rv = runcreate(argc, argv); } else if(!strcmp(argv[1], "inform")){ rv = runinform(argc, argv); } else if(!strcmp(argv[1], "put")){ rv = runput(argc, argv); } else if(!strcmp(argv[1], "out")){ rv = runout(argc, argv); } else if(!strcmp(argv[1], "get")){ rv = runget(argc, argv); } else if(!strcmp(argv[1], "list")){ rv = runlist(argc, argv); } else if(!strcmp(argv[1], "search")){ rv = runsearch(argc, argv); } else if(!strcmp(argv[1], "optimize")){ rv = runoptimize(argc, argv); } else if(!strcmp(argv[1], "setindex")){ rv = runsetindex(argc, argv); } else if(!strcmp(argv[1], "importtsv")){ rv = runimporttsv(argc, argv); } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){ rv = runversion(argc, argv); } else { usage(); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: the command line utility of the table database API\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s create [-tl] [-td|-tb|-tt|-tx] path [bnum [apow [fpow]]]\n", g_progname); fprintf(stderr, " %s inform [-nl|-nb] path\n", g_progname); fprintf(stderr, " %s put [-nl|-nb] [-sx] [-dk|-dc|-dai|-dad] path pkey [cols...]\n", g_progname); fprintf(stderr, " %s out [-nl|-nb] [-sx] path pkey\n", g_progname); fprintf(stderr, " %s get [-nl|-nb] [-sx] [-px] [-pz] path pkey\n", g_progname); fprintf(stderr, " %s list [-nl|-nb] [-m num] [-pv] [-px] [-fm str] path\n", g_progname); fprintf(stderr, " %s search [-nl|-nb] [-ord name type] [-m num] [-sk num] [-kw] [-pv] [-px]" " [-ph] [-bt num] [-rm] [-ms type] path [name op expr ...]\n", g_progname); fprintf(stderr, " %s optimize [-tl] [-td|-tb|-tt|-tx] [-tz] [-nl|-nb] [-df]" " path [bnum [apow [fpow]]]\n", g_progname); fprintf(stderr, " %s setindex [-nl|-nb] [-it type] path name\n", g_progname); fprintf(stderr, " %s importtsv [-nl|-nb] [-sc] path [file]\n", g_progname); fprintf(stderr, " %s version\n", g_progname); fprintf(stderr, "\n"); exit(1); } /* print error information */ static void printerr(TCTDB *tdb){ const char *path = tctdbpath(tdb); int ecode = tctdbecode(tdb); fprintf(stderr, "%s: %s: %d: %s\n", g_progname, path ? path : "-", ecode, tctdberrmsg(ecode)); } /* print record data */ static int printdata(const char *ptr, int size, bool px){ int len = 0; while(size-- > 0){ if(px){ if(len > 0) putchar(' '); len += printf("%02X", *(unsigned char *)ptr); } else { if(strchr("\t\r\n", *ptr)){ putchar(' '); } else { putchar(*ptr); } len++; } ptr++; } return len; } /* read a line from a file descriptor */ static char *mygetline(FILE *ifp){ int len = 0; int blen = 1024; char *buf = tcmalloc(blen + 1); bool end = true; int c; while((c = fgetc(ifp)) != EOF){ end = false; if(c == '\0') continue; if(blen <= len){ blen *= 2; buf = tcrealloc(buf, blen + 1); } if(c == '\n' || c == '\r') c = '\0'; buf[len++] = c; if(c == '\0') break; } if(end){ tcfree(buf); return NULL; } buf[len] = '\0'; return buf; } /* parse arguments of create command */ static int runcreate(int argc, char **argv){ char *path = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; int opts = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tl")){ opts |= TDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= TDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= TDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= TDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= TDBTEXCODEC; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path) usage(); int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = proccreate(path, bnum, apow, fpow, opts); return rv; } /* parse arguments of inform command */ static int runinform(int argc, char **argv){ char *path = NULL; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procinform(path, omode); return rv; } /* parse arguments of put command */ static int runput(int argc, char **argv){ char *path = NULL; char *pkey = NULL; TCLIST *vals = tcmpoollistnew(tcmpoolglobal()); int omode = 0; int dmode = 0; bool sx = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-dk")){ dmode = -1; } else if(!strcmp(argv[i], "-dc")){ dmode = 1; } else if(!strcmp(argv[i], "-dai")){ dmode = 10; } else if(!strcmp(argv[i], "-dad")){ dmode = 11; } else if(!strcmp(argv[i], "-sx")){ sx = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!pkey){ pkey = argv[i]; } else { tclistpush2(vals, argv[i]); } } if(!path || !pkey) usage(); TCMAP *cols = tcmapnew(); char *pkbuf; int pksiz; if(sx){ pkbuf = tchexdecode(pkey, &pksiz); for(int i = 0; i < tclistnum(vals) - 1; i += 2){ const char *name = tclistval2(vals, i); const char *value = tclistval2(vals, i + 1); int nsiz; char *nbuf = tchexdecode(name, &nsiz); int vsiz; char *vbuf = tchexdecode(value, &vsiz); tcmapput(cols, nbuf, nsiz, vbuf, vsiz); tcfree(vbuf); tcfree(nbuf); } } else { pksiz = strlen(pkey); pkbuf = tcmemdup(pkey, pksiz); for(int i = 0; i < tclistnum(vals) - 1; i += 2){ const char *name = tclistval2(vals, i); const char *value = tclistval2(vals, i + 1); tcmapput2(cols, name, value); } } int rv = procput(path, pkbuf, pksiz, cols, omode, dmode); tcmapdel(cols); tcfree(pkbuf); return rv; } /* parse arguments of out command */ static int runout(int argc, char **argv){ char *path = NULL; char *pkey = NULL; int omode = 0; bool sx = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-sx")){ sx = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!pkey){ pkey = argv[i]; } else { usage(); } } if(!path || !pkey) usage(); int pksiz; char *pkbuf; if(sx){ pkbuf = tchexdecode(pkey, &pksiz); } else { pksiz = strlen(pkey); pkbuf = tcmemdup(pkey, pksiz); } int rv = procout(path, pkbuf, pksiz, omode); tcfree(pkbuf); return rv; } /* parse arguments of get command */ static int runget(int argc, char **argv){ char *path = NULL; char *pkey = NULL; int omode = 0; bool sx = false; bool px = false; bool pz = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-sx")){ sx = true; } else if(!strcmp(argv[i], "-px")){ px = true; } else if(!strcmp(argv[i], "-pz")){ pz = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!pkey){ pkey = argv[i]; } else { usage(); } } if(!path || !pkey) usage(); int pksiz; char *pkbuf; if(sx){ pkbuf = tchexdecode(pkey, &pksiz); } else { pksiz = strlen(pkey); pkbuf = tcmemdup(pkey, pksiz); } int rv = procget(path, pkbuf, pksiz, omode, px, pz); tcfree(pkbuf); return rv; } /* parse arguments of list command */ static int runlist(int argc, char **argv){ char *path = NULL; int omode = 0; int max = -1; bool pv = false; bool px = false; char *fmstr = NULL; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-m")){ if(++i >= argc) usage(); max = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-pv")){ pv = true; } else if(!strcmp(argv[i], "-px")){ px = true; } else if(!strcmp(argv[i], "-fm")){ if(++i >= argc) usage(); fmstr = argv[i]; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = proclist(path, omode, max, pv, px, fmstr); return rv; } /* parse arguments of search command */ static int runsearch(int argc, char **argv){ char *path = NULL; TCLIST *conds = tcmpoollistnew(tcmpoolglobal()); char *oname = NULL; char *otype = NULL; int omode = 0; int max = -1; int skip = -1; bool pv = false; bool px = false; bool kw = false; bool ph = false; int bt = 0; bool rm = false; char *mtype = NULL; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-ord")){ if(++i >= argc) usage(); oname = argv[i]; if(++i >= argc) usage(); otype = argv[i]; } else if(!strcmp(argv[i], "-m")){ if(++i >= argc) usage(); max = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-sk")){ if(++i >= argc) usage(); skip = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-kw")){ kw = true; } else if(!strcmp(argv[i], "-pv")){ pv = true; } else if(!strcmp(argv[i], "-px")){ px = true; } else if(!strcmp(argv[i], "-ph")){ ph = true; } else if(!strcmp(argv[i], "-bt")){ if(++i >= argc) usage(); bt = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-rm")){ rm = true; } else if(!strcmp(argv[i], "-ms")){ if(++i >= argc) usage(); mtype = argv[i]; } else { usage(); } } else if(!path){ path = argv[i]; } else { tclistpush2(conds, argv[i]); } } if(!path || tclistnum(conds) % 3 != 0) usage(); int rv = procsearch(path, conds, oname, otype, omode, max, skip, pv, px, kw, ph, bt, rm, mtype); return rv; } /* parse arguments of optimize command */ static int runoptimize(int argc, char **argv){ char *path = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; int opts = UINT8_MAX; int omode = 0; bool df = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tl")){ if(opts == UINT8_MAX) opts = 0; opts |= TDBTLARGE; } else if(!strcmp(argv[i], "-td")){ if(opts == UINT8_MAX) opts = 0; opts |= TDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ if(opts == UINT8_MAX) opts = 0; opts |= TDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ if(opts == UINT8_MAX) opts = 0; opts |= TDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ if(opts == UINT8_MAX) opts = 0; opts |= TDBTEXCODEC; } else if(!strcmp(argv[i], "-tz")){ if(opts == UINT8_MAX) opts = 0; } else if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-df")){ df = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path) usage(); int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = procoptimize(path, bnum, apow, fpow, opts, omode, df); return rv; } /* parse arguments of setindex command */ static int runsetindex(int argc, char **argv){ char *path = NULL; char *name = NULL; int omode = 0; int type = TDBITLEXICAL; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-it")){ if(++i >= argc) usage(); type = tctdbstrtoindextype(argv[i]); } else { usage(); } } else if(!path){ path = argv[i]; } else if(!name){ name = argv[i]; } else { usage(); } } if(!path || !name) usage(); if(type < 0) usage(); int rv = procsetindex(path, name, omode, type); return rv; } /* parse arguments of importtsv command */ static int runimporttsv(int argc, char **argv){ char *path = NULL; char *file = NULL; int omode = 0; bool sc = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-sc")){ sc = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!file){ file = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procimporttsv(path, file, omode, sc); return rv; } /* parse arguments of version command */ static int runversion(int argc, char **argv){ int rv = procversion(); return rv; } /* perform create command */ static int proccreate(const char *path, int bnum, int apow, int fpow, int opts){ TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); if(!tctdbtune(tdb, bnum, apow, fpow, opts)){ printerr(tdb); tctdbdel(tdb); return 1; } if(!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | TDBOTRUNC)){ printerr(tdb); tctdbdel(tdb); return 1; } bool err = false; if(!tctdbclose(tdb)){ printerr(tdb); err = true; } tctdbdel(tdb); return err ? 1 : 0; } /* perform inform command */ static int procinform(const char *path, int omode){ TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL); if(!tctdbopen(tdb, path, TDBOREADER | omode)){ printerr(tdb); tctdbdel(tdb); return 1; } bool err = false; const char *npath = tctdbpath(tdb); if(!npath) npath = "(unknown)"; printf("path: %s\n", npath); printf("database type: table\n"); uint8_t flags = tctdbflags(tdb); printf("additional flags:"); if(flags & TDBFOPEN) printf(" open"); if(flags & TDBFFATAL) printf(" fatal"); printf("\n"); printf("bucket number: %llu\n", (unsigned long long)tctdbbnum(tdb)); if(tdb->hdb->cnt_writerec >= 0) printf("used bucket number: %lld\n", (long long)tctdbbnumused(tdb)); printf("alignment: %u\n", tctdbalign(tdb)); printf("free block pool: %u\n", tctdbfbpmax(tdb)); printf("index number: %d\n", tctdbinum(tdb)); TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; for(int i = 0; i < inum; i++){ TDBIDX *idxp = idxs + i; switch(idxp->type){ case TDBITLEXICAL: printf(" name=%s, type=lexical, rnum=%lld, fsiz=%lld\n", idxp->name, (long long)tcbdbrnum(idxp->db), (long long)tcbdbfsiz(idxp->db)); break; case TDBITDECIMAL: printf(" name=%s, type=decimal, rnum=%lld, fsiz=%lld\n", idxp->name, (long long)tcbdbrnum(idxp->db), (long long)tcbdbfsiz(idxp->db)); break; case TDBITTOKEN: printf(" name=%s, type=token, rnum=%lld, fsiz=%lld\n", idxp->name, (long long)tcbdbrnum(idxp->db), (long long)tcbdbfsiz(idxp->db)); break; case TDBITQGRAM: printf(" name=%s, type=qgram, rnum=%lld, fsiz=%lld\n", idxp->name, (long long)tcbdbrnum(idxp->db), (long long)tcbdbfsiz(idxp->db)); break; } } printf("unique ID seed: %lld\n", (long long)tctdbuidseed(tdb)); printf("inode number: %lld\n", (long long)tctdbinode(tdb)); char date[48]; tcdatestrwww(tctdbmtime(tdb), INT_MAX, date); printf("modified time: %s\n", date); uint8_t opts = tctdbopts(tdb); printf("options:"); if(opts & TDBTLARGE) printf(" large"); if(opts & TDBTDEFLATE) printf(" deflate"); if(opts & TDBTBZIP) printf(" bzip"); if(opts & TDBTTCBS) printf(" tcbs"); if(opts & TDBTEXCODEC) printf(" excodec"); printf("\n"); printf("record number: %llu\n", (unsigned long long)tctdbrnum(tdb)); printf("file size: %llu\n", (unsigned long long)tctdbfsiz(tdb)); if(!tctdbclose(tdb)){ if(!err) printerr(tdb); err = true; } tctdbdel(tdb); return err ? 1 : 0; } /* perform put command */ static int procput(const char *path, const char *pkbuf, int pksiz, TCMAP *cols, int omode, int dmode){ TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); if(!tctdbopen(tdb, path, TDBOWRITER | omode)){ printerr(tdb); tctdbdel(tdb); return 1; } bool err = false; char pknumbuf[TCNUMBUFSIZ]; if(pksiz < 1){ pksiz = sprintf(pknumbuf, "%lld", (long long)tctdbgenuid(tdb)); pkbuf = pknumbuf; } const char *vbuf; switch(dmode){ case -1: if(!tctdbputkeep(tdb, pkbuf, pksiz, cols)){ printerr(tdb); err = true; } break; case 1: if(!tctdbputcat(tdb, pkbuf, pksiz, cols)){ printerr(tdb); err = true; } break; case 10: vbuf = tcmapget2(cols, "_num"); if(!vbuf) vbuf = "1"; if(tctdbaddint(tdb, pkbuf, pksiz, tcatoi(vbuf)) == INT_MIN){ printerr(tdb); err = true; } break; case 11: vbuf = tcmapget2(cols, "_num"); if(!vbuf) vbuf = "1.0"; if(isnan(tctdbadddouble(tdb, pkbuf, pksiz, tcatof(vbuf)))){ printerr(tdb); err = true; } break; default: if(!tctdbput(tdb, pkbuf, pksiz, cols)){ printerr(tdb); err = true; } break; } if(!tctdbclose(tdb)){ if(!err) printerr(tdb); err = true; } tctdbdel(tdb); return err ? 1 : 0; } /* perform out command */ static int procout(const char *path, const char *pkbuf, int pksiz, int omode){ TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); if(!tctdbopen(tdb, path, TDBOWRITER | omode)){ printerr(tdb); tctdbdel(tdb); return 1; } bool err = false; if(!tctdbout(tdb, pkbuf, pksiz)){ printerr(tdb); err = true; } if(!tctdbclose(tdb)){ if(!err) printerr(tdb); err = true; } tctdbdel(tdb); return err ? 1 : 0; } /* perform get command */ static int procget(const char *path, const char *pkbuf, int pksiz, int omode, bool px, bool pz){ TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); if(!tctdbopen(tdb, path, TDBOREADER | omode)){ printerr(tdb); tctdbdel(tdb); return 1; } bool err = false; TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); if(cols){ tcmapiterinit(cols); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(cols, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); printdata(kbuf, ksiz, px); putchar('\t'); printdata(vbuf, vsiz, px); putchar(pz ? '\t' : '\n'); } tcmapdel(cols); } else { printerr(tdb); err = true; } if(!tctdbclose(tdb)){ if(!err) printerr(tdb); err = true; } tctdbdel(tdb); return err ? 1 : 0; } /* perform list command */ static int proclist(const char *path, int omode, int max, bool pv, bool px, const char *fmstr){ TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); if(!tctdbopen(tdb, path, TDBOREADER | omode)){ printerr(tdb); tctdbdel(tdb); return 1; } bool err = false; if(fmstr){ TCLIST *pkeys = tctdbfwmkeys2(tdb, fmstr, max); for(int i = 0; i < tclistnum(pkeys); i++){ int pksiz; const char *pkbuf = tclistval(pkeys, i, &pksiz); printdata(pkbuf, pksiz, px); if(pv){ TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); if(cols){ tcmapiterinit(cols); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(cols, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); putchar('\t'); printdata(kbuf, ksiz, px); putchar('\t'); printdata(vbuf, vsiz, px); } tcmapdel(cols); } } putchar('\n'); } tclistdel(pkeys); } else { if(!tctdbiterinit(tdb)){ printerr(tdb); err = true; } int cnt = 0; TCMAP *cols; while((cols = tctdbiternext3(tdb)) != NULL){ int pksiz; const char *pkbuf = tcmapget(cols, "", 0, &pksiz); if(pkbuf){ printdata(pkbuf, pksiz, px); if(pv){ tcmapiterinit(cols); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(cols, &ksiz)) != NULL){ if(*kbuf == '\0') continue; int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); putchar('\t'); printdata(kbuf, ksiz, px); putchar('\t'); printdata(vbuf, vsiz, px); } } } tcmapdel(cols); putchar('\n'); if(max >= 0 && ++cnt >= max) break; } } if(!tctdbclose(tdb)){ if(!err) printerr(tdb); err = true; } tctdbdel(tdb); return err ? 1 : 0; } /* perform search command */ static int procsearch(const char *path, TCLIST *conds, const char *oname, const char *otype, int omode, int max, int skip, bool pv, bool px, bool kw, bool ph, int bt, bool rm, const char *mtype){ TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); if(!tctdbopen(tdb, path, (rm ? TDBOWRITER : TDBOREADER) | omode)){ printerr(tdb); tctdbdel(tdb); return 1; } bool err = false; TDBQRY *qry = tctdbqrynew(tdb); int cnum = tclistnum(conds); for(int i = 0; i < cnum - 2; i += 3){ const char *name = tclistval2(conds, i); const char *opstr = tclistval2(conds, i + 1); const char *expr = tclistval2(conds, i + 2); int op = tctdbqrystrtocondop(opstr); if(op >= 0) tctdbqryaddcond(qry, name, op, expr); } if(oname){ int type = tctdbqrystrtoordertype(otype); if(type >= 0) tctdbqrysetorder(qry, oname, type); } tctdbqrysetlimit(qry, max, skip); if(rm){ double stime = tctime(); if(!tctdbqrysearchout(qry)){ printerr(tdb); err = true; } double etime = tctime(); if(ph){ TCLIST *hints = tcstrsplit(tctdbqryhint(qry), "\n"); int hnum = tclistnum(hints); for(int i = 0; i < hnum; i++){ const char *hint = tclistval2(hints, i); if(*hint == '\0') continue; printf("\t:::: %s\n", hint); } tclistdel(hints); printf("\t:::: number of records: %d\n", tctdbqrycount(qry)); printf("\t:::: elapsed time: %.5f\n", etime - stime); } } else if(bt > 0){ double sum = 0; for(int i = 1; i <= bt; i++){ double stime = tctime(); TCLIST *res = tctdbqrysearch(qry); double etime = tctime(); tclistdel(res); printf("%d: %.5f sec.\n", i, etime - stime); sum += etime - stime; } printf("----\n"); printf("total: %.5f sec. (%.5f s/q = %.5f q/s)\n", sum, sum / bt, bt / sum); } else { double stime = tctime(); TCLIST *res; TCLIST *hints; int count; int mtnum = mtype ? tctdbmetastrtosettype(mtype) : -1; if(mtnum >= 0){ TDBQRY *qrys[cnum/3+1]; int qnum = 0; for(int i = 0; i < cnum - 2; i += 3){ const char *name = tclistval2(conds, i); const char *opstr = tclistval2(conds, i + 1); const char *expr = tclistval2(conds, i + 2); int op = tctdbqrystrtocondop(opstr); if(op >= 0){ qrys[qnum] = tctdbqrynew(tdb); tctdbqryaddcond(qrys[qnum], name, op, expr); if(oname){ int type = tctdbqrystrtoordertype(otype); if(type >= 0) tctdbqrysetorder(qrys[qnum], oname, type); } tctdbqrysetlimit(qrys[qnum], max, skip); qnum++; } } res = tctdbmetasearch(qrys, qnum, mtnum); hints = qnum > 0 ? tcstrsplit(tctdbqryhint(qrys[0]), "\n") : tclistnew2(1); count = qnum > 0 ? tctdbqrycount(qrys[0]) : 0; for(int i = 0; i < qnum; i++){ tctdbqrydel(qrys[i]); } } else { res = tctdbqrysearch(qry); hints = tcstrsplit(tctdbqryhint(qry), "\n"); count = tctdbqrycount(qry); } double etime = tctime(); if(max < 0) max = INT_MAX; int rnum = tclistnum(res); for(int i = 0; i < rnum && max > 0; i++){ int pksiz; const char *pkbuf = tclistval(res, i, &pksiz); if(kw){ TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); if(cols){ TCLIST *texts = tctdbqrykwic(qry, cols, NULL, 16, TCKWMUTAB); int tnum = tclistnum(texts); for(int j = 0; j < tnum && max > 0; j++){ int tsiz; const char *text = tclistval(texts, j, &tsiz); printdata(pkbuf, pksiz, px); putchar('\t'); printdata(text, tsiz, px); putchar('\n'); max--; } tclistdel(texts); tcmapdel(cols); } } else { printdata(pkbuf, pksiz, px); if(pv){ TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); if(cols){ tcmapiterinit(cols); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(cols, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); putchar('\t'); printdata(kbuf, ksiz, px); putchar('\t'); printdata(vbuf, vsiz, px); } tcmapdel(cols); } } putchar('\n'); max--; } } if(ph){ int hnum = tclistnum(hints); for(int i = 0; i < hnum; i++){ const char *hint = tclistval2(hints, i); if(*hint == '\0') continue; printf("\t:::: %s\n", hint); } printf("\t:::: number of records: %d\n", count); printf("\t:::: elapsed time: %.5f\n", etime - stime); } tclistdel(hints); tclistdel(res); } tctdbqrydel(qry); if(!tctdbclose(tdb)){ if(!err) printerr(tdb); err = true; } tctdbdel(tdb); return err ? 1 : 0; } /* perform optimize command */ static int procoptimize(const char *path, int bnum, int apow, int fpow, int opts, int omode, bool df){ TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); int64_t msiz = 0; TCMAP *info = tcsysinfo(); if(info){ msiz = tcatoi(tcmapget4(info, "total", "0")); tcmapdel(info); } if(!tctdbsetinvcache(tdb, msiz >= (1 << 30) ? msiz / 4 : 0, 1.0)) printerr(tdb); if(!tctdbopen(tdb, path, TDBOWRITER | omode)){ printerr(tdb); tctdbdel(tdb); return 1; } bool err = false; if(df){ if(!tctdbdefrag(tdb, INT64_MAX)){ printerr(tdb); err = true; } } else { if(!tctdboptimize(tdb, bnum, apow, fpow, opts)){ printerr(tdb); err = true; } } if(!tctdbclose(tdb)){ if(!err) printerr(tdb); err = true; } tctdbdel(tdb); return err ? 1 : 0; } /* perform setindex command */ static int procsetindex(const char *path, const char *name, int omode, int type){ TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); int64_t msiz = 0; TCMAP *info = tcsysinfo(); if(info){ msiz = tcatoi(tcmapget4(info, "total", "0")); tcmapdel(info); } if(!tctdbsetinvcache(tdb, msiz >= (1 << 30) ? msiz / 4 : 0, 1.0)) printerr(tdb); if(!tctdbopen(tdb, path, TDBOWRITER | omode)){ printerr(tdb); tctdbdel(tdb); return 1; } bool err = false; if(!tctdbsetindex(tdb, name, type)){ printerr(tdb); err = true; } if(!tctdbclose(tdb)){ if(!err) printerr(tdb); err = true; } tctdbdel(tdb); return err ? 1 : 0; } /* perform importtsv command */ static int procimporttsv(const char *path, const char *file, int omode, bool sc){ FILE *ifp = file ? fopen(file, "rb") : stdin; if(!ifp){ fprintf(stderr, "%s: could not open\n", file ? file : "(stdin)"); return 1; } TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(tdb); int64_t msiz = 0; TCMAP *info = tcsysinfo(); if(info){ msiz = tcatoi(tcmapget4(info, "total", "0")); tcmapdel(info); } if(!tctdbsetinvcache(tdb, msiz >= (1 << 30) ? msiz / 4 : 0, 1.0)) printerr(tdb); if(!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | omode)){ printerr(tdb); tctdbdel(tdb); if(ifp != stdin) fclose(ifp); return 1; } bool err = false; char *line, numbuf[TCNUMBUFSIZ]; int cnt = 0; while(!err && (line = mygetline(ifp)) != NULL){ char *pv = strchr(line, '\t'); if(!pv){ tcfree(line); continue; } *pv = '\0'; if(sc) tcstrutfnorm(line, TCUNSPACE | TCUNLOWER | TCUNNOACC | TCUNWIDTH); const char *pkey; if(*line == '\0'){ sprintf(numbuf, "%lld", (long long)tctdbgenuid(tdb)); pkey = numbuf; } else { pkey = line; } if(!tctdbput3(tdb, pkey, pv + 1)){ printerr(tdb); err = true; } tcfree(line); if(cnt > 0 && cnt % 100 == 0){ putchar('.'); fflush(stdout); if(cnt % 5000 == 0) printf(" (%08d)\n", cnt); } cnt++; } printf(" (%08d)\n", cnt); if(!tctdbclose(tdb)){ if(!err) printerr(tdb); err = true; } tctdbdel(tdb); if(ifp != stdin) fclose(ifp); return err ? 1 : 0; } /* perform version command */ static int procversion(void){ printf("Tokyo Cabinet version %s (%d:%s) for %s\n", tcversion, _TC_LIBVER, _TC_FORMATVER, TCSYSNAME); printf("Copyright (C) 2006-2012 FAL Labs\n"); return 0; } // END OF FILE tokyocabinet-1.4.48/README0000644000175000017500000000202611525204221014157 0ustar mikiomikio================================================================ 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 == tokyocabinet-1.4.48/tctdb.c0000644000175000017500000057440612013574446014577 0ustar mikiomikio/************************************************************************************************* * The table database API of Tokyo Cabinet * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include "tcutil.h" #include "tchdb.h" #include "tcbdb.h" #include "tctdb.h" #include "myconf.h" #define TDBOPAQUESIZ 64 // size of using opaque field #define TDBLEFTOPQSIZ 64 // size of left opaque field #define TDBPAGEBUFSIZ 32768 // size of a buffer to read each page #define TDBDEFBNUM 131071 // default bucket number #define TDBDEFAPOW 4 // default alignment power #define TDBDEFFPOW 10 // default free block pool power #define TDBDEFLCNUM 4096 // default number of leaf cache #define TDBDEFNCNUM 512 // default number of node cache #define TDBDEFXMSIZ (64LL<<20) // default size of the extra mapped memory #define TDBIDXSUFFIX "idx" // suffix of column index file #define TDBIDXLMEMB 64 // number of members in each leaf of the index #define TDBIDXNMEMB 256 // number of members in each node of the index #define TDBIDXLSMAX 4096 // maximum size of each leaf of the index #define TDBIDXICCBNUM 262139 // bucket number of the index cache #define TDBIDXICCMAX (64LL<<20) // maximum size of the index cache #define TDBIDXICCSYNC 0.01 // ratio of cache synchronization #define TDBIDXQGUNIT 3 // unit number of the q-gram index #define TDBFTSUNITMAX 32 // maximum number of full-text search units #define TDBFTSOCRUNIT 8192 // maximum number of full-text search units #define TDBFTSBMNUM 524287 // number of elements of full-text search bitmap #define TDBNUMCNTCOL "_num" // column name of number counting #define TDBCOLBUFSIZ 1024 // size of a buffer for a column value #define TDBNUMCOLMAX 16 // maximum number of columns of the long double #define TDBHINTUSIZ 256 // unit size of the hint string #define TDBORDRATIO 0.2 // ratio of records to use the order index enum { // enumeration for duplication behavior TDBPDOVER, // overwrite an existing value TDBPDKEEP, // keep the existing value TDBPDCAT // concatenate values }; typedef struct { // type of structure for a sort record const char *kbuf; // pointer to the primary key int ksiz; // size of the primary key char *vbuf; // pointer to the value int vsiz; // size of the value } TDBSORTREC; typedef struct { // type of structure for a full-text search unit TCLIST *tokens; // q-gram tokens bool sign; // positive sign } TDBFTSUNIT; typedef struct { // type of structure for a full-text string occurrence const char *pkbuf; // primary key string int32_t pksiz; // size of the primary key int32_t off; // offset of the token uint16_t seq; // sequence number uint16_t hash; // hash value for counting sort } TDBFTSSTROCR; typedef struct { // type of structure for a full-text number occurrence int64_t pkid; // primery key number int32_t off; // offset of the token uint16_t seq; // sequence number uint16_t hash; // hash value for counting sort } TDBFTSNUMOCR; /* private macros */ #define TDBLOCKMETHOD(TC_tdb, TC_wr) \ ((TC_tdb)->mmtx ? tctdblockmethod((TC_tdb), (TC_wr)) : true) #define TDBUNLOCKMETHOD(TC_tdb) \ ((TC_tdb)->mmtx ? tctdbunlockmethod(TC_tdb) : true) #define TDBTHREADYIELD(TC_tdb) \ do { if((TC_tdb)->mmtx) sched_yield(); } while(false) /* private function prototypes */ static void tctdbclear(TCTDB *tdb); static bool tctdbopenimpl(TCTDB *tdb, const char *path, int omode); static bool tctdbcloseimpl(TCTDB *tdb); static bool tctdbputimpl(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols, int dmode); static bool tctdboutimpl(TCTDB *tdb, const char *pkbuf, int pksiz); static TCMAP *tctdbgetimpl(TCTDB *tdb, const void *pkbuf, int pksiz); static char *tctdbgetonecol(TCTDB *tdb, const void *pkbuf, int pksiz, const void *nbuf, int nsiz, int *sp); static double tctdbaddnumber(TCTDB *tdb, const void *pkbuf, int pksiz, double num); static bool tctdboptimizeimpl(TCTDB *tdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts); static bool tctdbvanishimpl(TCTDB *tdb); static bool tctdbcopyimpl(TCTDB *tdb, const char *path); static bool tctdbtranbeginimpl(TCTDB *tdb); static bool tctdbtrancommitimpl(TCTDB *tdb); static bool tctdbtranabortimpl(TCTDB *tdb); static bool tctdbsetindeximpl(TCTDB *tdb, const char *name, int type); static int64_t tctdbgenuidimpl(TCTDB *tdb, int64_t inc); static TCLIST *tctdbqrysearchimpl(TDBQRY *qry); static TCMAP *tctdbqryidxfetch(TDBQRY *qry, TDBCOND *cond, TDBIDX *idx); static long double tctdbatof(const char *str); static bool tctdbqryidxcurjumpnum(BDBCUR *cur, const char *kbuf, int ksiz, bool first); static bool tctdbqryonecondmatch(TDBQRY *qry, TDBCOND *cond, const char *pkbuf, int pksiz); static bool tctdbqryallcondmatch(TDBQRY *qry, const char *pkbuf, int pksiz); static bool tctdbqrycondmatch(TDBCOND *cond, const char *vbuf, int vsiz); static bool tctdbqrycondcheckstrand(const char *tval, const char *oval); static bool tctdbqrycondcheckstror(const char *tval, const char *oval); static bool tctdbqrycondcheckstroreq(const char *vbuf, const char *expr); static bool tctdbqrycondchecknumbt(const char *vbuf, const char *expr); static bool tctdbqrycondchecknumoreq(const char *vbuf, const char *expr); static bool tctdbqrycondcheckfts(const char *vbuf, int vsiz, TDBCOND *cond); static int tdbcmppkeynumasc(const TCLISTDATUM *a, const TCLISTDATUM *b); static int tdbcmppkeynumdesc(const TCLISTDATUM *a, const TCLISTDATUM *b); static int tdbcmpsortrecstrasc(const TDBSORTREC *a, const TDBSORTREC *b); static int tdbcmpsortrecstrdesc(const TDBSORTREC *a, const TDBSORTREC *b); static int tdbcmpsortrecnumasc(const TDBSORTREC *a, const TDBSORTREC *b); static int tdbcmpsortrecnumdesc(const TDBSORTREC *a, const TDBSORTREC *b); static uint16_t tctdbidxhash(const char *pkbuf, int pksiz); static bool tctdbidxput(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols); static bool tctdbidxputone(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, uint16_t hash, const char *vbuf, int vsiz); static bool tctdbidxputtoken(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, const char *vbuf, int vsiz); static bool tctdbidxputqgram(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, const char *vbuf, int vsiz); static bool tctdbidxout(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols); static bool tctdbidxoutone(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, uint16_t hash, const char *vbuf, int vsiz); static bool tctdbidxouttoken(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, const char *vbuf, int vsiz); static bool tctdbidxoutqgram(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, const char *vbuf, int vsiz); static bool tctdbidxsyncicc(TCTDB *tdb, TDBIDX *idx, bool all); static int tctdbidxcmpkey(const char **a, const char **b); static TCMAP *tctdbidxgetbytokens(TCTDB *tdb, TDBIDX *idx, const TCLIST *tokens, int op, TCXSTR *hint); static TCMAP *tctdbidxgetbyfts(TCTDB *tdb, TDBIDX *idx, TDBCOND *cond, TCXSTR *hint); static void tctdbidxgetbyftsunion(TDBIDX *idx, const TCLIST *tokens, bool sign, TCMAP *ores, TCMAP *nres, TCXSTR *hint); static int tctdbidxftscmpstrocr(TDBFTSSTROCR *a, TDBFTSSTROCR *b); static int tctdbidxftscmpnumocr(TDBFTSNUMOCR *a, TDBFTSNUMOCR *b); static TDBFTSUNIT *tctdbftsparseexpr(const char *expr, int esiz, int op, int *np); static bool tctdbdefragimpl(TCTDB *tdb, int64_t step); static bool tctdbcacheclearimpl(TCTDB *tdb); static bool tctdbforeachimpl(TCTDB *tdb, TCITER iter, void *op); static int tctdbqryprocoutcb(const void *pkbuf, int pksiz, TCMAP *cols, void *op); static bool tctdblockmethod(TCTDB *tdb, bool wr); static bool tctdbunlockmethod(TCTDB *tdb); /* debugging function prototypes */ void tctdbprintmeta(TCTDB *tdb); /************************************************************************************************* * API *************************************************************************************************/ /* Get the message string corresponding to an error code. */ const char *tctdberrmsg(int ecode){ return tcerrmsg(ecode); } /* Create a table database object. */ TCTDB *tctdbnew(void){ TCTDB *tdb; TCMALLOC(tdb, sizeof(*tdb)); tctdbclear(tdb); tdb->hdb = tchdbnew(); tchdbtune(tdb->hdb, TDBDEFBNUM, TDBDEFAPOW, TDBDEFFPOW, 0); tchdbsetxmsiz(tdb->hdb, TDBDEFXMSIZ); return tdb; } /* Delete a table database object. */ void tctdbdel(TCTDB *tdb){ assert(tdb); if(tdb->open) tctdbclose(tdb); tchdbdel(tdb->hdb); if(tdb->mmtx){ pthread_rwlock_destroy(tdb->mmtx); TCFREE(tdb->mmtx); } TCFREE(tdb); } /* Get the last happened error code of a table database object. */ int tctdbecode(TCTDB *tdb){ assert(tdb); return tchdbecode(tdb->hdb); } /* Set mutual exclusion control of a table database object for threading. */ bool tctdbsetmutex(TCTDB *tdb){ assert(tdb); if(!TCUSEPTHREAD) return true; if(tdb->mmtx || tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } TCMALLOC(tdb->mmtx, sizeof(pthread_rwlock_t)); bool err = false; if(pthread_rwlock_init(tdb->mmtx, NULL) != 0) err = true; if(err){ TCFREE(tdb->mmtx); tdb->mmtx = NULL; return false; } return tchdbsetmutex(tdb->hdb); } /* 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){ assert(tdb); if(tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } tdb->opts = opts; uint8_t hopts = 0; if(opts & TDBTLARGE) hopts |= HDBTLARGE; if(opts & TDBTDEFLATE) hopts |= HDBTDEFLATE; if(opts & TDBTBZIP) hopts |= HDBTBZIP; if(opts & TDBTTCBS) hopts |= HDBTTCBS; if(opts & TDBTEXCODEC) hopts |= HDBTEXCODEC; bnum = (bnum > 0) ? bnum : TDBDEFBNUM; apow = (apow >= 0) ? apow : TDBDEFAPOW; fpow = (fpow >= 0) ? fpow : TDBDEFFPOW; return tchdbtune(tdb->hdb, bnum, apow, fpow, hopts); } /* Set the caching parameters of a table database object. */ bool tctdbsetcache(TCTDB *tdb, int32_t rcnum, int32_t lcnum, int32_t ncnum){ assert(tdb); if(tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } if(lcnum > 0) tdb->lcnum = lcnum; if(ncnum > 0) tdb->ncnum = ncnum; return tchdbsetcache(tdb->hdb, rcnum); } /* Set the size of the extra mapped memory of a table database object. */ bool tctdbsetxmsiz(TCTDB *tdb, int64_t xmsiz){ assert(tdb); if(tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } return tchdbsetxmsiz(tdb->hdb, xmsiz); } /* Set the unit step number of auto defragmentation of a table database object. */ bool tctdbsetdfunit(TCTDB *tdb, int32_t dfunit){ assert(tdb); if(tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } return tchdbsetdfunit(tdb->hdb, dfunit); } /* Open a database file and connect a table database object. */ bool tctdbopen(TCTDB *tdb, const char *path, int omode){ assert(tdb && path); if(!TDBLOCKMETHOD(tdb, true)) return false; if(tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } bool rv = tctdbopenimpl(tdb, path, omode); TDBUNLOCKMETHOD(tdb); return rv; } /* Close a table database object. */ bool tctdbclose(TCTDB *tdb){ assert(tdb); if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } bool rv = tctdbcloseimpl(tdb); TDBUNLOCKMETHOD(tdb); return rv; } /* Store a record into a table database object. */ bool tctdbput(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols){ assert(tdb && pkbuf && pksiz >= 0 && cols); int vsiz; if(tcmapget(cols, "", 0, &vsiz)){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open || !tdb->wmode){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } bool rv = tctdbputimpl(tdb, pkbuf, pksiz, cols, TDBPDOVER); TDBUNLOCKMETHOD(tdb); return rv; } /* 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){ assert(tdb && pkbuf && pksiz >= 0 && cbuf && csiz >= 0); TCMAP *cols = tcstrsplit4(cbuf, csiz); bool rv = tctdbput(tdb, pkbuf, pksiz, cols); tcmapdel(cols); return rv; } /* 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){ assert(tdb && pkstr && cstr); TCMAP *cols = tcstrsplit3(cstr, "\t"); bool rv = tctdbput(tdb, pkstr, strlen(pkstr), cols); tcmapdel(cols); return rv; } /* Store a new record into a table database object. */ bool tctdbputkeep(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols){ assert(tdb && pkbuf && pksiz >= 0 && cols); int vsiz; if(tcmapget(cols, "", 0, &vsiz)){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open || !tdb->wmode){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } bool rv = tctdbputimpl(tdb, pkbuf, pksiz, cols, TDBPDKEEP); TDBUNLOCKMETHOD(tdb); return rv; } /* 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){ assert(tdb && pkbuf && pksiz >= 0 && cbuf && csiz >= 0); TCMAP *cols = tcstrsplit4(cbuf, csiz); bool rv = tctdbputkeep(tdb, pkbuf, pksiz, cols); tcmapdel(cols); return rv; } /* 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){ assert(tdb && pkstr && cstr); TCMAP *cols = tcstrsplit3(cstr, "\t"); bool rv = tctdbputkeep(tdb, pkstr, strlen(pkstr), cols); tcmapdel(cols); return rv; } /* Concatenate columns of the existing record in a table database object. */ bool tctdbputcat(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols){ assert(tdb && pkbuf && pksiz >= 0 && cols); int vsiz; if(tcmapget(cols, "", 0, &vsiz)){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open || !tdb->wmode){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } bool rv = tctdbputimpl(tdb, pkbuf, pksiz, cols, TDBPDCAT); TDBUNLOCKMETHOD(tdb); return rv; } /* 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){ assert(tdb && pkbuf && pksiz >= 0 && cbuf && csiz >= 0); TCMAP *cols = tcstrsplit4(cbuf, csiz); bool rv = tctdbputcat(tdb, pkbuf, pksiz, cols); tcmapdel(cols); return rv; } /* Concatenate columns in a table database object with with a tab separated column string. */ bool tctdbputcat3(TCTDB *tdb, const char *pkstr, const char *cstr){ assert(tdb && pkstr && cstr); TCMAP *cols = tcstrsplit3(cstr, "\t"); bool rv = tctdbputcat(tdb, pkstr, strlen(pkstr), cols); tcmapdel(cols); return rv; } /* Remove a record of a table database object. */ bool tctdbout(TCTDB *tdb, const void *pkbuf, int pksiz){ assert(tdb && pkbuf && pksiz >= 0); if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open || !tdb->wmode){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } bool rv = tctdboutimpl(tdb, pkbuf, pksiz); TDBUNLOCKMETHOD(tdb); return rv; } /* Remove a string record of a table database object. */ bool tctdbout2(TCTDB *tdb, const char *pkstr){ assert(tdb && pkstr); return tctdbout(tdb, pkstr, strlen(pkstr)); } /* Retrieve a record in a table database object. */ TCMAP *tctdbget(TCTDB *tdb, const void *pkbuf, int pksiz){ assert(tdb && pkbuf && pksiz >= 0); if(!TDBLOCKMETHOD(tdb, false)) return NULL; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return NULL; } TCMAP *rv = tctdbgetimpl(tdb, pkbuf, pksiz); TDBUNLOCKMETHOD(tdb); return rv; } /* 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){ assert(tdb && pkbuf && pksiz >= 0 && sp); TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); if(!cols) return NULL; char *cbuf = tcstrjoin4(cols, sp); tcmapdel(cols); return cbuf; } /* Retrieve a string record in a table database object as a tab separated column string. */ char *tctdbget3(TCTDB *tdb, const char *pkstr){ assert(tdb && pkstr); TCMAP *cols = tctdbget(tdb, pkstr, strlen(pkstr)); if(!cols) return NULL; char *cstr = tcstrjoin3(cols, '\t'); tcmapdel(cols); return cstr; } /* Get the size of the value of a record in a table database object. */ int tctdbvsiz(TCTDB *tdb, const void *pkbuf, int pksiz){ assert(tdb && pkbuf && pksiz >= 0); if(!TDBLOCKMETHOD(tdb, false)) return -1; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return -1; } int rv = tchdbvsiz(tdb->hdb, pkbuf, pksiz); TDBUNLOCKMETHOD(tdb); return rv; } /* Get the size of the value of a string record in a table database object. */ int tctdbvsiz2(TCTDB *tdb, const char *pkstr){ assert(tdb && pkstr); return tctdbvsiz(tdb, pkstr, strlen(pkstr)); } /* Initialize the iterator of a table database object. */ bool tctdbiterinit(TCTDB *tdb){ assert(tdb); if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } bool rv = tchdbiterinit(tdb->hdb); TDBUNLOCKMETHOD(tdb); return rv; } /* Get the next primary key of the iterator of a table database object. */ void *tctdbiternext(TCTDB *tdb, int *sp){ assert(tdb && sp); if(!TDBLOCKMETHOD(tdb, true)) return NULL; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return NULL; } char *rv = tchdbiternext(tdb->hdb, sp); TDBUNLOCKMETHOD(tdb); return rv; } /* Get the next primary key string of the iterator of a table database object. */ char *tctdbiternext2(TCTDB *tdb){ assert(tdb); int pksiz; return tctdbiternext(tdb, &pksiz); } /* Get the columns of the next record of the iterator of a table database object. */ TCMAP *tctdbiternext3(TCTDB *tdb){ assert(tdb); TCXSTR *kstr = tcxstrnew(); TCXSTR *vstr = tcxstrnew(); TCMAP *cols = NULL; if(tchdbiternext3(tdb->hdb, kstr, vstr)){ cols = tcmapload(TCXSTRPTR(vstr), TCXSTRSIZE(vstr)); tcmapput(cols, "", 0, TCXSTRPTR(kstr), TCXSTRSIZE(kstr)); } tcxstrdel(vstr); tcxstrdel(kstr); return cols; } /* Get forward matching primary keys in a table database object. */ TCLIST *tctdbfwmkeys(TCTDB *tdb, const void *pbuf, int psiz, int max){ assert(tdb && pbuf && psiz >= 0); if(!TDBLOCKMETHOD(tdb, true)) return tclistnew(); if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return tclistnew(); } TCLIST *rv = tchdbfwmkeys(tdb->hdb, pbuf, psiz, max); TDBUNLOCKMETHOD(tdb); return rv; } /* Get forward matching string primary keys in a table database object. */ TCLIST *tctdbfwmkeys2(TCTDB *tdb, const char *pstr, int max){ assert(tdb && pstr); return tctdbfwmkeys(tdb, pstr, strlen(pstr), max); } /* 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){ assert(tdb && pkbuf && pksiz >= 0); if(!TDBLOCKMETHOD(tdb, true)) return INT_MIN; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return INT_MIN; } double rv = tctdbaddnumber(tdb, pkbuf, pksiz, num); TDBUNLOCKMETHOD(tdb); return isnan(rv) ? INT_MIN : (int)rv; } /* 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){ assert(tdb && pkbuf && pksiz >= 0); if(!TDBLOCKMETHOD(tdb, true)) return INT_MIN; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return INT_MIN; } double rv = tctdbaddnumber(tdb, pkbuf, pksiz, num); TDBUNLOCKMETHOD(tdb); return rv; } /* Synchronize updated contents of a table database object with the file and the device. */ bool tctdbsync(TCTDB *tdb){ assert(tdb); if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open || !tdb->wmode || tdb->tran){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } bool rv = tctdbmemsync(tdb, true); TDBUNLOCKMETHOD(tdb); return rv; } /* Optimize the file of a table database object. */ bool tctdboptimize(TCTDB *tdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){ assert(tdb); if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open || !tdb->wmode || tdb->tran){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } TDBTHREADYIELD(tdb); bool rv = tctdboptimizeimpl(tdb, bnum, apow, fpow, opts); TDBUNLOCKMETHOD(tdb); return rv; } /* Remove all records of a table database object. */ bool tctdbvanish(TCTDB *tdb){ assert(tdb); if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open || !tdb->wmode || tdb->tran){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } TDBTHREADYIELD(tdb); bool rv = tctdbvanishimpl(tdb); TDBUNLOCKMETHOD(tdb); return rv; } /* Copy the database file of a table database object. */ bool tctdbcopy(TCTDB *tdb, const char *path){ assert(tdb && path); if(!TDBLOCKMETHOD(tdb, false)) return false; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } TDBTHREADYIELD(tdb); bool rv = tctdbcopyimpl(tdb, path); TDBUNLOCKMETHOD(tdb); return rv; } /* Begin the transaction of a table database object. */ bool tctdbtranbegin(TCTDB *tdb){ assert(tdb); for(double wsec = 1.0 / sysconf(_SC_CLK_TCK); true; wsec *= 2){ if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open || !tdb->wmode){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } if(!tdb->tran) break; TDBUNLOCKMETHOD(tdb); if(wsec > 1.0) wsec = 1.0; tcsleep(wsec); } if(!tctdbtranbeginimpl(tdb)){ TDBUNLOCKMETHOD(tdb); return false; } tdb->tran = true; TDBUNLOCKMETHOD(tdb); return true; } /* Commit the transaction of a table database object. */ bool tctdbtrancommit(TCTDB *tdb){ assert(tdb); if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open || !tdb->wmode || !tdb->tran){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } tdb->tran = false; bool err = false; if(!tctdbtrancommitimpl(tdb)) err = true; TDBUNLOCKMETHOD(tdb); return !err; } /* Abort the transaction of a table database object. */ bool tctdbtranabort(TCTDB *tdb){ assert(tdb); if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open || !tdb->wmode || !tdb->tran){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } tdb->tran = false; bool err = false; if(!tctdbtranabortimpl(tdb)) err = true; TDBUNLOCKMETHOD(tdb); return !err; } /* Get the file path of a table database object. */ const char *tctdbpath(TCTDB *tdb){ assert(tdb); if(!TDBLOCKMETHOD(tdb, false)) return NULL; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return NULL; } const char *rv = tchdbpath(tdb->hdb); TDBUNLOCKMETHOD(tdb); return rv; } /* Get the number of records of a table database object. */ uint64_t tctdbrnum(TCTDB *tdb){ assert(tdb); if(!TDBLOCKMETHOD(tdb, false)) return 0; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return 0; } uint64_t rv = tchdbrnum(tdb->hdb); TDBUNLOCKMETHOD(tdb); return rv; } /* Get the size of the database file of a table database object. */ uint64_t tctdbfsiz(TCTDB *tdb){ assert(tdb); if(!TDBLOCKMETHOD(tdb, false)) return 0; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return 0; } uint64_t rv = tchdbfsiz(tdb->hdb); TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: case TDBITTOKEN: case TDBITQGRAM: rv += tcbdbfsiz(idx->db); break; } } TDBUNLOCKMETHOD(tdb); return rv; } /* Set a column index to a table database object. */ bool tctdbsetindex(TCTDB *tdb, const char *name, int type){ assert(tdb && name); if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open || !tdb->wmode || tdb->tran){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } bool err = false; double iccsync = tdb->iccsync; tdb->iccsync = 1.0; if(!tctdbsetindeximpl(tdb, name, type)) err = true; if(!tctdbmemsync(tdb, false)) err = true; tdb->iccsync = iccsync; TDBUNLOCKMETHOD(tdb); return !err; } /* Generate a unique ID number of a table database object. */ int64_t tctdbgenuid(TCTDB *tdb){ assert(tdb); if(!TDBLOCKMETHOD(tdb, true)) return -1; if(!tdb->open || !tdb->wmode){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return -1; } int64_t rv = tctdbgenuidimpl(tdb, 1); TDBUNLOCKMETHOD(tdb); return rv; } /* Create a query object. */ TDBQRY *tctdbqrynew(TCTDB *tdb){ assert(tdb); TDBQRY *qry; TCMALLOC(qry, sizeof(*qry)); qry->tdb = tdb; TCMALLOC(qry->conds, sizeof(qry->conds[0]) * 2); qry->cnum = 0; qry->oname = NULL; qry->otype = TDBQOSTRASC; qry->max = INT_MAX; qry->skip = 0; qry->hint = tcxstrnew3(TDBHINTUSIZ); qry->count = 0; return qry; } /* Delete a query object. */ void tctdbqrydel(TDBQRY *qry){ assert(qry); tcxstrdel(qry->hint); TCFREE(qry->oname); TDBCOND *conds = qry->conds; int cnum = qry->cnum; for(int i = 0; i < cnum; i++){ TDBCOND *cond = conds + i; if(cond->ftsunits){ TDBFTSUNIT *ftsunits = cond->ftsunits; int ftsnum = cond->ftsnum; for(int j = 0; j < ftsnum; j++){ TDBFTSUNIT *ftsunit = ftsunits + j; tclistdel(ftsunit->tokens); } TCFREE(ftsunits); } if(cond->regex){ regfree(cond->regex); TCFREE(cond->regex); } TCFREE(cond->expr); TCFREE(cond->name); } TCFREE(conds); TCFREE(qry); } /* Add a narrowing condition to a query object. */ void tctdbqryaddcond(TDBQRY *qry, const char *name, int op, const char *expr){ assert(qry && name && expr); int cnum = qry->cnum; TCREALLOC(qry->conds, qry->conds, sizeof(qry->conds[0]) * (cnum + 1)); TDBCOND *cond = qry->conds + cnum; int nsiz = strlen(name); int esiz = strlen(expr); TCMEMDUP(cond->name, name, nsiz); cond->nsiz = nsiz; bool sign = true; if(op & TDBQCNEGATE){ op &= ~TDBQCNEGATE; sign = false; } bool noidx = false; if(op & TDBQCNOIDX){ op &= ~TDBQCNOIDX; noidx = true; } cond->op = op; cond->sign = sign; cond->noidx = noidx; TCMEMDUP(cond->expr, expr, esiz); cond->esiz = esiz; cond->regex = NULL; if(op == TDBQCSTRRX){ const char *rxstr = expr; int rxopt = REG_EXTENDED | REG_NOSUB; if(*rxstr == '*'){ rxopt |= REG_ICASE; rxstr++; } regex_t rxbuf; if(regcomp(&rxbuf, rxstr, rxopt) == 0){ TCMALLOC(cond->regex, sizeof(rxbuf)); memcpy(cond->regex, &rxbuf, sizeof(rxbuf)); } } cond->ftsunits = NULL; cond->ftsnum = 0; if(op >= TDBQCFTSPH && op <= TDBQCFTSEX){ cond->op = TDBQCFTSPH; cond->ftsunits = tctdbftsparseexpr(expr, esiz, op, &(cond->ftsnum)); } qry->cnum++; } /* Set the order of a query object. */ void tctdbqrysetorder(TDBQRY *qry, const char *name, int type){ assert(qry && name); if(qry->oname) TCFREE(qry->oname); qry->oname = tcstrdup(name); qry->otype = type; } /* Set the limit number of records of the result of a query object. */ void tctdbqrysetlimit(TDBQRY *qry, int max, int skip){ assert(qry); qry->max = (max >= 0) ? max : INT_MAX; qry->skip = (skip > 0) ? skip : 0; } /* Execute the search of a query object. */ TCLIST *tctdbqrysearch(TDBQRY *qry){ assert(qry); TCTDB *tdb = qry->tdb; if(!TDBLOCKMETHOD(tdb, false)) return tclistnew(); if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return tclistnew(); } TCLIST *rv = tctdbqrysearchimpl(qry); TDBUNLOCKMETHOD(tdb); return rv; } /* Remove each record corresponding to a query object. */ bool tctdbqrysearchout(TDBQRY *qry){ assert(qry); return tctdbqryproc(qry, tctdbqryprocoutcb, NULL); } /* Process each record corresponding to a query object. */ bool tctdbqryproc(TDBQRY *qry, TDBQRYPROC proc, void *op){ assert(qry && proc); TCTDB *tdb = qry->tdb; if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open || !tdb->wmode){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } bool err = false; int64_t getnum = 0; int64_t putnum = 0; int64_t outnum = 0; TCLIST *res = tctdbqrysearchimpl(qry); int rnum = TCLISTNUM(res); for(int i = 0; i < rnum; i++){ const char *pkbuf; int pksiz; TCLISTVAL(pkbuf, res, i, pksiz); TCMAP *cols = tctdbgetimpl(tdb, pkbuf, pksiz); if(!cols){ err = true; continue; } getnum++; int flags = proc(pkbuf, pksiz, cols, op); if(flags & TDBQPPUT){ if(tctdbputimpl(tdb, pkbuf, pksiz, cols, TDBPDOVER)){ putnum++; } else { err = true; } } else if(flags & TDBQPOUT){ if(tctdboutimpl(tdb, pkbuf, pksiz)){ outnum++; } else { err = true; } } tcmapdel(cols); if(flags & TDBQPSTOP) break; } tclistdel(res); tcxstrprintf(qry->hint, "post treatment: get=%lld, put=%lld, out=%lld\n", (long long)getnum, (long long)putnum, (long long)outnum); TDBUNLOCKMETHOD(tdb); return !err; } /* Get the hint string of a query object. */ const char *tctdbqryhint(TDBQRY *qry){ assert(qry); return tcxstrptr(qry->hint); } /* Retrieve records with multiple query objects and get the set of the result. */ TCLIST *tctdbmetasearch(TDBQRY **qrys, int num, int type){ assert(qrys && num >= 0); if(num < 1) return tclistnew2(1); if(num < 2) return tctdbqrysearch(qrys[0]); const char *oname = qrys[0]->oname; int onsiz = oname ? strlen(oname) : 0; int otype = qrys[0]->otype; TCMAP *uset = tcmapnew(); if(type == TDBMSUNION){ for(int i = 0; i < num; i++){ TDBQRY *qry = qrys[i]; TCTDB *tdb = qry->tdb; int omax = qry->max; int oskip = qry->skip; if(qry->max < INT_MAX - qry->skip) qry->max += qry->skip; qry->skip = 0; TCLIST *res = tctdbqrysearch(qry); qry->max = omax; qry->skip = oskip; int rnum = TCLISTNUM(res); for(int j = 0; j < rnum; j++){ const char *pkbuf; int pksiz; TCLISTVAL(pkbuf, res, j, pksiz); if(oname){ int csiz; char *cbuf = tctdbget4(tdb, pkbuf, pksiz, oname, onsiz, &csiz); if(cbuf){ tcmapput4(uset, pkbuf, pksiz, "=", 1, cbuf, csiz); TCFREE(cbuf); } else { tcmapput(uset, pkbuf, pksiz, "*", 1); } } else { tcmapputkeep(uset, pkbuf, pksiz, "", 0); } } tclistdel(res); } } else if(type == TDBMSISECT){ int omax = qrys[0]->max; int oskip = qrys[0]->skip; qrys[0]->max = INT_MAX; qrys[0]->skip = 0; TCLIST *res = tctdbqrysearch(qrys[0]); qrys[0]->max = omax; qrys[0]->skip = oskip; int rnum = TCLISTNUM(res); for(int i = 0; i < rnum; i++){ const char *pkbuf; int pksiz; TCLISTVAL(pkbuf, res, i, pksiz); tcmapputkeep(uset, pkbuf, pksiz, "", 0); } tclistdel(res); for(int i = 1; i < num; i++){ TDBQRY *qry = qrys[i]; if(TCMAPRNUM(uset) < 1){ tcxstrclear(qry->hint); tcxstrprintf(qry->hint, "omitted\n"); continue; } omax = qry->max; oskip = qry->skip; qry->max = INT_MAX; qry->skip = 0; res = tctdbqrysearch(qry); qry->max = omax; qry->skip = oskip; rnum = TCLISTNUM(res); TCMAP *nset = tcmapnew2(tclmin(TCMAPRNUM(uset), rnum) + 1); for(int j = 0; j < rnum; j++){ const char *pkbuf; int pksiz; TCLISTVAL(pkbuf, res, j, pksiz); int vsiz; if(tcmapget(uset, pkbuf, pksiz, &vsiz)) tcmapputkeep(nset, pkbuf, pksiz, "", 0); } tcmapdel(uset); uset = nset; tclistdel(res); } } else if(type == TDBMSDIFF){ int omax = qrys[0]->max; int oskip = qrys[0]->skip; qrys[0]->max = INT_MAX; qrys[0]->skip = 0; TCLIST *res = tctdbqrysearch(qrys[0]); qrys[0]->max = omax; qrys[0]->skip = oskip; int rnum = TCLISTNUM(res); for(int i = 0; i < rnum; i++){ const char *pkbuf; int pksiz; TCLISTVAL(pkbuf, res, i, pksiz); tcmapputkeep(uset, pkbuf, pksiz, "", 0); } tclistdel(res); for(int i = 1; i < num; i++){ TDBQRY *qry = qrys[i]; if(TCMAPRNUM(uset) < 1){ tcxstrclear(qry->hint); tcxstrprintf(qry->hint, "omitted\n"); continue; } omax = qry->max; oskip = qry->skip; qry->max = INT_MAX; qry->skip = 0; res = tctdbqrysearch(qry); qry->max = omax; qry->skip = oskip; rnum = TCLISTNUM(res); for(int j = 0; j < rnum; j++){ const char *pkbuf; int pksiz; TCLISTVAL(pkbuf, res, j, pksiz); tcmapout(uset, pkbuf, pksiz); } tclistdel(res); } } int max = qrys[0]->max; int skip = qrys[0]->skip; TCLIST *res; if(oname && type == TDBMSUNION){ int rnum = TCMAPRNUM(uset); TDBSORTREC *keys; TCMALLOC(keys, sizeof(*keys) * rnum + 1); tcmapiterinit(uset); const char *pkbuf; int pksiz; TDBSORTREC *key = keys; while((pkbuf = tcmapiternext(uset, &pksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(pkbuf, &vsiz); key->kbuf = pkbuf; key->ksiz = pksiz; if(*vbuf == '='){ key->vbuf = (char *)vbuf + 1; key->vsiz = vsiz - 1; } else { key->vbuf = NULL; key->vsiz = 0; } key++; } int (*compar)(const TDBSORTREC *a, const TDBSORTREC *b) = NULL; switch(otype){ case TDBQOSTRASC: compar = tdbcmpsortrecstrasc; break; case TDBQOSTRDESC: compar = tdbcmpsortrecstrdesc; break; case TDBQONUMASC: compar = tdbcmpsortrecnumasc; break; case TDBQONUMDESC: compar = tdbcmpsortrecnumdesc; break; } if(compar) qsort(keys, rnum, sizeof(*keys), (int (*)(const void *, const void *))compar); res = tclistnew2(tclmin(rnum, max)); for(int i = skip; max > 0 && i < rnum; i++){ key = keys + i; TCLISTPUSH(res, key->kbuf, key->ksiz); max--; } TCFREE(keys); } else { res = tclistnew2(tclmin(tcmaprnum(uset), max)); tcmapiterinit(uset); const char *pkbuf; int pksiz; while(max > 0 && (pkbuf = tcmapiternext(uset, &pksiz)) != NULL){ if(skip > 0){ skip--; } else { TCLISTPUSH(res, pkbuf, pksiz); max--; } } } tcmapdel(uset); TCXSTR *hint = tcxstrnew(); for(int i = 0; i < num; i++){ TDBQRY *qry = qrys[i]; TCLIST *lines = tcstrsplit(tctdbqryhint(qry), "\n"); int lnum = TCLISTNUM(lines); for(int j = 0; j < lnum; j++){ const char *line = TCLISTVALPTR(lines, j); if(*line != 0) tcxstrprintf(hint, "[%d] %s\n", i, line); } tclistdel(lines); tcxstrclear(qry->hint); qry->count = 0; } TCXSTRCAT(qrys[0]->hint, TCXSTRPTR(hint), TCXSTRSIZE(hint)); qrys[0]->count = TCLISTNUM(res); tcxstrdel(hint); return res; } /************************************************************************************************* * features for experts *************************************************************************************************/ /* Set the error code of a table database object. */ void tctdbsetecode(TCTDB *tdb, int ecode, const char *filename, int line, const char *func){ assert(tdb && filename && line >= 1 && func); tchdbsetecode(tdb->hdb, ecode, filename, line, func); } /* Set the file descriptor for debugging output. */ void tctdbsetdbgfd(TCTDB *tdb, int fd){ assert(tdb && fd >= 0); tchdbsetdbgfd(tdb->hdb, fd); } /* Get the file descriptor for debugging output. */ int tctdbdbgfd(TCTDB *tdb){ assert(tdb); return tchdbdbgfd(tdb->hdb); } /* Check whether mutual exclusion control is set to a table database object. */ bool tctdbhasmutex(TCTDB *tdb){ assert(tdb); return tdb->mmtx != NULL; } /* Synchronize updating contents on memory of a table database object. */ bool tctdbmemsync(TCTDB *tdb, bool phys){ assert(tdb); if(!tdb->open || !tdb->wmode){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } bool err = false; if(!tchdbmemsync(tdb->hdb, phys)) err = true; TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITTOKEN: case TDBITQGRAM: if(!tctdbidxsyncicc(tdb, idx, true)) err = true; break; } } for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: case TDBITTOKEN: case TDBITQGRAM: if(!tcbdbmemsync(idx->db, phys)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } break; } } return !err; } /* Get the number of elements of the bucket array of a table database object. */ uint64_t tctdbbnum(TCTDB *tdb){ assert(tdb); if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tchdbbnum(tdb->hdb); } /* Get the record alignment of a table database object. */ uint32_t tctdbalign(TCTDB *tdb){ assert(tdb); if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tchdbalign(tdb->hdb); } /* Get the maximum number of the free block pool of a table database object. */ uint32_t tctdbfbpmax(TCTDB *tdb){ assert(tdb); if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tchdbfbpmax(tdb->hdb); } /* Get the inode number of the database file of a table database object. */ uint64_t tctdbinode(TCTDB *tdb){ assert(tdb); if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tchdbinode(tdb->hdb); } /* Get the modification time of the database file of a table database object. */ time_t tctdbmtime(TCTDB *tdb){ assert(tdb); if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tchdbmtime(tdb->hdb); } /* Get the additional flags of a table database object. */ uint8_t tctdbflags(TCTDB *tdb){ assert(tdb); if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tchdbflags(tdb->hdb); } /* Get the options of a table database object. */ uint8_t tctdbopts(TCTDB *tdb){ assert(tdb); if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tdb->opts; } /* Get the pointer to the opaque field of a table database object. */ char *tctdbopaque(TCTDB *tdb){ assert(tdb); if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return NULL; } return tchdbopaque(tdb->hdb) + TDBOPAQUESIZ; } /* Get the number of used elements of the bucket array of a table database object. */ uint64_t tctdbbnumused(TCTDB *tdb){ assert(tdb); if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tchdbbnumused(tdb->hdb); } /* Get the number of column indices of a table database object. */ int tctdbinum(TCTDB *tdb){ assert(tdb); if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tdb->inum; } /* Get the seed of unique ID unumbers of a table database object. */ int64_t tctdbuidseed(TCTDB *tdb){ assert(tdb); if(!TDBLOCKMETHOD(tdb, false)) return -1; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return -1; } int64_t rv = tctdbgenuidimpl(tdb, 0); TDBUNLOCKMETHOD(tdb); return rv; } /* Set the parameters of the inverted cache of a table database object. */ bool tctdbsetinvcache(TCTDB *tdb, int64_t iccmax, double iccsync){ assert(tdb); if(tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } tdb->iccmax = (iccmax > 0) ? iccmax : TDBIDXICCMAX; tdb->iccsync = (iccsync > 0) ? iccsync : TDBIDXICCSYNC; return true; } /* Set the seed of unique ID unumbers of a table database object. */ bool tctdbsetuidseed(TCTDB *tdb, int64_t seed){ assert(tdb && seed >= 0); if(!TDBLOCKMETHOD(tdb, true)) return -1; if(!tdb->open || !tdb->wmode){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } tctdbgenuidimpl(tdb, -seed - 1); TDBUNLOCKMETHOD(tdb); return true; } /* Set the custom codec functions of a table database object. */ bool tctdbsetcodecfunc(TCTDB *tdb, TCCODEC enc, void *encop, TCCODEC dec, void *decop){ assert(tdb && enc && dec); if(!TDBLOCKMETHOD(tdb, true)) return false; if(tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } bool rv = tchdbsetcodecfunc(tdb->hdb, enc, encop, dec, decop); TDBUNLOCKMETHOD(tdb); return rv; } /* Get the unit step number of auto defragmentation of a table database object. */ uint32_t tctdbdfunit(TCTDB *tdb){ assert(tdb); return tchdbdfunit(tdb->hdb); } /* Perform dynamic defragmentation of a table database object. */ bool tctdbdefrag(TCTDB *tdb, int64_t step){ assert(tdb); if(!TDBLOCKMETHOD(tdb, false)) return false; if(!tdb->open || !tdb->wmode){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } bool rv = tctdbdefragimpl(tdb, step); TDBUNLOCKMETHOD(tdb); return rv; } /* Clear the cache of a table tree database object. */ bool tctdbcacheclear(TCTDB *tdb){ assert(tdb); if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } bool rv = tctdbcacheclearimpl(tdb); TDBUNLOCKMETHOD(tdb); return rv; } /* Store a record into a table database object with a duplication handler. */ bool tctdbputproc(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz, TCPDPROC proc, void *op){ assert(tdb && pkbuf && pksiz >= 0 && proc); if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open || !tdb->wmode){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } bool err = false; TCMAP *cols = tctdbgetimpl(tdb, pkbuf, pksiz); if(cols){ int zsiz; char *zbuf = tcstrjoin4(cols, &zsiz); int ncsiz; void *ncbuf = proc(zbuf, zsiz, &ncsiz, op); if(ncbuf == (void *)-1){ if(!tctdboutimpl(tdb, pkbuf, pksiz)) err = true; } else if(ncbuf){ TCMAP *ncols = tcstrsplit4(ncbuf, ncsiz); if(!tctdbputimpl(tdb, pkbuf, pksiz, ncols, TDBPDOVER)) err = true; tcmapdel(ncols); TCFREE(ncbuf); } else { tctdbsetecode(tdb, TCEKEEP, __FILE__, __LINE__, __func__); err = true; } TCFREE(zbuf); tcmapdel(cols); } else { if(cbuf){ cols = tcstrsplit4(cbuf, csiz); if(!tctdbputimpl(tdb, pkbuf, pksiz, cols, TDBPDOVER)) err = true; tcmapdel(cols); } else { tctdbsetecode(tdb, TCENOREC, __FILE__, __LINE__, __func__); err = true; } } TDBUNLOCKMETHOD(tdb); return !err; } /* Retrieve the value of a column of a record in a table database object. */ char *tctdbget4(TCTDB *tdb, const void *pkbuf, int pksiz, const void *nbuf, int nsiz, int *sp){ assert(tdb && pkbuf && pksiz >= 0 && nbuf && nsiz >= 0 && sp); if(!TDBLOCKMETHOD(tdb, false)) return NULL; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return NULL; } char *rv = tctdbgetonecol(tdb, pkbuf, pksiz, nbuf, nsiz, sp); TDBUNLOCKMETHOD(tdb); return rv; } /* Move the iterator to the record corresponding a key of a table database object. */ bool tctdbiterinit2(TCTDB *tdb, const void *pkbuf, int pksiz){ assert(tdb && pkbuf && pksiz >= 0); if(!TDBLOCKMETHOD(tdb, true)) return false; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } bool rv = tchdbiterinit2(tdb->hdb, pkbuf, pksiz); TDBUNLOCKMETHOD(tdb); return rv; } /* Move the iterator to the record corresponding a key string of a table database object. */ bool tctdbiterinit3(TCTDB *tdb, const char *kstr){ assert(tdb && kstr); return tctdbiterinit2(tdb, kstr, strlen(kstr)); } /* Process each record atomically of a table database object. */ bool tctdbforeach(TCTDB *tdb, TCITER iter, void *op){ assert(tdb && iter); if(!TDBLOCKMETHOD(tdb, false)) return false; if(!tdb->open){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); return false; } TDBTHREADYIELD(tdb); bool rv = tctdbforeachimpl(tdb, iter, op); TDBUNLOCKMETHOD(tdb); return rv; } /* Process each record corresponding to a query object with non-atomic fashion. */ bool tctdbqryproc2(TDBQRY *qry, TDBQRYPROC proc, void *op){ assert(qry && proc); TCTDB *tdb = qry->tdb; TDBCOND *conds = qry->conds; int cnum = qry->cnum; bool err = false; int64_t getnum = 0; int64_t putnum = 0; int64_t outnum = 0; TCLIST *res = tctdbqrysearch(qry); int rnum = TCLISTNUM(res); for(int i = 0; i < rnum; i++){ if(!TDBLOCKMETHOD(tdb, true)){ err = true; break; } if(!tdb->open || !tdb->wmode){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); TDBUNLOCKMETHOD(tdb); err = true; break; } int pksiz; const char *pkbuf; TCLISTVAL(pkbuf, res, i, pksiz); TCMAP *cols = tctdbgetimpl(tdb, pkbuf, pksiz); if(cols){ getnum++; bool ok = true; for(int j = 0; j < cnum; j++){ TDBCOND *cond = conds + j; if(cond->nsiz < 1){ if(tctdbqrycondmatch(cond, pkbuf, pksiz) != cond->sign){ ok = false; break; } } else { int vsiz; const char *vbuf = tcmapget(cols, cond->name, cond->nsiz, &vsiz); if(vbuf){ if(tctdbqrycondmatch(cond, vbuf, vsiz) != cond->sign){ ok = false; break; } } else { if(cond->sign){ ok = false; break; } } } } if(ok){ int flags = proc(pkbuf, pksiz, cols, op); if(flags & TDBQPPUT){ if(tctdbputimpl(tdb, pkbuf, pksiz, cols, TDBPDOVER)){ putnum++; } else { err = true; } } else if(flags & TDBQPOUT){ if(tctdboutimpl(tdb, pkbuf, pksiz)){ outnum++; } else { err = true; } } if(flags & TDBQPSTOP) i = rnum; } tcmapdel(cols); } TDBUNLOCKMETHOD(tdb); } tclistdel(res); tcxstrprintf(qry->hint, "post treatment: get=%lld, put=%lld, out=%lld\n", (long long)getnum, (long long)putnum, (long long)outnum); return !err; } /* Remove each record corresponding to a query object with non-atomic fashion. */ bool tctdbqrysearchout2(TDBQRY *qry){ assert(qry); return tctdbqryproc2(qry, tctdbqryprocoutcb, NULL); } /* Convert a string into the index type number. */ int tctdbstrtoindextype(const char *str){ assert(str); int type = -1; int flags = 0; if(*str == '+'){ flags |= TDBITKEEP; str++; } if(!tcstricmp(str, "LEX") || !tcstricmp(str, "LEXICAL") || !tcstricmp(str, "STR")){ type = TDBITLEXICAL; } else if(!tcstricmp(str, "DEC") || !tcstricmp(str, "DECIMAL") || !tcstricmp(str, "NUM")){ type = TDBITDECIMAL; } else if(!tcstricmp(str, "TOK") || !tcstricmp(str, "TOKEN")){ type = TDBITTOKEN; } else if(!tcstricmp(str, "QGR") || !tcstricmp(str, "QGRAM") || !tcstricmp(str, "FTS")){ type = TDBITQGRAM; } else if(!tcstricmp(str, "OPT") || !tcstricmp(str, "OPTIMIZE")){ type = TDBITOPT; } else if(!tcstricmp(str, "VOID") || !tcstricmp(str, "NULL")){ type = TDBITVOID; } else if(tcstrisnum(str)){ type = tcatoi(str); } return type | flags; } /* Convert a string into the meta search type number. */ int tctdbstrtometasearcytype(const char *str){ assert(str); int type = -1; if(!tcstricmp(str, "UNION") || !tcstricmp(str, "OR")){ type = TDBMSUNION; } else if(!tcstricmp(str, "ISECT") || !tcstricmp(str, "INTERSECTION") || !tcstricmp(str, "AND")){ type = TDBMSISECT; } else if(!tcstricmp(str, "DIFF") || !tcstricmp(str, "DIFFERENCE") || !tcstricmp(str, "ANDNOT") || !tcstricmp(str, "NOT")){ type = TDBMSDIFF; } else if(tcstrisnum(str)){ type = tcatoi(str); } return type; } /* Get the count of corresponding records of a query object. */ int tctdbqrycount(TDBQRY *qry){ assert(qry); return qry->count; } /* Generate keyword-in-context strings from a query object. */ TCLIST *tctdbqrykwic(TDBQRY *qry, TCMAP *cols, const char *name, int width, int opts){ assert(qry && cols && width >= 0); TDBCOND *conds = qry->conds; int cnum = qry->cnum; TDBCOND *cond = NULL; if(name){ for(int i = 0; i < cnum; i++){ if(!strcmp(conds[i].name, name)){ cond = conds + i; break; } } } else if(cnum > 0){ cond = conds; name = cond->name; } if(!cond) return tclistnew2(1); const char *str = tcmapget2(cols, name); if(!str) return tclistnew2(1); TCLIST *words; if(cond->op == TDBQCSTRAND || cond->op == TDBQCSTROR || cond->op == TDBQCSTROREQ || cond->op == TDBQCNUMOREQ){ words = tcstrsplit(cond->expr, " ,"); } else if(cond->op == TDBQCFTSPH){ TDBFTSUNIT *ftsunits = cond->ftsunits; int ftsnum = cond->ftsnum; if(ftsnum > 0){ words = tclistnew2(ftsnum * 2 + 1); for(int i = 0; i < ftsnum; i++){ if(!ftsunits[i].sign) continue; TCLIST *tokens = ftsunits[i].tokens; int tnum = TCLISTNUM(tokens); for(int j = 0; j < tnum; j++){ const char *token; int tsiz; TCLISTVAL(token, tokens, j, tsiz); TCLISTPUSH(words, token, tsiz); } } } else { words = tclistnew2(1); } } else { words = tclistnew3(cond->expr, NULL); } TCLIST *texts = tcstrkwic(str, words, width, opts); tclistdel(words); return texts; } /* Convert a string into the query operation number. */ int tctdbqrystrtocondop(const char *str){ assert(str); int op = -1; int flags = 0; if(*str == '~' || *str == '!'){ flags |= TDBQCNEGATE; str++; } if(*str == '+'){ flags |= TDBQCNOIDX; str++; } if(!tcstricmp(str, "STREQ") || !tcstricmp(str, "STR") || !tcstricmp(str, "EQ")){ op = TDBQCSTREQ; } else if(!tcstricmp(str, "STRINC") || !tcstricmp(str, "INC")){ op = TDBQCSTRINC; } else if(!tcstricmp(str, "STRBW") || !tcstricmp(str, "BW")){ op = TDBQCSTRBW; } else if(!tcstricmp(str, "STREW") || !tcstricmp(str, "EW")){ op = TDBQCSTREW; } else if(!tcstricmp(str, "STRAND") || !tcstricmp(str, "AND")){ op = TDBQCSTRAND; } else if(!tcstricmp(str, "STROR") || !tcstricmp(str, "OR")){ op = TDBQCSTROR; } else if(!tcstricmp(str, "STROREQ") || !tcstricmp(str, "OREQ")){ op = TDBQCSTROREQ; } else if(!tcstricmp(str, "STRRX") || !tcstricmp(str, "RX")){ op = TDBQCSTRRX; } else if(!tcstricmp(str, "NUMEQ") || !tcstricmp(str, "NUM") || !tcstricmp(str, "=") || !tcstricmp(str, "==")){ op = TDBQCNUMEQ; } else if(!tcstricmp(str, "NUMGT") || !tcstricmp(str, ">")){ op = TDBQCNUMGT; } else if(!tcstricmp(str, "NUMGE") || !tcstricmp(str, ">=")){ op = TDBQCNUMGE; } else if(!tcstricmp(str, "NUMLT") || !tcstricmp(str, "<")){ op = TDBQCNUMLT; } else if(!tcstricmp(str, "NUMLE") || !tcstricmp(str, "<=")){ op = TDBQCNUMLE; } else if(!tcstricmp(str, "NUMBT")){ op = TDBQCNUMBT; } else if(!tcstricmp(str, "NUMOREQ")){ op = TDBQCNUMOREQ; } else if(!tcstricmp(str, "FTSPH") || !tcstricmp(str, "FTS")){ op = TDBQCFTSPH; } else if(!tcstricmp(str, "FTSAND")){ op = TDBQCFTSAND; } else if(!tcstricmp(str, "FTSOR")){ op = TDBQCFTSOR; } else if(!tcstricmp(str, "FTSEX")){ op = TDBQCFTSEX; } else if(tcstrisnum(str)){ op = tcatoi(str); } return op | flags; } /* Convert a string into the query order type number. */ int tctdbqrystrtoordertype(const char *str){ assert(str); int type = -1; if(!tcstricmp(str, "STRASC") || !tcstricmp(str, "STR") || !tcstricmp(str, "ASC")){ type = TDBQOSTRASC; } else if(!tcstricmp(str, "STRDESC") || !tcstricmp(str, "DESC")){ type = TDBQOSTRDESC; } else if(!tcstricmp(str, "NUMASC") || !tcstricmp(str, "NUM")){ type = TDBQONUMASC; } else if(!tcstricmp(str, "NUMDESC")){ type = TDBQONUMDESC; } else if(tcstrisnum(str)){ type = tcatoi(str); } return type; } /* Convert a string into the set operation type number. */ int tctdbmetastrtosettype(const char *str){ assert(str); int type = -1; if(!tcstricmp(str, "UNION") || !tcstricmp(str, "CUP") || !tcstricmp(str, "+")){ type = TDBMSUNION; } else if(!tcstricmp(str, "ISECT") || !tcstricmp(str, "INTERSECTION") || !tcstricmp(str, "CAP") || !tcstricmp(str, "*")){ type = TDBMSISECT; } else if(!tcstricmp(str, "DIFF") || !tcstricmp(str, "DIFFERENCE") || !tcstricmp(str, "MINUS") || !tcstricmp(str, "-")){ type = TDBMSDIFF; } else if(tcstrisnum(str)){ type = tcatoi(str); } return type; } /************************************************************************************************* * private features *************************************************************************************************/ /* Clear all members. `tdb' specifies the table database object. */ static void tctdbclear(TCTDB *tdb){ assert(tdb); tdb->mmtx = NULL; tdb->hdb = NULL; tdb->open = false; tdb->wmode = false; tdb->opts = 0; tdb->lcnum = TDBDEFLCNUM; tdb->ncnum = TDBDEFNCNUM; tdb->iccmax = TDBIDXICCMAX; tdb->iccsync = TDBIDXICCSYNC; tdb->idxs = NULL; tdb->inum = 0; tdb->tran = false; } /* Open a database file and connect a table database object. `tdb' specifies the table database object. `path' specifies the path of the internal database file. `omode' specifies the connection mode. If successful, the return value is true, else, it is false. */ static bool tctdbopenimpl(TCTDB *tdb, const char *path, int omode){ assert(tdb && path); int dbgfd = tchdbdbgfd(tdb->hdb); TCCODEC enc, dec; void *encop, *decop; tchdbcodecfunc(tdb->hdb, &enc, &encop, &dec, &decop); int homode = HDBOREADER; int bomode = BDBOREADER; if(omode & TDBOWRITER){ homode = HDBOWRITER; bomode = BDBOWRITER; if(omode & TDBOCREAT){ homode |= HDBOCREAT; bomode |= BDBOCREAT; } if(omode & TDBOTRUNC){ homode |= HDBOTRUNC; bomode |= BDBOTRUNC; } tdb->wmode = true; } else { tdb->wmode = false; } if(omode & TDBONOLCK){ homode |= HDBONOLCK; bomode |= BDBONOLCK; } if(omode & TDBOLCKNB){ homode |= HDBOLCKNB; bomode |= BDBOLCKNB; } if(omode & TDBOTSYNC){ homode |= HDBOTSYNC; bomode |= BDBOTSYNC; } tchdbsettype(tdb->hdb, TCDBTTABLE); if(!tchdbopen(tdb->hdb, path, homode)) return false; char *tpath = tcsprintf("%s%c%s%c*", path, MYEXTCHR, TDBIDXSUFFIX, MYEXTCHR); if((omode & TDBOWRITER) && (omode & TDBOTRUNC)){ TCLIST *paths = tcglobpat(tpath); int pnum = TCLISTNUM(paths); for(int i = 0; i < pnum; i++){ unlink(TCLISTVALPTR(paths, i)); } tclistdel(paths); } TCLIST *paths = tcglobpat(tpath); int pnum = TCLISTNUM(paths); TCMALLOC(tdb->idxs, sizeof(tdb->idxs[0]) * pnum + 1); TDBIDX *idxs = tdb->idxs; int inum = 0; for(int i = 0; i < pnum; i++){ const char *ipath = TCLISTVALPTR(paths, i); if(!tcstrfwm(ipath, path)) continue; const char *rp = ipath + strlen(path); if(*rp != MYEXTCHR) continue; rp++; if(!tcstrfwm(rp, TDBIDXSUFFIX)) continue; rp += strlen(TDBIDXSUFFIX); if(*rp != MYEXTCHR) continue; rp++; char *stem = tcstrdup(rp); char *ep = strrchr(stem, MYEXTCHR); if(!ep) continue; *(ep++) = '\0'; int nsiz; char *name = tcurldecode(stem, &nsiz); if(!strcmp(ep, "lex") || !strcmp(ep, "dec") || !strcmp(ep, "tok") || !strcmp(ep, "qgr")){ TCBDB *bdb = tcbdbnew(); if(dbgfd >= 0) tcbdbsetdbgfd(bdb, dbgfd); if(tdb->mmtx) tcbdbsetmutex(bdb); if(enc && dec) tcbdbsetcodecfunc(bdb, enc, encop, dec, decop); tcbdbsetcache(bdb, tdb->lcnum, tdb->ncnum); tcbdbsetxmsiz(bdb, tchdbxmsiz(tdb->hdb)); tcbdbsetdfunit(bdb, tchdbdfunit(tdb->hdb)); tcbdbsetlsmax(bdb, TDBIDXLSMAX); if(tcbdbopen(bdb, ipath, bomode)){ idxs[inum].name = tcstrdup(name); idxs[inum].type = TDBITLEXICAL; if(!strcmp(ep, "dec")){ idxs[inum].type = TDBITDECIMAL; } else if(!strcmp(ep, "tok")){ idxs[inum].type = TDBITTOKEN; } else if(!strcmp(ep, "qgr")){ idxs[inum].type = TDBITQGRAM; } idxs[inum].db = bdb; idxs[inum].cc = NULL; if(idxs[inum].type == TDBITTOKEN){ idxs[inum].cc = tcmapnew2(TDBIDXICCBNUM); } else if(idxs[inum].type == TDBITQGRAM){ idxs[inum].cc = tcmapnew2(TDBIDXICCBNUM); } inum++; } else { tcbdbdel(bdb); } } TCFREE(name); TCFREE(stem); } tclistdel(paths); TCFREE(tpath); tdb->inum = inum; tdb->open = true; uint8_t hopts = tchdbopts(tdb->hdb); uint8_t opts = 0; if(hopts & HDBTLARGE) opts |= TDBTLARGE; if(hopts & HDBTDEFLATE) opts |= TDBTDEFLATE; if(hopts & HDBTBZIP) opts |= TDBTBZIP; if(hopts & HDBTTCBS) opts |= TDBTTCBS; if(hopts & HDBTEXCODEC) opts |= TDBTEXCODEC; tdb->opts = opts; tdb->tran = false; return true; } /* Close a table database object. `tdb' specifies the table database object. If successful, the return value is true, else, it is false. */ static bool tctdbcloseimpl(TCTDB *tdb){ assert(tdb); bool err = false; if(tdb->tran && !tctdbtranabortimpl(tdb)) err = true; TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITTOKEN: case TDBITQGRAM: if(!tctdbidxsyncicc(tdb, idx, true)) err = true; tcmapdel(idx->cc); break; } } for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: case TDBITTOKEN: case TDBITQGRAM: if(!tcbdbclose(idx->db)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } tcbdbdel(idx->db); break; } TCFREE(idx->name); } TCFREE(idxs); if(!tchdbclose(tdb->hdb)) err = true; tdb->open = false; return !err; } /* Store a record into a table database object. `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. `cols' specifies a map object containing columns. `dmode' specifies behavior when the key overlaps. If successful, the return value is true, else, it is false. */ static bool tctdbputimpl(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols, int dmode){ assert(tdb && pkbuf && pksiz >= 0 && cols); bool err = false; int osiz; char *obuf = tchdbget(tdb->hdb, pkbuf, pksiz, &osiz); if(obuf){ if(dmode == TDBPDKEEP){ tctdbsetecode(tdb, TCEKEEP, __FILE__, __LINE__, __func__); TCFREE(obuf); return false; } TCMAP *ocols = tcmapload(obuf, osiz); if(dmode == TDBPDCAT){ TCMAP *ncols = tcmapnew2(TCMAPRNUM(cols) + 1); tcmapiterinit(cols); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(cols, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); if(tcmapputkeep(ocols, kbuf, ksiz, vbuf, vsiz)) tcmapput(ncols, kbuf, ksiz, vbuf, vsiz); } if(!tctdbidxput(tdb, pkbuf, pksiz, ncols)) err = true; tcmapdel(ncols); int csiz; char *cbuf = tcmapdump(ocols, &csiz); if(!tchdbput(tdb->hdb, pkbuf, pksiz, cbuf, csiz)) err = true; TCFREE(cbuf); } else { TCMAP *ncols = tcmapnew2(TCMAPRNUM(cols) + 1); tcmapiterinit(cols); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(cols, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); int osiz; const char *obuf = tcmapget(ocols, kbuf, ksiz, &osiz); if(obuf && osiz == vsiz && !memcmp(obuf, vbuf, osiz)){ tcmapout(ocols, kbuf, ksiz); } else { tcmapput(ncols, kbuf, ksiz, vbuf, vsiz); } } if(!tctdbidxout(tdb, pkbuf, pksiz, ocols)) err = true; if(!tctdbidxput(tdb, pkbuf, pksiz, ncols)) err = true; tcmapdel(ncols); int csiz; char *cbuf = tcmapdump(cols, &csiz); if(!tchdbput(tdb->hdb, pkbuf, pksiz, cbuf, csiz)) err = true; TCFREE(cbuf); } tcmapdel(ocols); TCFREE(obuf); } else { if(!tctdbidxput(tdb, pkbuf, pksiz, cols)) err = true; int csiz; char *cbuf = tcmapdump(cols, &csiz); if(!tchdbput(tdb->hdb, pkbuf, pksiz, cbuf, csiz)) err = true; TCFREE(cbuf); } return !err; } /* Remove a record of a table database object. `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 true, else, it is false. */ static bool tctdboutimpl(TCTDB *tdb, const char *pkbuf, int pksiz){ assert(tdb && pkbuf && pksiz >= 0); int csiz; char *cbuf = tchdbget(tdb->hdb, pkbuf, pksiz, &csiz); if(!cbuf) return false; bool err = false; TCMAP *cols = tcmapload(cbuf, csiz); if(!tctdbidxout(tdb, pkbuf, pksiz, cols)) err = true; if(!tchdbout(tdb->hdb, pkbuf, pksiz)) err = true; tcmapdel(cols); TCFREE(cbuf); return !err; } /* Retrieve a record in a table database object. `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. */ static TCMAP *tctdbgetimpl(TCTDB *tdb, const void *pkbuf, int pksiz){ assert(tdb && pkbuf && pksiz >= 0); int csiz; char *cbuf = tchdbget(tdb->hdb, pkbuf, pksiz, &csiz); if(!cbuf) return NULL; TCMAP *cols = tcmapload(cbuf, csiz); TCFREE(cbuf); return cols; } /* Retrieve the value of a column of a record in a table database object. `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. `nbuf' specifies the pointer to the region of the column name. `nsiz' specifies the size of the region of the column name. `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 column of the corresponding record. */ static char *tctdbgetonecol(TCTDB *tdb, const void *pkbuf, int pksiz, const void *nbuf, int nsiz, int *sp){ assert(tdb && pkbuf && pksiz >= 0 && nbuf && nsiz >= 0 && sp); int csiz; char *cbuf = tchdbget(tdb->hdb, pkbuf, pksiz, &csiz); if(!cbuf) return NULL; void *rv = tcmaploadone(cbuf, csiz, nbuf, nsiz, sp); TCFREE(cbuf); return rv; } /* Add a real number to a column of a record in a table database object. `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. `num' specifies the additional value. If successful, the return value is the summation value, else, it is Not-a-Number. */ static double tctdbaddnumber(TCTDB *tdb, const void *pkbuf, int pksiz, double num){ assert(tdb && pkbuf && pksiz >= 0); int csiz; char *cbuf = tchdbget(tdb->hdb, pkbuf, pksiz, &csiz); TCMAP *cols = cbuf ? tcmapload(cbuf, csiz) : tcmapnew2(1); if(cbuf){ const char *vbuf = tcmapget2(cols, TDBNUMCNTCOL); if(vbuf) num += tctdbatof(vbuf); TCFREE(cbuf); } char numbuf[TDBCOLBUFSIZ]; int len = snprintf(numbuf, TDBCOLBUFSIZ - 1, "%f", num); if(len > TDBCOLBUFSIZ - 1){ tctdbsetecode(tdb, TCEMISC, __FILE__, __LINE__, __func__); num = nan(""); } else { while(--len > 0){ if(numbuf[len] != '0') break; numbuf[len] = '\0'; } if(numbuf[len] == '.') numbuf[len] = '\0'; tcmapput2(cols, TDBNUMCNTCOL, numbuf); if(!tctdbputimpl(tdb, pkbuf, pksiz, cols, TDBPDOVER)) num = nan(""); } tcmapdel(cols); return num; } /* Optimize the file of a table database object. `tdb' specifies the table database object. `bnum' specifies the number of elements of the bucket array. `apow' specifies the size of record alignment by power of 2. `fpow' specifies the maximum number of elements of the free block pool by power of 2. `opts' specifies options by bitwise-or. If successful, the return value is true, else, it is false. */ static bool tctdboptimizeimpl(TCTDB *tdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){ assert(tdb); bool err = false; TCHDB *hdb = tdb->hdb; TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITTOKEN: case TDBITQGRAM: tcmapclear(idx->cc); break; } } for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: case TDBITTOKEN: case TDBITQGRAM: if(!tcbdbvanish(idx->db)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } break; } } const char *path = tchdbpath(tdb->hdb); char *tpath = tcsprintf("%s%ctmp%c%llu", path, MYEXTCHR, MYEXTCHR, tchdbinode(tdb->hdb)); TCHDB *thdb = tchdbnew(); tchdbsettype(thdb, TCDBTTABLE); int dbgfd = tchdbdbgfd(tdb->hdb); if(dbgfd >= 0) tchdbsetdbgfd(thdb, dbgfd); TCCODEC enc, dec; void *encop, *decop; tchdbcodecfunc(hdb, &enc, &encop, &dec, &decop); if(enc && dec) tchdbsetcodecfunc(thdb, enc, encop, dec, decop); if(bnum < 1) bnum = tchdbrnum(hdb) * 2 + 1; if(apow < 0) apow = tclog2l(tchdbalign(hdb)); if(fpow < 0) fpow = tclog2l(tchdbfbpmax(hdb)); if(opts == UINT8_MAX) opts = tdb->opts; uint8_t hopts = 0; if(opts & TDBTLARGE) hopts |= HDBTLARGE; if(opts & TDBTDEFLATE) hopts |= HDBTDEFLATE; if(opts & TDBTBZIP) hopts |= HDBTBZIP; if(opts & TDBTTCBS) hopts |= HDBTTCBS; if(opts & TDBTEXCODEC) hopts |= HDBTEXCODEC; tchdbtune(thdb, bnum, apow, fpow, hopts); if(tchdbopen(thdb, tpath, HDBOWRITER | HDBOCREAT | HDBOTRUNC)){ memcpy(tchdbopaque(thdb), tchdbopaque(hdb), TDBOPAQUESIZ + TDBLEFTOPQSIZ); if(!tchdbiterinit(hdb)) err = true; TCXSTR *kxstr = tcxstrnew(); TCXSTR *vxstr = tcxstrnew(); while(tchdbiternext3(hdb, kxstr, vxstr)){ TCMAP *cols = tcmapload(TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr)); if(!tctdbidxput(tdb, TCXSTRPTR(kxstr), TCXSTRSIZE(kxstr), cols)) err = true; tcmapdel(cols); if(!tchdbput(thdb, TCXSTRPTR(kxstr), TCXSTRSIZE(kxstr), TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr))){ tctdbsetecode(tdb, tchdbecode(thdb), __FILE__, __LINE__, __func__); err = true; } } tcxstrdel(vxstr); tcxstrdel(kxstr); if(!tchdbclose(thdb)){ tctdbsetecode(tdb, tchdbecode(thdb), __FILE__, __LINE__, __func__); err = true; } if(!err){ if(unlink(path) == -1){ tctdbsetecode(tdb, TCEUNLINK, __FILE__, __LINE__, __func__); err = true; } if(rename(tpath, path) == -1){ tctdbsetecode(tdb, TCERENAME, __FILE__, __LINE__, __func__); err = true; } char *npath = tcstrdup(path); int omode = (tchdbomode(hdb) & ~HDBOCREAT) & ~HDBOTRUNC; if(!tchdbclose(hdb)) err = true; if(!tchdbopen(hdb, npath, omode)) err = true; TCFREE(npath); } } else { tctdbsetecode(tdb, tchdbecode(thdb), __FILE__, __LINE__, __func__); err = true; } tchdbdel(thdb); TCFREE(tpath); for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITTOKEN: case TDBITQGRAM: if(!tctdbidxsyncicc(tdb, idx, true)) err = true; break; } } for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: case TDBITTOKEN: case TDBITQGRAM: if(!tcbdboptimize(idx->db, -1, -1, -1, -1, -1, UINT8_MAX)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } break; } } return !err; } /* Remove all records of a table database object. `tdb' specifies the table database object. If successful, the return value is true, else, it is false. */ static bool tctdbvanishimpl(TCTDB *tdb){ assert(tdb); bool err = false; if(!tchdbvanish(tdb->hdb)) err = true; TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITTOKEN: case TDBITQGRAM: tcmapclear(idx->cc); break; } } for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: case TDBITTOKEN: case TDBITQGRAM: if(!tcbdbvanish(idx->db)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } break; } } return !err; } /* Copy the database file of a table database object. `tdb' specifies the table database object. `path' specifies the path of the destination file. If successful, the return value is true, else, it is false. */ static bool tctdbcopyimpl(TCTDB *tdb, const char *path){ assert(tdb); bool err = false; if(!tchdbcopy(tdb->hdb, path)) err = true; const char *opath = tchdbpath(tdb->hdb); TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITTOKEN: case TDBITQGRAM: if(!tctdbidxsyncicc(tdb, idx, true)) err = true; break; } } for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; const char *ipath; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: case TDBITTOKEN: case TDBITQGRAM: if(*path == '@'){ if(!tcbdbcopy(idx->db, path)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } } else { ipath = tcbdbpath(idx->db); if(tcstrfwm(ipath, opath)){ char *tpath = tcsprintf("%s%s", path, ipath + strlen(opath)); if(!tcbdbcopy(idx->db, tpath)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } TCFREE(tpath); } else { tctdbsetecode(tdb, TCEMISC, __FILE__, __LINE__, __func__); err = true; } } break; } } return !err; } /* Begin the transaction of a table database object. `tdb' specifies the table database object. If successful, the return value is true, else, it is false. */ static bool tctdbtranbeginimpl(TCTDB *tdb){ assert(tdb); if(!tctdbmemsync(tdb, false)) return false; if(!tchdbtranbegin(tdb->hdb)) return false; bool err = false; TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITTOKEN: case TDBITQGRAM: if(!tctdbidxsyncicc(tdb, idx, true)) err = true; break; } } for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: case TDBITTOKEN: case TDBITQGRAM: if(!tcbdbtranbegin(idx->db)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } break; } } return !err; } /* Commit the transaction of a table database object. `tdb' specifies the table database object. If successful, the return value is true, else, it is false. */ static bool tctdbtrancommitimpl(TCTDB *tdb){ assert(tdb); bool err = false; if(!tctdbmemsync(tdb, false)) err = true; if(!tchdbtrancommit(tdb->hdb)) err = true; TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITTOKEN: case TDBITQGRAM: if(!tctdbidxsyncicc(tdb, idx, true)) err = true; break; } } for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: case TDBITTOKEN: case TDBITQGRAM: if(!tcbdbtrancommit(idx->db)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } break; } } return !err; } /* Abort the transaction of a table database object. `tdb' specifies the table database object. If successful, the return value is true, else, it is false. */ static bool tctdbtranabortimpl(TCTDB *tdb){ assert(tdb); bool err = false; if(!tchdbtranabort(tdb->hdb)) err = true; TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITTOKEN: case TDBITQGRAM: tcmapclear(idx->cc); break; } } for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: case TDBITTOKEN: case TDBITQGRAM: if(!tcbdbtranabort(idx->db)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } break; } } return !err; } /* Set a column index to a table database object. `tdb' specifies the table database object. connected as a writer. `name' specifies the name of a column. `type' specifies the index type. If successful, the return value is true, else, it is false. */ static bool tctdbsetindeximpl(TCTDB *tdb, const char *name, int type){ assert(tdb && name); bool err = false; bool keep = false; if(type & TDBITKEEP){ type &= ~TDBITKEEP; keep = true; } bool done = false; TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; const char *path; if(!strcmp(idx->name, name)){ if(keep){ tctdbsetecode(tdb, TCEKEEP, __FILE__, __LINE__, __func__); return false; } if(type == TDBITOPT){ switch(idx->type){ case TDBITTOKEN: case TDBITQGRAM: if(!tctdbidxsyncicc(tdb, idx, true)) err = true; break; } switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: case TDBITTOKEN: case TDBITQGRAM: if(!tcbdboptimize(idx->db, -1, -1, -1, -1, -1, UINT8_MAX)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } break; } done = true; break; } switch(idx->type){ case TDBITTOKEN: case TDBITQGRAM: tcmapdel(idx->cc); break; } switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: case TDBITTOKEN: case TDBITQGRAM: path = tcbdbpath(idx->db); if(path && unlink(path)){ tctdbsetecode(tdb, TCEUNLINK, __FILE__, __LINE__, __func__); err = true; } tcbdbdel(idx->db); break; } TCFREE(idx->name); tdb->inum--; inum = tdb->inum; memmove(idxs + i, idxs + i + 1, sizeof(*idxs) * (inum - i)); done = true; break; } } if(type == TDBITOPT || type == TDBITVOID){ if(!done){ tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); err = true; } return !err; } TCXSTR *pbuf = tcxstrnew(); tcxstrprintf(pbuf, "%s%c%s%c%?", tchdbpath(tdb->hdb), MYEXTCHR, TDBIDXSUFFIX, MYEXTCHR, name); TCREALLOC(tdb->idxs, tdb->idxs, sizeof(tdb->idxs[0]) * (inum + 1)); TDBIDX *idx = tdb->idxs + inum; int homode = tchdbomode(tdb->hdb); int bomode = BDBOWRITER | BDBOCREAT | BDBOTRUNC; if(homode & HDBONOLCK) bomode |= BDBONOLCK; if(homode & HDBOLCKNB) bomode |= BDBOLCKNB; if(homode & HDBOTSYNC) bomode |= BDBOTSYNC; int dbgfd = tchdbdbgfd(tdb->hdb); TCCODEC enc, dec; void *encop, *decop; tchdbcodecfunc(tdb->hdb, &enc, &encop, &dec, &decop); int64_t bbnum = (tchdbbnum(tdb->hdb) / TDBIDXLMEMB) * 4 + TDBIDXLMEMB; int64_t bxmsiz = tchdbxmsiz(tdb->hdb); uint8_t opts = tdb->opts; uint8_t bopts = 0; if(opts & TDBTLARGE) bopts |= BDBTLARGE; if(opts & TDBTDEFLATE) bopts |= BDBTDEFLATE; if(opts & TDBTBZIP) bopts |= BDBTBZIP; if(opts & TDBTTCBS) bopts |= BDBTTCBS; if(opts & TDBTEXCODEC) bopts |= BDBTEXCODEC; switch(type){ case TDBITLEXICAL: idx->db = tcbdbnew(); idx->name = tcstrdup(name); tcxstrprintf(pbuf, "%clex", MYEXTCHR); if(dbgfd >= 0) tcbdbsetdbgfd(idx->db, dbgfd); if(tdb->mmtx) tcbdbsetmutex(idx->db); if(enc && dec) tcbdbsetcodecfunc(idx->db, enc, encop, dec, decop); tcbdbtune(idx->db, TDBIDXLMEMB, TDBIDXNMEMB, bbnum, -1, -1, bopts); tcbdbsetcache(idx->db, tdb->lcnum, tdb->ncnum); tcbdbsetxmsiz(idx->db, bxmsiz); tcbdbsetdfunit(idx->db, tchdbdfunit(tdb->hdb)); tcbdbsetlsmax(idx->db, TDBIDXLSMAX); if(!tcbdbopen(idx->db, TCXSTRPTR(pbuf), bomode)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } tdb->inum++; break; case TDBITDECIMAL: idx->db = tcbdbnew(); idx->name = tcstrdup(name); tcxstrprintf(pbuf, "%cdec", MYEXTCHR); if(dbgfd >= 0) tcbdbsetdbgfd(idx->db, dbgfd); if(tdb->mmtx) tcbdbsetmutex(idx->db); tcbdbsetcmpfunc(idx->db, tccmpdecimal, NULL); if(enc && dec) tcbdbsetcodecfunc(idx->db, enc, encop, dec, decop); tcbdbtune(idx->db, TDBIDXLMEMB, TDBIDXNMEMB, bbnum, -1, -1, bopts); tcbdbsetcache(idx->db, tdb->lcnum, tdb->ncnum); tcbdbsetxmsiz(idx->db, bxmsiz); tcbdbsetdfunit(idx->db, tchdbdfunit(tdb->hdb)); tcbdbsetlsmax(idx->db, TDBIDXLSMAX); if(!tcbdbopen(idx->db, TCXSTRPTR(pbuf), bomode)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } tdb->inum++; break; case TDBITTOKEN: idx->db = tcbdbnew(); idx->cc = tcmapnew2(TDBIDXICCBNUM); idx->name = tcstrdup(name); tcxstrprintf(pbuf, "%ctok", MYEXTCHR); if(dbgfd >= 0) tcbdbsetdbgfd(idx->db, dbgfd); if(tdb->mmtx) tcbdbsetmutex(idx->db); if(enc && dec) tcbdbsetcodecfunc(idx->db, enc, encop, dec, decop); tcbdbtune(idx->db, TDBIDXLMEMB, TDBIDXNMEMB, bbnum, -1, -1, bopts); tcbdbsetcache(idx->db, tdb->lcnum, tdb->ncnum); tcbdbsetxmsiz(idx->db, bxmsiz); tcbdbsetdfunit(idx->db, tchdbdfunit(tdb->hdb)); tcbdbsetlsmax(idx->db, TDBIDXLSMAX); if(!tcbdbopen(idx->db, TCXSTRPTR(pbuf), bomode)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } tdb->inum++; break; case TDBITQGRAM: idx->db = tcbdbnew(); idx->cc = tcmapnew2(TDBIDXICCBNUM); idx->name = tcstrdup(name); tcxstrprintf(pbuf, "%cqgr", MYEXTCHR); if(dbgfd >= 0) tcbdbsetdbgfd(idx->db, dbgfd); if(tdb->mmtx) tcbdbsetmutex(idx->db); if(enc && dec) tcbdbsetcodecfunc(idx->db, enc, encop, dec, decop); tcbdbtune(idx->db, TDBIDXLMEMB, TDBIDXNMEMB, bbnum, -1, -1, bopts); tcbdbsetcache(idx->db, tdb->lcnum, tdb->ncnum); tcbdbsetxmsiz(idx->db, bxmsiz); tcbdbsetdfunit(idx->db, tchdbdfunit(tdb->hdb)); tcbdbsetlsmax(idx->db, TDBIDXLSMAX); if(!tcbdbopen(idx->db, TCXSTRPTR(pbuf), bomode)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } tdb->inum++; break; default: tctdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__); err = true; break; } idx->type = type; if(!err){ TCHDB *hdb = tdb->hdb; if(!tchdbiterinit(hdb)) err = true; void *db = idx->db; TCXSTR *kxstr = tcxstrnew(); TCXSTR *vxstr = tcxstrnew(); int nsiz = strlen(name); while(tchdbiternext3(hdb, kxstr, vxstr)){ if(nsiz < 1){ const char *pkbuf = TCXSTRPTR(kxstr); int pksiz = TCXSTRSIZE(kxstr); switch(type){ case TDBITLEXICAL: case TDBITDECIMAL: if(!tcbdbput(db, pkbuf, pksiz, pkbuf, pksiz)){ tctdbsetecode(tdb, tcbdbecode(db), __FILE__, __LINE__, __func__); err = true; } break; case TDBITTOKEN: if(!tctdbidxputtoken(tdb, idx, pkbuf, pksiz, pkbuf, pksiz)) err = true; break; case TDBITQGRAM: if(!tctdbidxputqgram(tdb, idx, pkbuf, pksiz, pkbuf, pksiz)) err = true; break; } } else { const char *pkbuf = TCXSTRPTR(kxstr); int pksiz = TCXSTRSIZE(kxstr); uint16_t hash = tctdbidxhash(pkbuf, pksiz); int vsiz; char *vbuf = tcmaploadone(TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr), name, nsiz, &vsiz); if(vbuf){ switch(type){ case TDBITLEXICAL: case TDBITDECIMAL: if(!tctdbidxputone(tdb, idx, pkbuf, pksiz, hash, vbuf, vsiz)) err = true; break; case TDBITTOKEN: if(!tctdbidxputtoken(tdb, idx, pkbuf, pksiz, vbuf, vsiz)) err = true; break; case TDBITQGRAM: if(!tctdbidxputqgram(tdb, idx, pkbuf, pksiz, vbuf, vsiz)) err = true; break; } TCFREE(vbuf); } } } tcxstrdel(vxstr); tcxstrdel(kxstr); } tcxstrdel(pbuf); return !err; } /* Generate a unique ID number. `tdb' specifies the table database object. `inc' specifies the increment of the seed. The return value is the new unique ID number or -1 on failure. */ static int64_t tctdbgenuidimpl(TCTDB *tdb, int64_t inc){ assert(tdb); void *opq = tchdbopaque(tdb->hdb); uint64_t llnum, uid; if(inc < 0){ uid = -inc - 1; } else { memcpy(&llnum, opq, sizeof(llnum)); if(inc == 0) return TCITOHLL(llnum); uid = TCITOHLL(llnum) + inc; } llnum = TCITOHLL(uid); memcpy(opq, &llnum, sizeof(llnum)); return uid; } /* Execute the search of a query object. `qry' specifies the query object. The return value is a list object of the primary keys of the corresponding records. */ static TCLIST *tctdbqrysearchimpl(TDBQRY *qry){ assert(qry); TCTDB *tdb = qry->tdb; TCHDB *hdb = tdb->hdb; TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; TDBCOND *conds = qry->conds; int cnum = qry->cnum; int acnum = cnum; int max = qry->max; if(max < INT_MAX - qry->skip) max += qry->skip; const char *oname = qry->oname; int otype = qry->otype; TCXSTR *hint = qry->hint; TCLIST *res = NULL; for(int i = 0; i < cnum; i++){ TDBCOND *cond = conds + i; cond->alive = true; } tcxstrclear(hint); bool isord = oname != NULL; TDBCOND *mcond = NULL; TDBIDX *midx = NULL; TDBCOND *ncond = NULL; TDBIDX *nidx = NULL; TDBCOND *scond = NULL; TDBIDX *sidx = NULL; for(int i = 0; i < cnum; i++){ TDBCOND *cond = conds + i; if(!cond->sign || cond->noidx) continue; for(int j = 0; j < inum; j++){ TDBIDX *idx = idxs + j; if(!strcmp(cond->name, idx->name)){ switch(idx->type){ case TDBITLEXICAL: switch(cond->op){ case TDBQCSTREQ: case TDBQCSTRBW: case TDBQCSTROREQ: if(!mcond){ mcond = cond; midx = idx; } else if(!ncond){ ncond = cond; nidx = idx; } break; default: if(!scond){ scond = cond; sidx = idx; } break; } break; case TDBITDECIMAL: switch(cond->op){ case TDBQCNUMEQ: case TDBQCNUMGT: case TDBQCNUMGE: case TDBQCNUMLT: case TDBQCNUMLE: case TDBQCNUMBT: case TDBQCNUMOREQ: if(!mcond){ mcond = cond; midx = idx; } else if(!ncond){ ncond = cond; nidx = idx; } break; default: if(!scond){ scond = cond; sidx = idx; } break; } break; case TDBITTOKEN: switch(cond->op){ case TDBQCSTRAND: case TDBQCSTROR: if(!mcond){ mcond = cond; midx = idx; } else if(!ncond){ ncond = cond; nidx = idx; } break; } break; case TDBITQGRAM: switch(cond->op){ case TDBQCFTSPH: if(!mcond){ mcond = cond; midx = idx; } else if(!ncond){ ncond = cond; nidx = idx; } break; } break; } } } } if(mcond){ res = tclistnew(); mcond->alive = false; acnum--; TCMAP *nmap = NULL; if(ncond){ ncond->alive = false; acnum--; nmap = tctdbqryidxfetch(qry, ncond, nidx); max = tclmin(max, TCMAPRNUM(nmap)); } const char *expr = mcond->expr; int esiz = mcond->esiz; TDBCOND *ucond = NULL; for(int i = 0; i < cnum; i++){ TDBCOND *cond = conds + i; if(!cond->alive) continue; if(ucond){ ucond = NULL; break; } ucond = cond; } bool trim = *midx->name != '\0'; if(mcond->op == TDBQCSTREQ){ tcxstrprintf(hint, "using an index: \"%s\" asc (STREQ)\n", mcond->name); BDBCUR *cur = tcbdbcurnew(midx->db); tcbdbcurjump(cur, expr, esiz + trim); if(oname && !strcmp(oname, mcond->name)) oname = NULL; bool all = oname != NULL; if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); trim = *midx->name != '\0'; const char *kbuf; int ksiz; while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(trim) ksiz -= 3; if(ksiz == esiz && !memcmp(kbuf, expr, esiz)){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); int nsiz; if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){ if(acnum < 1){ TCLISTPUSH(res, vbuf, vsiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); } else if(tctdbqryallcondmatch(qry, vbuf, vsiz)){ TCLISTPUSH(res, vbuf, vsiz); } } } else { break; } tcbdbcurnext(cur); } tcbdbcurdel(cur); } else if(mcond->op == TDBQCSTRBW){ tcxstrprintf(hint, "using an index: \"%s\" asc (STRBW)\n", mcond->name); BDBCUR *cur = tcbdbcurnew(midx->db); tcbdbcurjump(cur, expr, esiz + trim); bool all = oname && (strcmp(oname, mcond->name) || otype != TDBQOSTRASC); if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); const char *kbuf; int ksiz; while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(trim) ksiz -= 3; if(ksiz >= esiz && !memcmp(kbuf, expr, esiz)){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); int nsiz; if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){ if(acnum < 1){ TCLISTPUSH(res, vbuf, vsiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); } else if(tctdbqryallcondmatch(qry, vbuf, vsiz)){ TCLISTPUSH(res, vbuf, vsiz); } } } else { break; } tcbdbcurnext(cur); } tcbdbcurdel(cur); if(oname && !strcmp(oname, mcond->name)){ if(otype == TDBQOSTRASC){ oname = NULL; } else if(otype == TDBQOSTRDESC){ tclistinvert(res); oname = NULL; } } } else if(mcond->op == TDBQCSTROREQ){ tcxstrprintf(hint, "using an index: \"%s\" skip (STROREQ)\n", mcond->name); BDBCUR *cur = tcbdbcurnew(midx->db); TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); 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(oname && !strcmp(oname, mcond->name)){ if(otype == TDBQOSTRASC){ oname = NULL; } else if(otype == TDBQOSTRDESC){ tclistinvert(tokens); oname = NULL; } } int tnum = TCLISTNUM(tokens); bool all = oname != NULL; if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); for(int i = 0; (all || TCLISTNUM(res) < max) && i < tnum; i++){ const char *token; int tsiz; TCLISTVAL(token, tokens, i, tsiz); if(tsiz < 1) continue; tcbdbcurjump(cur, token, tsiz + trim); const char *kbuf; int ksiz; while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(trim) ksiz -= 3; if(ksiz == tsiz && !memcmp(kbuf, token, tsiz)){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); int nsiz; if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){ if(acnum < 1){ TCLISTPUSH(res, vbuf, vsiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); } else if(tctdbqryallcondmatch(qry, vbuf, vsiz)){ TCLISTPUSH(res, vbuf, vsiz); } } } else { break; } tcbdbcurnext(cur); } } tclistdel(tokens); tcbdbcurdel(cur); } else if(mcond->op == TDBQCNUMEQ){ tcxstrprintf(hint, "using an index: \"%s\" asc (NUMEQ)\n", mcond->name); BDBCUR *cur = tcbdbcurnew(midx->db); if(oname && !strcmp(oname, mcond->name)) oname = NULL; long double xnum = tctdbatof(expr); tctdbqryidxcurjumpnum(cur, expr, esiz, true); bool all = oname != NULL; if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); const char *kbuf; int ksiz; while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(tctdbatof(kbuf) == xnum){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); int nsiz; if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){ if(acnum < 1){ TCLISTPUSH(res, vbuf, vsiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); } else if(tctdbqryallcondmatch(qry, vbuf, vsiz)){ TCLISTPUSH(res, vbuf, vsiz); } } } else { break; } tcbdbcurnext(cur); } tcbdbcurdel(cur); } else if(mcond->op == TDBQCNUMGT || mcond->op == TDBQCNUMGE){ if(oname && !strcmp(oname, mcond->name) && otype == TDBQONUMDESC){ tcxstrprintf(hint, "using an index: \"%s\" desc (NUMGT/NUMGE)\n", mcond->name); long double xnum = tctdbatof(expr); BDBCUR *cur = tcbdbcurnew(midx->db); tcbdbcurlast(cur); if(max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); const char *kbuf; int ksiz; while(TCLISTNUM(res) < max && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ long double knum = tctdbatof(kbuf); if(knum < xnum) break; if(knum > xnum || (knum >= xnum && mcond->op == TDBQCNUMGE)){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); int nsiz; if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){ if(acnum < 1){ TCLISTPUSH(res, vbuf, vsiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); } else if(tctdbqryallcondmatch(qry, vbuf, vsiz)){ TCLISTPUSH(res, vbuf, vsiz); } } } tcbdbcurprev(cur); } tcbdbcurdel(cur); oname = NULL; } else { tcxstrprintf(hint, "using an index: \"%s\" asc (NUMGT/NUMGE)\n", mcond->name); long double xnum = tctdbatof(expr); BDBCUR *cur = tcbdbcurnew(midx->db); tctdbqryidxcurjumpnum(cur, expr, esiz, true); bool all = oname && (strcmp(oname, mcond->name) || otype != TDBQONUMASC); if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); const char *kbuf; int ksiz; while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ long double knum = tctdbatof(kbuf); if(knum > xnum || (knum >= xnum && mcond->op == TDBQCNUMGE)){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); int nsiz; if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){ if(acnum < 1){ TCLISTPUSH(res, vbuf, vsiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); } else if(tctdbqryallcondmatch(qry, vbuf, vsiz)){ TCLISTPUSH(res, vbuf, vsiz); } } } tcbdbcurnext(cur); } tcbdbcurdel(cur); if(!all) oname = NULL; } } else if(mcond->op == TDBQCNUMLT || mcond->op == TDBQCNUMLE){ if(oname && !strcmp(oname, mcond->name) && otype == TDBQONUMASC){ tcxstrprintf(hint, "using an index: \"%s\" asc (NUMLT/NUMLE)\n", mcond->name); long double xnum = tctdbatof(expr); BDBCUR *cur = tcbdbcurnew(midx->db); tcbdbcurfirst(cur); if(max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); const char *kbuf; int ksiz; while(TCLISTNUM(res) < max && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ long double knum = tctdbatof(kbuf); if(knum > xnum) break; if(knum < xnum || (knum <= xnum && mcond->op == TDBQCNUMLE)){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); int nsiz; if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){ if(acnum < 1){ TCLISTPUSH(res, vbuf, vsiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); } else if(tctdbqryallcondmatch(qry, vbuf, vsiz)){ TCLISTPUSH(res, vbuf, vsiz); } } } tcbdbcurnext(cur); } tcbdbcurdel(cur); oname = NULL; } else { tcxstrprintf(hint, "using an index: \"%s\" desc (NUMLT/NUMLE)\n", mcond->name); long double xnum = tctdbatof(expr); BDBCUR *cur = tcbdbcurnew(midx->db); tctdbqryidxcurjumpnum(cur, expr, esiz, false); bool all = oname && (strcmp(oname, mcond->name) || otype != TDBQONUMDESC); if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); const char *kbuf; int ksiz; while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ long double knum = tctdbatof(kbuf); if(knum < xnum || (knum <= xnum && mcond->op == TDBQCNUMLE)){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); int nsiz; if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){ if(acnum < 1){ TCLISTPUSH(res, vbuf, vsiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); } else if(tctdbqryallcondmatch(qry, vbuf, vsiz)){ TCLISTPUSH(res, vbuf, vsiz); } } } tcbdbcurprev(cur); } tcbdbcurdel(cur); if(!all) oname = NULL; } } else if(mcond->op == TDBQCNUMBT){ tcxstrprintf(hint, "using an index: \"%s\" asc (NUMBT)\n", mcond->name); while(*expr == ' ' || *expr == ','){ expr++; } const char *pv = expr; while(*pv != '\0' && *pv != ' ' && *pv != ','){ pv++; } esiz = pv - expr; if(*pv != ' ' && *pv != ',') pv = " "; pv++; while(*pv == ' ' || *pv == ','){ pv++; } long double lower = tctdbatof(expr); long double upper = tctdbatof(pv); if(lower > upper){ expr = pv; esiz = strlen(expr); long double swap = lower; lower = upper; upper = swap; } BDBCUR *cur = tcbdbcurnew(midx->db); tctdbqryidxcurjumpnum(cur, expr, esiz, true); bool all = oname && (strcmp(oname, mcond->name) || otype != TDBQONUMASC); if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); const char *kbuf; int ksiz; while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(tctdbatof(kbuf) > upper) break; int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); int nsiz; if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){ if(acnum < 1){ TCLISTPUSH(res, vbuf, vsiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); } else if(tctdbqryallcondmatch(qry, vbuf, vsiz)){ TCLISTPUSH(res, vbuf, vsiz); } } tcbdbcurnext(cur); } tcbdbcurdel(cur); if(oname && !strcmp(oname, mcond->name)){ if(otype == TDBQONUMASC){ oname = NULL; } else if(otype == TDBQONUMDESC){ tclistinvert(res); oname = NULL; } } } else if(mcond->op == TDBQCNUMOREQ){ tcxstrprintf(hint, "using an index: \"%s\" skip (NUMOREQ)\n", mcond->name); BDBCUR *cur = tcbdbcurnew(midx->db); TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); 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(oname && !strcmp(oname, mcond->name)){ if(otype == TDBQONUMASC){ oname = NULL; } else if(otype == TDBQONUMDESC){ tclistinvert(tokens); oname = NULL; } } int tnum = TCLISTNUM(tokens); bool all = oname != NULL; if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); for(int i = 0; (all || TCLISTNUM(res) < 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); const char *kbuf; int ksiz; while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(tctdbatof(kbuf) == xnum){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); int nsiz; if(!nmap || tcmapget(nmap, vbuf, vsiz, &nsiz)){ if(acnum < 1){ TCLISTPUSH(res, vbuf, vsiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); } else if(tctdbqryallcondmatch(qry, vbuf, vsiz)){ TCLISTPUSH(res, vbuf, vsiz); } } } else { break; } tcbdbcurnext(cur); } } tclistdel(tokens); tcbdbcurdel(cur); } else if(mcond->op == TDBQCSTRAND || mcond->op == TDBQCSTROR){ tcxstrprintf(hint, "using an index: \"%s\" inverted (%s)\n", mcond->name, mcond->op == TDBQCSTRAND ? "STRAND" : "STROR"); bool all = oname != NULL; if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); 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(tdb, midx, tokens, mcond->op, hint); tcmapiterinit(tres); const char *kbuf; int ksiz; while((all || TCLISTNUM(res) < max) && (kbuf = tcmapiternext(tres, &ksiz)) != NULL){ int nsiz; if(!nmap || tcmapget(nmap, kbuf, ksiz, &nsiz)){ if(acnum < 1){ TCLISTPUSH(res, kbuf, ksiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, kbuf, ksiz)) TCLISTPUSH(res, kbuf, ksiz); } else if(tctdbqryallcondmatch(qry, kbuf, ksiz)){ TCLISTPUSH(res, kbuf, ksiz); } } } tcmapdel(tres); tclistdel(tokens); } else if(mcond->op == TDBQCFTSPH){ tcxstrprintf(hint, "using an index: \"%s\" inverted (FTS)\n", mcond->name); TCMAP *tres = tctdbidxgetbyfts(tdb, midx, mcond, hint); bool all = oname != NULL; if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); tcmapiterinit(tres); const char *kbuf; int ksiz; while((all || TCLISTNUM(res) < max) && (kbuf = tcmapiternext(tres, &ksiz)) != NULL){ int nsiz; if(!nmap || tcmapget(nmap, kbuf, ksiz, &nsiz)){ if(acnum < 1){ TCLISTPUSH(res, kbuf, ksiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, kbuf, ksiz)) TCLISTPUSH(res, kbuf, ksiz); } else if(tctdbqryallcondmatch(qry, kbuf, ksiz)){ TCLISTPUSH(res, kbuf, ksiz); } } } tcmapdel(tres); } if(nmap) tcmapdel(nmap); } if(!res && scond){ res = tclistnew(); scond->alive = false; acnum--; TDBCOND *ucond = NULL; for(int i = 0; i < cnum; i++){ TDBCOND *cond = conds + i; if(!cond->alive) continue; if(ucond){ ucond = NULL; break; } ucond = cond; } bool trim = *sidx->name != '\0'; bool asc = true; bool all = true; if(!oname){ all = false; } else if(!strcmp(oname, scond->name)){ switch(sidx->type){ case TDBITLEXICAL: switch(otype){ case TDBQOSTRASC: asc = true; all = false; break; case TDBQOSTRDESC: asc = false; all = false; break; } break; case TDBITDECIMAL: switch(otype){ case TDBQONUMASC: asc = true; all = false; break; case TDBQONUMDESC: asc = false; all = false; break; } break; } } if(asc){ tcxstrprintf(hint, "using an index: \"%s\" asc (all)\n", scond->name); BDBCUR *cur = tcbdbcurnew(sidx->db); tcbdbcurfirst(cur); if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); const char *kbuf; int ksiz; while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(trim) ksiz -= 3; if(ksiz < 0) break; if(tctdbqrycondmatch(scond, kbuf, ksiz)){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); if(acnum < 1){ TCLISTPUSH(res, vbuf, vsiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); } else if(tctdbqryallcondmatch(qry, vbuf, vsiz)){ TCLISTPUSH(res, vbuf, vsiz); } } tcbdbcurnext(cur); } tcbdbcurdel(cur); } else { tcxstrprintf(hint, "using an index: \"%s\" desc (all)\n", scond->name); BDBCUR *cur = tcbdbcurnew(sidx->db); tcbdbcurlast(cur); if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); const char *kbuf; int ksiz; while((all || TCLISTNUM(res) < max) && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(trim) ksiz -= 3; if(ksiz < 0) break; if(tctdbqrycondmatch(scond, kbuf, ksiz)){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); if(acnum < 1){ TCLISTPUSH(res, vbuf, vsiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); } else if(tctdbqryallcondmatch(qry, vbuf, vsiz)){ TCLISTPUSH(res, vbuf, vsiz); } } tcbdbcurprev(cur); } tcbdbcurdel(cur); } if(!all) oname = NULL; } if(!res && oname && max < tchdbrnum(hdb) * TDBORDRATIO){ TDBIDX *oidx = NULL; bool asc = true; for(int i = 0; !oidx && i < inum; i++){ TDBIDX *idx = idxs + i; if(strcmp(idx->name, oname)) continue; switch(idx->type){ case TDBITLEXICAL: switch(otype){ case TDBQOSTRASC: oidx = idx; asc = true; break; case TDBQOSTRDESC: oidx = idx; asc = false; break; } break; case TDBITDECIMAL: switch(otype){ case TDBQONUMASC: oidx = idx; asc = true; break; case TDBQONUMDESC: oidx = idx; asc = false; break; } break; } } if(oidx){ res = tclistnew(); TDBCOND *ucond = NULL; for(int i = 0; i < cnum; i++){ TDBCOND *cond = conds + i; if(!cond->alive) continue; if(ucond){ ucond = NULL; break; } ucond = cond; } bool trim = *oidx->name != '\0'; if(asc){ tcxstrprintf(hint, "using an index: \"%s\" asc (order)\n", oname); BDBCUR *cur = tcbdbcurnew(oidx->db); tcbdbcurfirst(cur); tcxstrprintf(hint, "limited matching: %d\n", max); const char *kbuf; int ksiz; while(TCLISTNUM(res) < max && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(trim) ksiz -= 3; if(ksiz < 0) break; int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); if(acnum < 1){ TCLISTPUSH(res, vbuf, vsiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); } else if(tctdbqryallcondmatch(qry, vbuf, vsiz)){ TCLISTPUSH(res, vbuf, vsiz); } tcbdbcurnext(cur); } tcbdbcurdel(cur); } else { tcxstrprintf(hint, "using an index: \"%s\" desc (order)\n", oname); BDBCUR *cur = tcbdbcurnew(oidx->db); tcbdbcurlast(cur); tcxstrprintf(hint, "limited matching: %d\n", max); const char *kbuf; int ksiz; while(TCLISTNUM(res) < max && (kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(trim) ksiz -= 3; if(ksiz < 0) break; int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); if(acnum < 1){ TCLISTPUSH(res, vbuf, vsiz); } else if(ucond){ if(tctdbqryonecondmatch(qry, ucond, vbuf, vsiz)) TCLISTPUSH(res, vbuf, vsiz); } else if(tctdbqryallcondmatch(qry, vbuf, vsiz)){ TCLISTPUSH(res, vbuf, vsiz); } tcbdbcurprev(cur); } tcbdbcurdel(cur); } int rnum = TCLISTNUM(res); if(rnum >= max || tcbdbrnum(oidx->db) >= tchdbrnum(hdb)){ oname = NULL; } else { tcxstrprintf(hint, "abort the result: %d\n", rnum); tclistdel(res); res = NULL; } } } if(!res){ tcxstrprintf(hint, "scanning the whole table\n"); res = tclistnew(); TDBCOND *ucond = NULL; for(int i = 0; i < cnum; i++){ TDBCOND *cond = conds + i; if(!cond->alive) continue; if(ucond){ ucond = NULL; break; } ucond = cond; } char *lkbuf = NULL; int lksiz = 0; char *pkbuf; int pksiz; const char *cbuf; int csiz; bool all = oname != NULL; if(!all && max < INT_MAX) tcxstrprintf(hint, "limited matching: %d\n", max); while((all || TCLISTNUM(res) < max) && (pkbuf = tchdbgetnext3(hdb, lkbuf, lksiz, &pksiz, &cbuf, &csiz)) != NULL){ if(ucond){ if(ucond->nsiz < 1){ char *tkbuf; TCMEMDUP(tkbuf, pkbuf, pksiz); if(tctdbqrycondmatch(ucond, tkbuf, pksiz) == ucond->sign) TCLISTPUSH(res, pkbuf, pksiz); TCFREE(tkbuf); } else { int vsiz; char *vbuf = tcmaploadone(cbuf, csiz, ucond->name, ucond->nsiz, &vsiz); if(vbuf){ if(tctdbqrycondmatch(ucond, vbuf, vsiz) == ucond->sign) TCLISTPUSH(res, pkbuf, pksiz); TCFREE(vbuf); } else { if(!ucond->sign) TCLISTPUSH(res, pkbuf, pksiz); } } } else { TCMAP *cols = tcmapload(cbuf, csiz); bool ok = true; for(int i = 0; i < cnum; i++){ TDBCOND *cond = conds + i; if(cond->nsiz < 1){ char *tkbuf; TCMEMDUP(tkbuf, pkbuf, pksiz); if(tctdbqrycondmatch(cond, tkbuf, pksiz) != cond->sign){ TCFREE(tkbuf); ok = false; break; } TCFREE(tkbuf); } else { int vsiz; const char *vbuf = tcmapget(cols, cond->name, cond->nsiz, &vsiz); if(vbuf){ if(tctdbqrycondmatch(cond, vbuf, vsiz) != cond->sign){ ok = false; break; } } else { if(cond->sign){ ok = false; break; } } } } if(ok) TCLISTPUSH(res, pkbuf, pksiz); tcmapdel(cols); } TCFREE(lkbuf); lkbuf = pkbuf; lksiz = pksiz; } TCFREE(lkbuf); } int rnum = TCLISTNUM(res); tcxstrprintf(hint, "result set size: %d\n", rnum); if(oname){ if(*oname == '\0'){ tcxstrprintf(hint, "sorting the result set: \"%s\"\n", oname); switch(otype){ case TDBQOSTRASC: tclistsort(res); break; case TDBQOSTRDESC: tclistsort(res); tclistinvert(res); break; case TDBQONUMASC: tclistsortex(res, tdbcmppkeynumasc); break; case TDBQONUMDESC: tclistsortex(res, tdbcmppkeynumdesc); break; } } else { tcxstrprintf(hint, "sorting the result set: \"%s\"\n", oname); TDBSORTREC *keys; TCMALLOC(keys, sizeof(*keys) * rnum + 1); int onsiz = strlen(oname); for(int i = 0; i < rnum; i++){ TDBSORTREC *key = keys + i; const char *kbuf; int ksiz; TCLISTVAL(kbuf, res, i, ksiz); char *vbuf = NULL; int vsiz = 0; int csiz; char *cbuf = tchdbget(hdb, kbuf, ksiz, &csiz); if(cbuf){ vbuf = tcmaploadone(cbuf, csiz, oname, onsiz, &vsiz); TCFREE(cbuf); } key->kbuf = kbuf; key->ksiz = ksiz; key->vbuf = vbuf; key->vsiz = vsiz; } int (*compar)(const TDBSORTREC *a, const TDBSORTREC *b) = NULL; switch(otype){ case TDBQOSTRASC: compar = tdbcmpsortrecstrasc; break; case TDBQOSTRDESC: compar = tdbcmpsortrecstrdesc; break; case TDBQONUMASC: compar = tdbcmpsortrecnumasc; break; case TDBQONUMDESC: compar = tdbcmpsortrecnumdesc; break; } if(compar){ if(max <= rnum / 16){ tctopsort(keys, rnum, sizeof(*keys), max, (int (*)(const void *, const void *))compar); } else { qsort(keys, rnum, sizeof(*keys), (int (*)(const void *, const void *))compar); } } TCLIST *nres = tclistnew2(rnum); for(int i = 0; i < rnum; i++){ TDBSORTREC *key = keys + i; TCLISTPUSH(nres, key->kbuf, key->ksiz); TCFREE(key->vbuf); } tclistdel(res); res = nres; TCFREE(keys); } } else if(isord){ tcxstrprintf(hint, "leaving the index order\n"); } else { tcxstrprintf(hint, "leaving the natural order\n"); } if(qry->skip > 0){ int left = tclmin(TCLISTNUM(res), qry->skip); while(left-- > 0){ int rsiz; TCFREE(tclistshift(res, &rsiz)); } max -= qry->skip; } if(TCLISTNUM(res) > max){ int left = TCLISTNUM(res) - max; while(left-- > 0){ int rsiz; TCFREE(tclistpop(res, &rsiz)); } } qry->count = TCLISTNUM(res); return res; } /* Fetch record keys from an index matching to a condition. `qry' specifies the query object. `cond' specifies the condition object. `idx' specifies an index object. The return value is a map object containing primary keys of the corresponding records. */ static TCMAP *tctdbqryidxfetch(TDBQRY *qry, TDBCOND *cond, TDBIDX *idx){ assert(qry && cond && idx); TCTDB *tdb = qry->tdb; TCHDB *hdb = tdb->hdb; TCXSTR *hint = qry->hint; const char *expr = cond->expr; int esiz = cond->esiz; bool trim = *idx->name != '\0'; TCMAP *nmap = tcmapnew2(tclmin(TDBDEFBNUM, tchdbrnum(hdb)) / 4 + 1); if(cond->op == TDBQCSTREQ){ tcxstrprintf(hint, "using an auxiliary index: \"%s\" one (STREQ)\n", cond->name); BDBCUR *cur = tcbdbcurnew(idx->db); tcbdbcurjump(cur, expr, esiz + trim); const char *kbuf; int ksiz; while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(trim) ksiz -= 3; if(ksiz == esiz && !memcmp(kbuf, expr, esiz)){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); tcmapputkeep(nmap, vbuf, vsiz, "", 0); } else { break; } tcbdbcurnext(cur); } tcbdbcurdel(cur); } else if(cond->op == TDBQCSTRBW){ tcxstrprintf(hint, "using an auxiliary index: \"%s\" asc (STRBW)\n", cond->name); BDBCUR *cur = tcbdbcurnew(idx->db); tcbdbcurjump(cur, expr, esiz + trim); const char *kbuf; int ksiz; while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(trim) ksiz -= 3; if(ksiz >= esiz && !memcmp(kbuf, expr, esiz)){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); tcmapputkeep(nmap, vbuf, vsiz, "", 0); } else { break; } tcbdbcurnext(cur); } tcbdbcurdel(cur); } else if(cond->op == TDBQCSTROREQ){ tcxstrprintf(hint, "using an auxiliary index: \"%s\" skip (STROREQ)\n", cond->name); TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); tclistsort(tokens); for(int i = 1; i < TCLISTNUM(tokens); i++){ if(!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))){ TCFREE(tclistremove2(tokens, i)); i--; } } int tnum = TCLISTNUM(tokens); for(int i = 0; i < tnum; i++){ const char *token; int tsiz; TCLISTVAL(token, tokens, i, tsiz); if(tsiz < 1) continue; BDBCUR *cur = tcbdbcurnew(idx->db); tcbdbcurjump(cur, token, tsiz + trim); const char *kbuf; int ksiz; while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(trim) ksiz -= 3; if(ksiz == tsiz && !memcmp(kbuf, token, tsiz)){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); tcmapputkeep(nmap, vbuf, vsiz, "", 0); } else { break; } tcbdbcurnext(cur); } tcbdbcurdel(cur); } tclistdel(tokens); } else if(cond->op == TDBQCNUMEQ){ tcxstrprintf(hint, "using an auxiliary index: \"%s\" asc (NUMEQ)\n", cond->name); long double xnum = tctdbatof(expr); BDBCUR *cur = tcbdbcurnew(idx->db); tctdbqryidxcurjumpnum(cur, expr, esiz, true); const char *kbuf; int ksiz; while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(tctdbatof(kbuf) == xnum){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); tcmapputkeep(nmap, vbuf, vsiz, "", 0); } else { break; } tcbdbcurnext(cur); } tcbdbcurdel(cur); } else if(cond->op == TDBQCNUMGT || cond->op == TDBQCNUMGE){ tcxstrprintf(hint, "using an auxiliary index: \"%s\" asc (NUMGT/NUMGE)\n", cond->name); long double xnum = tctdbatof(expr); BDBCUR *cur = tcbdbcurnew(idx->db); tctdbqryidxcurjumpnum(cur, expr, esiz, true); const char *kbuf; int ksiz; while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ long double knum = tctdbatof(kbuf); if(knum > xnum || (knum >= xnum && cond->op == TDBQCNUMGE)){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); tcmapputkeep(nmap, vbuf, vsiz, "", 0); } tcbdbcurnext(cur); } tcbdbcurdel(cur); } else if(cond->op == TDBQCNUMLT || cond->op == TDBQCNUMLE){ tcxstrprintf(hint, "using an auxiliary index: \"%s\" desc (NUMLT/NUMLE)\n", cond->name); long double xnum = tctdbatof(expr); BDBCUR *cur = tcbdbcurnew(idx->db); tctdbqryidxcurjumpnum(cur, expr, esiz, false); const char *kbuf; int ksiz; while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ long double knum = tctdbatof(kbuf); if(knum < xnum || (knum <= xnum && cond->op == TDBQCNUMLE)){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); tcmapputkeep(nmap, vbuf, vsiz, "", 0); } tcbdbcurprev(cur); } tcbdbcurdel(cur); } else if(cond->op == TDBQCNUMBT){ tcxstrprintf(hint, "using an auxiliary index: \"%s\" asc (NUMBT)\n", cond->name); while(*expr == ' ' || *expr == ','){ expr++; } const char *pv = expr; while(*pv != '\0' && *pv != ' ' && *pv != ','){ pv++; } esiz = pv - expr; if(*pv != ' ' && *pv != ',') pv = " "; pv++; while(*pv == ' ' || *pv == ','){ pv++; } long double lower = tctdbatof(expr); long double upper = tctdbatof(pv); if(lower > upper){ expr = pv; esiz = strlen(expr); long double swap = lower; lower = upper; upper = swap; } BDBCUR *cur = tcbdbcurnew(idx->db); tctdbqryidxcurjumpnum(cur, expr, esiz, true); const char *kbuf; int ksiz; while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(tctdbatof(kbuf) > upper) break; int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); tcmapputkeep(nmap, vbuf, vsiz, "", 0); tcbdbcurnext(cur); } tcbdbcurdel(cur); } else if(cond->op == TDBQCNUMOREQ){ tcxstrprintf(hint, "using an auxiliary index: \"%s\" skip (NUMOREQ)\n", cond->name); BDBCUR *cur = tcbdbcurnew(idx->db); TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); 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--; } } int tnum = TCLISTNUM(tokens); for(int i = 0; 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); const char *kbuf; int ksiz; while((kbuf = tcbdbcurkey3(cur, &ksiz)) != NULL){ if(tctdbatof(kbuf) == xnum){ int vsiz; const char *vbuf = tcbdbcurval3(cur, &vsiz); tcmapputkeep(nmap, vbuf, vsiz, "", 0); } else { break; } tcbdbcurnext(cur); } } tclistdel(tokens); tcbdbcurdel(cur); } else if(cond->op == TDBQCSTRAND || cond->op == TDBQCSTROR){ tcxstrprintf(hint, "using an auxiliary index: \"%s\" inverted (%s)\n", cond->name, cond->op == TDBQCSTRAND ? "STRAND" : "STROR"); TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); tclistsort(tokens); for(int i = 1; i < TCLISTNUM(tokens); i++){ if(!strcmp(TCLISTVALPTR(tokens, i), TCLISTVALPTR(tokens, i - 1))){ TCFREE(tclistremove2(tokens, i)); i--; } } tcmapdel(nmap); nmap = tctdbidxgetbytokens(tdb, idx, tokens, cond->op, hint); tclistdel(tokens); } else if(cond->op == TDBQCFTSPH){ tcxstrprintf(hint, "using an auxiliary index: \"%s\" inverted (FTS)\n", cond->name); tcmapdel(nmap); nmap = tctdbidxgetbyfts(tdb, idx, cond, hint); } tcxstrprintf(hint, "auxiliary result set size: %lld\n", (long long)TCMAPRNUM(nmap)); return nmap; } /* Convert a string to a real number. `str' specifies the string. The return value is the real number. */ static long double tctdbatof(const char *str){ assert(str); while(*str > '\0' && *str <= ' '){ str++; } int sign = 1; if(*str == '-'){ str++; sign = -1; } else if(*str == '+'){ str++; } if(tcstrifwm(str, "inf")) return HUGE_VALL * sign; if(tcstrifwm(str, "nan")) return nanl(""); long double num = 0; int col = 0; while(*str != '\0'){ if(*str < '0' || *str > '9') break; num = num * 10 + *str - '0'; str++; if(num > 0) col++; } if(*str == '.'){ str++; long double fract = 0.0; long double base = 10; while(col < TDBNUMCOLMAX && *str != '\0'){ if(*str < '0' || *str > '9') break; fract += (*str - '0') / base; str++; col++; base *= 10; } num += fract; } return num * sign; } /* Jump a cursor to the record of a key. `cur' specifies the cursor object. `expr' specifies the expression. `esiz' specifies the size of the expression. `first' specifies whether to jump the first candidate. If successful, the return value is true, else, it is false. */ static bool tctdbqryidxcurjumpnum(BDBCUR *cur, const char *expr, int esiz, bool first){ assert(cur && expr && esiz >= 0); char stack[TCNUMBUFSIZ], *rbuf; if(esiz < sizeof(stack)){ rbuf = stack; } else { TCMALLOC(rbuf, esiz + 1); } rbuf[0] = first ? 0x00 : 0x7f; memcpy(rbuf + 1, expr, esiz); bool err = false; if(first){ if(!tcbdbcurjump(cur, rbuf, esiz + 1)) err = true; } else { if(!tcbdbcurjumpback(cur, rbuf, esiz + 1)) err = true; } if(rbuf != stack) TCFREE(rbuf); return !err; } /* Check matching of one condition and a record. `qry' specifies the query object. `cond' specifies the condition object. `pkbuf' specifies the pointer to the region of the primary key. `pksiz' specifies the size of the region of the primary key. If they matches, the return value is true, else it is false. */ static bool tctdbqryonecondmatch(TDBQRY *qry, TDBCOND *cond, const char *pkbuf, int pksiz){ assert(qry && cond && pkbuf && pksiz >= 0); if(cond->nsiz < 1) return tctdbqrycondmatch(cond, pkbuf, pksiz) == cond->sign; int csiz; char *cbuf = tchdbget(qry->tdb->hdb, pkbuf, pksiz, &csiz); if(!cbuf) return false; bool rv; int vsiz; char *vbuf = tcmaploadone(cbuf, csiz, cond->name, cond->nsiz, &vsiz); if(vbuf){ rv = tctdbqrycondmatch(cond, vbuf, vsiz) == cond->sign; TCFREE(vbuf); } else { rv = !cond->sign; } TCFREE(cbuf); return rv; } /* Check matching of all conditions and a record. `qry' specifies the query object. `pkbuf' specifies the pointer to the region of the primary key. `pksiz' specifies the size of the region of the primary key. If they matches, the return value is true, else it is false. */ static bool tctdbqryallcondmatch(TDBQRY *qry, const char *pkbuf, int pksiz){ assert(qry && pkbuf && pksiz >= 0); TCTDB *tdb = qry->tdb; TDBCOND *conds = qry->conds; int cnum = qry->cnum; int csiz; char *cbuf = tchdbget(tdb->hdb, pkbuf, pksiz, &csiz); if(!cbuf) return false; TCMAP *cols = tcmapload(cbuf, csiz); bool ok = true; for(int i = 0; i < cnum; i++){ TDBCOND *cond = conds + i; if(!cond->alive) continue; if(cond->nsiz < 1){ if(tctdbqrycondmatch(cond, pkbuf, pksiz) != cond->sign){ ok = false; break; } } else { int vsiz; const char *vbuf = tcmapget(cols, cond->name, cond->nsiz, &vsiz); if(vbuf){ if(tctdbqrycondmatch(cond, vbuf, vsiz) != cond->sign){ ok = false; break; } } else { if(cond->sign){ ok = false; break; } } } } tcmapdel(cols); TCFREE(cbuf); return ok; } /* Check matching of a operand expression and a column value. `cond' specifies the condition object. `vbuf' specifies the column value. `vsiz' specifies the size of the column value. If they matches, the return value is true, else it is false. */ static bool tctdbqrycondmatch(TDBCOND *cond, const char *vbuf, int vsiz){ assert(cond && vbuf && vsiz >= 0); bool hit = false; switch(cond->op){ case TDBQCSTREQ: hit = vsiz == cond->esiz && !memcmp(vbuf, cond->expr, cond->esiz); break; case TDBQCSTRINC: hit = strstr(vbuf, cond->expr) != NULL; break; case TDBQCSTRBW: hit = tcstrfwm(vbuf, cond->expr); break; case TDBQCSTREW: hit = tcstrbwm(vbuf, cond->expr); break; case TDBQCSTRAND: hit = tctdbqrycondcheckstrand(vbuf, cond->expr); break; case TDBQCSTROR: hit = tctdbqrycondcheckstror(vbuf, cond->expr); break; case TDBQCSTROREQ: hit = tctdbqrycondcheckstroreq(vbuf, cond->expr); break; case TDBQCSTRRX: hit = cond->regex && regexec(cond->regex, vbuf, 0, NULL, 0) == 0; break; case TDBQCNUMEQ: hit = tctdbatof(vbuf) == tctdbatof(cond->expr); break; case TDBQCNUMGT: hit = tctdbatof(vbuf) > tctdbatof(cond->expr); break; case TDBQCNUMGE: hit = tctdbatof(vbuf) >= tctdbatof(cond->expr); break; case TDBQCNUMLT: hit = tctdbatof(vbuf) < tctdbatof(cond->expr); break; case TDBQCNUMLE: hit = tctdbatof(vbuf) <= tctdbatof(cond->expr); break; case TDBQCNUMBT: hit = tctdbqrycondchecknumbt(vbuf, cond->expr); break; case TDBQCNUMOREQ: hit = tctdbqrycondchecknumoreq(vbuf, cond->expr); break; case TDBQCFTSPH: hit = tctdbqrycondcheckfts(vbuf, vsiz, cond); break; } return hit; } /* Check whether a string includes all tokens in another string. `vbuf' specifies the column value. `expr' specifies the operand expression. If they matches, the return value is true, else it is false. */ static bool tctdbqrycondcheckstrand(const char *vbuf, const char *expr){ assert(vbuf && expr); const unsigned char *sp = (unsigned char *)expr; while(*sp != '\0'){ while((*sp != '\0' && *sp <= ' ') || *sp == ','){ sp++; } const unsigned char *ep = sp; while(*ep > ' ' && *ep != ','){ ep++; } if(ep > sp){ bool hit = false; const unsigned char *rp = (unsigned char *)vbuf; while(*rp != '\0'){ const unsigned char *pp; for(pp = sp; pp < ep; pp++, rp++){ if(*pp != *rp) break; } if(pp == ep && (*rp <= ' ' || *rp == ',')){ hit = true; break; } while(*rp > ' ' && *rp != ','){ rp++; } while((*rp != '\0' && *rp <= ' ') || *rp == ','){ rp++; } } if(!hit) return false; } sp = ep; } return true; } /* Check whether a string includes at least one token in another string. `vbuf' specifies the target value. `expr' specifies the operation value. If they matches, the return value is true, else it is false. */ static bool tctdbqrycondcheckstror(const char *vbuf, const char *expr){ assert(vbuf && expr); const unsigned char *sp = (unsigned char *)expr; while(*sp != '\0'){ while((*sp != '\0' && *sp <= ' ') || *sp == ','){ sp++; } const unsigned char *ep = sp; while(*ep > ' ' && *ep != ','){ ep++; } if(ep > sp){ bool hit = false; const unsigned char *rp = (unsigned char *)vbuf; while(*rp != '\0'){ const unsigned char *pp; for(pp = sp; pp < ep; pp++, rp++){ if(*pp != *rp) break; } if(pp == ep && (*rp <= ' ' || *rp == ',')){ hit = true; break; } while(*rp > ' ' && *rp != ','){ rp++; } while((*rp != '\0' && *rp <= ' ') || *rp == ','){ rp++; } } if(hit) return true; } sp = ep; } return false; } /* Check whether a string is equal to at least one token in another string. `vbuf' specifies the target value. `expr' specifies the operation value. If they matches, the return value is true, else it is false. */ static bool tctdbqrycondcheckstroreq(const char *vbuf, const char *expr){ assert(vbuf && expr); const unsigned char *sp = (unsigned char *)expr; while(*sp != '\0'){ while((*sp != '\0' && *sp <= ' ') || *sp == ','){ sp++; } const unsigned char *ep = sp; while(*ep > ' ' && *ep != ','){ ep++; } if(ep > sp){ const unsigned char *rp; for(rp = (unsigned char *)vbuf; *rp != '\0'; rp++){ if(*sp != *rp || sp >= ep) break; sp++; } if(*rp == '\0' && sp == ep) return true; } sp = ep; } return false; } /* Check whether a decimal string is between two tokens in another string. `vbuf' specifies the target value. `expr' specifies the operation value. If they matches, the return value is true, else it is false. */ static bool tctdbqrycondchecknumbt(const char *vbuf, const char *expr){ assert(vbuf && expr); while(*expr == ' ' || *expr == ','){ expr++; } const char *pv = expr; while(*pv != '\0' && *pv != ' ' && *pv != ','){ pv++; } if(*pv != ' ' && *pv != ',') pv = " "; pv++; while(*pv == ' ' || *pv == ','){ pv++; } long double val = tctdbatof(vbuf); long double lower = tctdbatof(expr); long double upper = tctdbatof(pv); if(lower > upper){ long double swap = lower; lower = upper; upper = swap; } return val >= lower && val <= upper; } /* Check whether a number is equal to at least one token in another string. `vbuf' specifies the target value. `expr' specifies the operation value. If they matches, the return value is true, else it is false. */ static bool tctdbqrycondchecknumoreq(const char *vbuf, const char *expr){ assert(vbuf && expr); long double vnum = tctdbatof(vbuf); const char *sp = expr; while(*sp != '\0'){ while(*sp == ' ' || *sp == ','){ sp++; } const char *ep = sp; while(*ep != '\0' && *ep != ' ' && *ep != ','){ ep++; } if(ep > sp && vnum == tctdbatof(sp)) return true; sp = ep; } return false; } /* Check whether a text matches a condition. `vbuf' specifies the target value. `vsiz' specifies the size of the target value. `cond' specifies the condition object. If they matches, the return value is true, else it is false. */ static bool tctdbqrycondcheckfts(const char *vbuf, int vsiz, TDBCOND *cond){ assert(vbuf && cond); TDBFTSUNIT *ftsunits = cond->ftsunits; int ftsnum = cond->ftsnum; if(ftsnum < 1) return false; if(!ftsunits[0].sign) return false; char astack[TDBCOLBUFSIZ]; uint16_t *ary; int asiz = sizeof(*ary) * (vsiz + 1); if(asiz < sizeof(astack)){ ary = (uint16_t *)astack; } else { TCMALLOC(ary, asiz + 1); } int anum; tcstrutftoucs(vbuf, ary, &anum); anum = tcstrucsnorm(ary, anum, TCUNSPACE | TCUNLOWER | TCUNNOACC | TCUNWIDTH); char sstack[TDBCOLBUFSIZ], *str; int ssiz = anum * 3 + 1; if(ssiz < sizeof(sstack)){ str = sstack; } else { TCMALLOC(str, ssiz + 1); } tcstrucstoutf(ary, anum, str); bool ok = true; for(int i = 0; i < ftsnum; i++){ TDBFTSUNIT *ftsunit = ftsunits + i; TCLIST *tokens = ftsunit->tokens; int tnum = TCLISTNUM(tokens); bool hit = false; for(int j = 0; j < tnum; j++){ if(strstr(str, TCLISTVALPTR(tokens, j))){ hit = true; break; } } if(hit != ftsunit->sign) ok = false; } if(str != sstack) TCFREE(str); if(ary != (uint16_t *)astack) TCFREE(ary); return ok; } /* Compare two primary keys by number ascending. `a' specifies a key. `b' specifies of the other key. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tdbcmppkeynumasc(const TCLISTDATUM *a, const TCLISTDATUM *b){ assert(a && b); return tccmpdecimal(a->ptr, a->size, b->ptr, b->size, NULL); } /* Compare two primary keys by number descending. `a' specifies a key. `b' specifies of the other key. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tdbcmppkeynumdesc(const TCLISTDATUM *a, const TCLISTDATUM *b){ assert(a && b); return tccmpdecimal(b->ptr, b->size, a->ptr, a->size, NULL); } /* Compare two sort records by string ascending. `a' specifies a key. `b' specifies of the other key. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tdbcmpsortrecstrasc(const TDBSORTREC *a, const TDBSORTREC *b){ assert(a && b); if(!a->vbuf){ if(!b->vbuf) return 0; return 1; } if(!b->vbuf){ if(!a->vbuf) return 0; return -1; } int rv; TCCMPLEXICAL(rv, a->vbuf, a->vsiz, b->vbuf, b->vsiz); return rv; } /* Compare two sort records by string descending. `a' specifies a key. `b' specifies of the other key. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tdbcmpsortrecstrdesc(const TDBSORTREC *a, const TDBSORTREC *b){ assert(a && b); if(!a->vbuf){ if(!b->vbuf) return 0; return 1; } if(!b->vbuf){ if(!a->vbuf) return 0; return -1; } int rv; TCCMPLEXICAL(rv, a->vbuf, a->vsiz, b->vbuf, b->vsiz); return -rv; } /* Compare two sort records by number ascending. `a' specifies a key. `b' specifies of the other key. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tdbcmpsortrecnumasc(const TDBSORTREC *a, const TDBSORTREC *b){ assert(a && b); if(!a->vbuf){ if(!b->vbuf) return 0; return 1; } if(!b->vbuf){ if(!a->vbuf) return 0; return -1; } long double anum = tctdbatof(a->vbuf); long double bnum = tctdbatof(b->vbuf); if(anum < bnum) return -1; if(anum > bnum) return 1; return 0; } /* Compare two sort records by number descending. `a' specifies a key. `b' specifies of the other key. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tdbcmpsortrecnumdesc(const TDBSORTREC *a, const TDBSORTREC *b){ assert(a && b); if(!a->vbuf){ if(!b->vbuf) return 0; return 1; } if(!b->vbuf){ if(!a->vbuf) return 0; return -1; } long double anum = tctdbatof(a->vbuf); long double bnum = tctdbatof(b->vbuf); if(anum < bnum) return 1; if(anum > bnum) return -1; return 0; } /* Get the hash value of a record. `pkbuf' specifies the pointer to the region of the primary key. `pksiz' specifies the size of the region of the primary key. The return value is the hash value. */ static uint16_t tctdbidxhash(const char *pkbuf, int pksiz){ assert(pkbuf && pksiz && pksiz >= 0); uint32_t hash = 19780211; while(pksiz--){ hash = hash * 37 + *(uint8_t *)pkbuf++; } return hash; } /* Add a record into indices of a table database object. `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. `cols' specifies a map object containing columns. If successful, the return value is true, else, it is false. */ static bool tctdbidxput(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols){ assert(tdb && pkbuf && pksiz >= 0 && cols); bool err = false; uint16_t hash = tctdbidxhash(pkbuf, pksiz); TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; if(*(idx->name) != '\0') continue; char stack[TDBCOLBUFSIZ], *rbuf; if(pksiz < sizeof(stack)){ rbuf = stack; } else { TCMALLOC(rbuf, pksiz + 1); } memcpy(rbuf, pkbuf, pksiz); rbuf[pksiz] = '\0'; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: if(!tcbdbput(idx->db, pkbuf, pksiz, rbuf, pksiz)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } break; case TDBITTOKEN: if(!tctdbidxputtoken(tdb, idx, pkbuf, pksiz, pkbuf, pksiz)) err = true; break; case TDBITQGRAM: if(!tctdbidxputqgram(tdb, idx, pkbuf, pksiz, pkbuf, pksiz)) err = true; break; } if(rbuf != stack) TCFREE(rbuf); } tcmapiterinit(cols); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(cols, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; if(strcmp(idx->name, kbuf)) continue; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: if(!tctdbidxputone(tdb, idx, pkbuf, pksiz, hash, vbuf, vsiz)) err = true; break; case TDBITTOKEN: if(!tctdbidxputtoken(tdb, idx, pkbuf, pksiz, vbuf, vsiz)) err = true; break; case TDBITQGRAM: if(!tctdbidxputqgram(tdb, idx, pkbuf, pksiz, vbuf, vsiz)) err = true; break; } } } return !err; } /* Add a column of a record into an index of a table database object. `tdb' specifies the table database object. `idx' specifies the index object. `pkbuf' specifies the pointer to the region of the primary key. `pksiz' specifies the size of the region of the primary key. `hash' specifies the hash value of the primary key. `vbuf' specifies the pointer to the region of the column value. `vsiz' specifies the size of the region of the column value. If successful, the return value is true, else, it is false. */ static bool tctdbidxputone(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, uint16_t hash, const char *vbuf, int vsiz){ assert(tdb && pkbuf && pksiz >= 0 && vbuf && vsiz); bool err = false; char stack[TDBCOLBUFSIZ], *rbuf; int rsiz = vsiz + 3; if(rsiz <= sizeof(stack)){ rbuf = stack; } else { TCMALLOC(rbuf, rsiz); } memcpy(rbuf, vbuf, vsiz); rbuf[vsiz] = '\0'; rbuf[vsiz+1] = hash >> 8; rbuf[vsiz+2] = hash & 0xff; if(!tcbdbputdup(idx->db, rbuf, rsiz, pkbuf, pksiz)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } if(rbuf != stack) TCFREE(rbuf); return !err; } /* Add a column of a record into an token inverted index of a table database object. `tdb' specifies the table database object. `idx' specifies the index object. `pkbuf' specifies the pointer to the region of the primary key. `pksiz' specifies the size of the region of the primary key. `vbuf' specifies the pointer to the region of the column value. `vsiz' specifies the size of the region of the column value. If successful, the return value is true, else, it is false. */ static bool tctdbidxputtoken(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, const char *vbuf, int vsiz){ assert(tdb && idx && pkbuf && pksiz >= 0 && vbuf && vsiz >= 0); bool err = false; TCMAP *cc = idx->cc; char stack[TDBCOLBUFSIZ], *rbuf; int rsiz = pksiz + TCNUMBUFSIZ; if(rsiz < sizeof(stack)){ rbuf = stack; } else { TCMALLOC(rbuf, rsiz); } uint64_t pkid = 0; for(int i = 0; i < pksiz; i++){ int c = pkbuf[i]; if(c >= '0' && c <= '9'){ pkid = pkid * 10 + c - '0'; if(pkid > INT64_MAX){ pkid = 0; break; } } else { pkid = 0; break; } } if(pksiz > 0 && *pkbuf == '0') pkid = 0; if(pkid > 0){ TCSETVNUMBUF64(rsiz, rbuf, pkid); } else { char *wp = rbuf; *(wp++) = '\0'; TCSETVNUMBUF(rsiz, wp, pksiz); wp += rsiz; memcpy(wp, pkbuf, pksiz); wp += pksiz; rsiz = wp - rbuf; } const unsigned char *sp = (unsigned char *)vbuf; while(*sp != '\0'){ while((*sp != '\0' && *sp <= ' ') || *sp == ','){ sp++; } const unsigned char *ep = sp; while(*ep > ' ' && *ep != ','){ ep++; } if(ep > sp) tcmapputcat3(cc, sp, ep - sp, rbuf, rsiz); sp = ep; } if(rbuf != stack) TCFREE(rbuf); if(tcmapmsiz(cc) > tdb->iccmax && !tctdbidxsyncicc(tdb, idx, false)) err = true; return !err; } /* Add a column of a record into an q-gram inverted index of a table database object. `tdb' specifies the table database object. `idx' specifies the index object. `pkbuf' specifies the pointer to the region of the primary key. `pksiz' specifies the size of the region of the primary key. `vbuf' specifies the pointer to the region of the column value. `vsiz' specifies the size of the region of the column value. If successful, the return value is true, else, it is false. */ static bool tctdbidxputqgram(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, const char *vbuf, int vsiz){ assert(tdb && idx && pkbuf && pksiz >= 0 && vbuf && vsiz >= 0); bool err = false; TCMAP *cc = idx->cc; char stack[TDBCOLBUFSIZ], *rbuf; int rsiz = pksiz + TCNUMBUFSIZ * 2; if(rsiz < sizeof(stack)){ rbuf = stack; } else { TCMALLOC(rbuf, rsiz); } uint64_t pkid = 0; for(int i = 0; i < pksiz; i++){ int c = pkbuf[i]; if(c >= '0' && c <= '9'){ pkid = pkid * 10 + c - '0'; if(pkid > INT64_MAX){ pkid = 0; break; } } else { pkid = 0; break; } } if(pksiz > 0 && *pkbuf == '0') pkid = 0; if(pkid > 0){ TCSETVNUMBUF64(rsiz, rbuf, pkid); } else { char *wp = rbuf; *(wp++) = '\0'; TCSETVNUMBUF(rsiz, wp, pksiz); wp += rsiz; memcpy(wp, pkbuf, pksiz); wp += pksiz; rsiz = wp - rbuf; } uint16_t *ary; TCMALLOC(ary, sizeof(*ary) * (vsiz + TDBIDXQGUNIT)); int anum; tcstrutftoucs(vbuf, ary, &anum); anum = tcstrucsnorm(ary, anum, TCUNSPACE | TCUNLOWER | TCUNNOACC | TCUNWIDTH); for(int i = 0; i < TDBIDXQGUNIT; i++){ ary[anum+i] = 0; } char *wp = rbuf + rsiz; char token[TDBIDXQGUNIT*3+1]; for(int i = 0; i < anum; i++){ tcstrucstoutf(ary + i, TDBIDXQGUNIT, token); int step; TCSETVNUMBUF(step, wp, i); tcmapputcat3(cc, token, strlen(token), rbuf, rsiz + step); } TCFREE(ary); if(rbuf != stack) TCFREE(rbuf); if(tcmapmsiz(cc) > tdb->iccmax && !tctdbidxsyncicc(tdb, idx, false)) err = true; return !err; } /* Remove a record from indices of a table database object. `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. `cols' specifies a map object containing columns. If successful, the return value is true, else, it is false. */ static bool tctdbidxout(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols){ assert(tdb && pkbuf && pksiz >= 0 && cols); bool err = false; uint16_t hash = tctdbidxhash(pkbuf, pksiz); TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; if(*(idx->name) != '\0') continue; char stack[TDBCOLBUFSIZ], *rbuf; if(pksiz < sizeof(stack)){ rbuf = stack; } else { TCMALLOC(rbuf, pksiz + 1); } memcpy(rbuf, pkbuf, pksiz); rbuf[pksiz] = '\0'; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: if(!tcbdbout(idx->db, pkbuf, pksiz)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } break; case TDBITTOKEN: if(!tctdbidxouttoken(tdb, idx, pkbuf, pksiz, rbuf, pksiz)) err = true; break; case TDBITQGRAM: if(!tctdbidxoutqgram(tdb, idx, pkbuf, pksiz, rbuf, pksiz)) err = true; break; } if(rbuf != stack) TCFREE(rbuf); } tcmapiterinit(cols); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(cols, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; if(strcmp(idx->name, kbuf)) continue; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: if(!tctdbidxoutone(tdb, idx, pkbuf, pksiz, hash, vbuf, vsiz)) err = true; break; case TDBITTOKEN: if(!tctdbidxouttoken(tdb, idx, pkbuf, pksiz, vbuf, vsiz)) err = true; break; case TDBITQGRAM: if(!tctdbidxoutqgram(tdb, idx, pkbuf, pksiz, vbuf, vsiz)) err = true; break; } } } return !err; } /* Remove a column of a record from an index of a table database object. `tdb' specifies the table database object. `idx' specifies the index object. `pkbuf' specifies the pointer to the region of the primary key. `pksiz' specifies the size of the region of the primary key. `hash' specifies the hash value of the primary key. `vbuf' specifies the pointer to the region of the column value. `vsiz' specifies the size of the region of the column value. If successful, the return value is true, else, it is false. */ static bool tctdbidxoutone(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, uint16_t hash, const char *vbuf, int vsiz){ assert(tdb && idx && pkbuf && pksiz >= 0 && vbuf && vsiz >= 0); bool err = false; char stack[TDBCOLBUFSIZ], *rbuf; int rsiz = vsiz + 3; if(rsiz <= sizeof(stack)){ rbuf = stack; } else { TCMALLOC(rbuf, rsiz); } memcpy(rbuf, vbuf, vsiz); rbuf[vsiz] = '\0'; rbuf[vsiz+1] = hash >> 8; rbuf[vsiz+2] = hash & 0xff; int ovsiz; const char *ovbuf = tcbdbget3(idx->db, rbuf, rsiz, &ovsiz); if(ovbuf && ovsiz == pksiz && !memcmp(ovbuf, pkbuf, ovsiz)){ if(!tcbdbout(idx->db, rbuf, rsiz)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } } else { BDBCUR *cur = tcbdbcurnew(idx->db); if(tcbdbcurjump(cur, rbuf, rsiz)){ int oksiz; const char *okbuf; while((okbuf = tcbdbcurkey3(cur, &oksiz)) != NULL){ if(oksiz != rsiz || memcmp(okbuf, rbuf, oksiz)) break; ovbuf = tcbdbcurval3(cur, &ovsiz); if(ovsiz == pksiz && !memcmp(ovbuf, pkbuf, ovsiz)){ if(!tcbdbcurout(cur)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } break; } tcbdbcurnext(cur); } } else { tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } tcbdbcurdel(cur); } if(rbuf != stack) TCFREE(rbuf); return !err; } /* Remove a column of a record from a token inverted index of a table database object. `tdb' specifies the table database object. `idx' specifies the index object. `pkbuf' specifies the pointer to the region of the primary key. `pksiz' specifies the size of the region of the primary key. `vbuf' specifies the pointer to the region of the column value. `vsiz' specifies the size of the region of the column value. If successful, the return value is true, else, it is false. */ static bool tctdbidxouttoken(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, const char *vbuf, int vsiz){ assert(tdb && idx && pkbuf && pksiz >= 0 && vbuf && vsiz >= 0); bool err = false; TCBDB *db = idx->db; TCMAP *cc = idx->cc; uint64_t pkid = 0; for(int i = 0; i < pksiz; i++){ int c = pkbuf[i]; if(c >= '0' && c <= '9'){ pkid = pkid * 10 + c - '0'; if(pkid > INT64_MAX){ pkid = 0; break; } } else { pkid = 0; break; } } TCXSTR *xstr = tcxstrnew(); const unsigned char *sp = (unsigned char *)vbuf; while(*sp != '\0'){ while((*sp != '\0' && *sp <= ' ') || *sp == ','){ sp++; } const unsigned char *ep = sp; while(*ep > ' ' && *ep != ','){ ep++; } if(ep > sp){ tcxstrclear(xstr); int len = ep - sp; int csiz; const char *cbuf = tcmapget(cc, sp, len, &csiz); if(cbuf){ while(csiz > 0){ const char *pv = cbuf; bool ok = true; if(*cbuf == '\0'){ cbuf++; csiz--; int tsiz, step; TCREADVNUMBUF(cbuf, tsiz, step); cbuf += step; csiz -= step; if(tsiz == pksiz && !memcmp(cbuf, pkbuf, tsiz)) ok = false; cbuf += tsiz; csiz -= tsiz; } else { int64_t tid; int step; TCREADVNUMBUF64(cbuf, tid, step); if(tid == pkid) ok = false; cbuf += step; csiz -= step; } if(ok) TCXSTRCAT(xstr, pv, cbuf - pv); } if(csiz != 0){ tctdbsetecode(tdb, TCEMISC, __FILE__, __LINE__, __func__); err = true; } } cbuf = tcbdbget3(db, sp, len, &csiz); if(cbuf){ while(csiz > 0){ const char *pv = cbuf; bool ok = true; if(*cbuf == '\0'){ cbuf++; csiz--; int tsiz, step; TCREADVNUMBUF(cbuf, tsiz, step); cbuf += step; csiz -= step; if(tsiz == pksiz && !memcmp(cbuf, pkbuf, tsiz)) ok = false; cbuf += tsiz; csiz -= tsiz; } else { int64_t tid; int step; TCREADVNUMBUF64(cbuf, tid, step); if(tid == pkid) ok = false; cbuf += step; csiz -= step; } if(ok) TCXSTRCAT(xstr, pv, cbuf - pv); } if(csiz != 0){ tctdbsetecode(tdb, TCEMISC, __FILE__, __LINE__, __func__); err = true; } if(!tcbdbout(db, sp, len)){ tctdbsetecode(tdb, tcbdbecode(db), __FILE__, __LINE__, __func__); err = true; } } tcmapput(cc, sp, len, TCXSTRPTR(xstr), TCXSTRSIZE(xstr)); } sp = ep; } tcxstrdel(xstr); if(tcmapmsiz(cc) > tdb->iccmax && !tctdbidxsyncicc(tdb, idx, false)) err = true; return !err; } /* Remove a column of a record from a q-gram inverted index of a table database object. `tdb' specifies the table database object. `idx' specifies the index object. `pkbuf' specifies the pointer to the region of the primary key. `pksiz' specifies the size of the region of the primary key. `vbuf' specifies the pointer to the region of the column value. `vsiz' specifies the size of the region of the column value. If successful, the return value is true, else, it is false. */ static bool tctdbidxoutqgram(TCTDB *tdb, TDBIDX *idx, const char *pkbuf, int pksiz, const char *vbuf, int vsiz){ assert(tdb && idx && pkbuf && pksiz >= 0 && vbuf && vsiz >= 0); bool err = false; TCBDB *db = idx->db; TCMAP *cc = idx->cc; uint64_t pkid = 0; for(int i = 0; i < pksiz; i++){ int c = pkbuf[i]; if(c >= '0' && c <= '9'){ pkid = pkid * 10 + c - '0'; if(pkid > INT64_MAX){ pkid = 0; break; } } else { pkid = 0; break; } } TCXSTR *xstr = tcxstrnew(); uint16_t *ary; TCMALLOC(ary, sizeof(*ary) * (vsiz + TDBIDXQGUNIT)); int anum; tcstrutftoucs(vbuf, ary, &anum); anum = tcstrucsnorm(ary, anum, TCUNSPACE | TCUNLOWER | TCUNNOACC | TCUNWIDTH); for(int i = 0; i < TDBIDXQGUNIT; i++){ ary[anum+i] = 0; } char token[TDBIDXQGUNIT*3+1]; for(int i = 0; i < anum; i++){ tcstrucstoutf(ary + i, TDBIDXQGUNIT, token); int tsiz = strlen(token); tcxstrclear(xstr); int csiz; const char *cbuf = tcmapget(cc, token, tsiz, &csiz); if(cbuf){ while(csiz > 0){ const char *pv = cbuf; bool ok = true; if(*cbuf == '\0'){ cbuf++; csiz--; int tsiz, step; TCREADVNUMBUF(cbuf, tsiz, step); cbuf += step; csiz -= step; if(tsiz == pksiz && !memcmp(cbuf, pkbuf, tsiz)) ok = false; cbuf += tsiz; csiz -= tsiz; } else { int64_t tid; int step; TCREADVNUMBUF64(cbuf, tid, step); if(tid == pkid) ok = false; cbuf += step; csiz -= step; } if(csiz > 0){ int off, step; TCREADVNUMBUF(cbuf, off, step); cbuf += step; csiz -= step; if(ok) TCXSTRCAT(xstr, pv, cbuf - pv); } } if(csiz != 0){ tctdbsetecode(tdb, TCEMISC, __FILE__, __LINE__, __func__); err = true; } } cbuf = tcbdbget3(db, token, tsiz, &csiz); if(cbuf){ while(csiz > 0){ const char *pv = cbuf; bool ok = true; if(*cbuf == '\0'){ cbuf++; csiz--; int tsiz, step; TCREADVNUMBUF(cbuf, tsiz, step); cbuf += step; csiz -= step; if(tsiz == pksiz && !memcmp(cbuf, pkbuf, tsiz)) ok = false; cbuf += tsiz; csiz -= tsiz; } else { int64_t tid; int step; TCREADVNUMBUF64(cbuf, tid, step); if(tid == pkid) ok = false; cbuf += step; csiz -= step; } if(csiz > 0){ int off, step; TCREADVNUMBUF(cbuf, off, step); cbuf += step; csiz -= step; if(ok) TCXSTRCAT(xstr, pv, cbuf - pv); } } if(csiz != 0){ tctdbsetecode(tdb, TCEMISC, __FILE__, __LINE__, __func__); err = true; } if(!tcbdbout(db, token, tsiz)){ tctdbsetecode(tdb, tcbdbecode(db), __FILE__, __LINE__, __func__); err = true; } } tcmapput(cc, token, tsiz, TCXSTRPTR(xstr), TCXSTRSIZE(xstr)); } TCFREE(ary); tcxstrdel(xstr); if(tcmapmsiz(cc) > tdb->iccmax && !tctdbidxsyncicc(tdb, idx, false)) err = true; return !err; } /* Synchronize updated contents of an inverted cache of a table database object. `tdb' specifies the table database object. `idx' specifies the index object. `all' specifies whether to sync all tokens. If successful, the return value is true, else, it is false. */ static bool tctdbidxsyncicc(TCTDB *tdb, TDBIDX *idx, bool all){ assert(tdb && idx); TCBDB *db = idx->db; TCMAP *cc = idx->cc; int rnum = TCMAPRNUM(cc); if(rnum < 1) return true; bool err = false; const char **keys; TCMALLOC(keys, sizeof(*keys) * rnum); int knum = 0; int64_t usiz = tcmapmsiz(cc) - sizeof(void *) * TDBIDXICCBNUM; int64_t max = all ? INT64_MAX : usiz * tdb->iccsync; int64_t sum = 0; const char *kbuf; int ksiz; tcmapiterinit(cc); while(sum < max && (kbuf = tcmapiternext(cc, &ksiz)) != NULL){ int vsiz; tcmapiterval(kbuf, &vsiz); keys[knum++] = kbuf; sum += sizeof(TCMAPREC) + sizeof(void *) + ksiz + vsiz; } qsort(keys, knum, sizeof(*keys), (int(*)(const void *, const void *))tctdbidxcmpkey); for(int i = 0; i < knum; i++){ const char *kbuf = keys[i]; int ksiz = strlen(kbuf); int vsiz; const char *vbuf = tcmapget(cc, kbuf, ksiz, &vsiz); if(vsiz > 0 && !tcbdbputcat(db, kbuf, ksiz, vbuf, vsiz)){ tctdbsetecode(tdb, tcbdbecode(db), __FILE__, __LINE__, __func__); err = true; } tcmapout(cc, kbuf, ksiz); } TCFREE(keys); return !err; } /* Compare two index search keys in lexical order. `a' specifies the pointer to one element. `b' specifies the pointer to the other element. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tctdbidxcmpkey(const char **a, const char **b){ assert(a && b); const unsigned char *ap = (unsigned char *)*a; const unsigned char *bp = (unsigned char *)*b; while(true){ if(*ap == '\0') return *bp == '\0' ? 0 : -1; if(*bp == '\0') return *ap == '\0' ? 0 : 1; if(*ap != *bp) return *ap - *bp; ap++; bp++; } return 0; } /* Retrieve records by a token inverted index of a table database object. `tdb' specifies the table database object. `idx' specifies the index object. `token' specifies the list object of tokens. `op' specifies the operation type. `hint' specifies the hint object. The return value is a map object of the primary keys of the corresponding records. */ static TCMAP *tctdbidxgetbytokens(TCTDB *tdb, TDBIDX *idx, const TCLIST *tokens, int op, TCXSTR *hint){ assert(tdb && idx && tokens && hint); TCBDB *db = idx->db; TCMAP *cc = idx->cc; int tnum = TCLISTNUM(tokens); TCMAP *res = tcmapnew(); int cnt = 0; for(int i = 0; i < tnum; i++){ const char *token; int tsiz; TCLISTVAL(token, tokens, i, tsiz); if(tsiz < 1) continue; int onum = 0; TCMAP *wring = (cnt > 0 && op == TDBQCSTRAND) ? tcmapnew() : NULL; int csiz; const char *cbuf = tcmapget(cc, token, tsiz, &csiz); if(cbuf){ while(csiz > 0){ if(*cbuf == '\0'){ cbuf++; csiz--; int tsiz, step; TCREADVNUMBUF(cbuf, tsiz, step); cbuf += step; csiz -= step; if(cnt < 1){ tcmapput(res, cbuf, tsiz, "", 0); } else if(wring){ int rsiz; if(tcmapget(res, cbuf, tsiz, &rsiz)) tcmapput(wring, cbuf, tsiz, "", 0); } else { tcmapput(res, cbuf, tsiz, "", 0); } cbuf += tsiz; csiz -= tsiz; } else { int64_t tid; int step; TCREADVNUMBUF64(cbuf, tid, step); char pkbuf[TCNUMBUFSIZ]; int pksiz = sprintf(pkbuf, "%lld", (long long)tid); if(cnt < 1){ tcmapput(res, pkbuf, pksiz, "", 0); } else if(wring){ int rsiz; if(tcmapget(res, pkbuf, pksiz, &rsiz)) tcmapput(wring, pkbuf, pksiz, "", 0); } else { tcmapput(res, pkbuf, pksiz, "", 0); } cbuf += step; csiz -= step; } onum++; } } cbuf = tcbdbget3(db, token, tsiz, &csiz); if(cbuf){ while(csiz > 0){ if(*cbuf == '\0'){ cbuf++; csiz--; int tsiz, step; TCREADVNUMBUF(cbuf, tsiz, step); cbuf += step; csiz -= step; if(cnt < 1){ tcmapput(res, cbuf, tsiz, "", 0); } else if(wring){ int rsiz; if(tcmapget(res, cbuf, tsiz, &rsiz)) tcmapput(wring, cbuf, tsiz, "", 0); } else { tcmapput(res, cbuf, tsiz, "", 0); } cbuf += tsiz; csiz -= tsiz; } else { int64_t tid; int step; TCREADVNUMBUF64(cbuf, tid, step); char pkbuf[TCNUMBUFSIZ]; int pksiz = sprintf(pkbuf, "%lld", (long long)tid); if(cnt < 1){ tcmapput(res, pkbuf, pksiz, "", 0); } else if(wring){ int rsiz; if(tcmapget(res, pkbuf, pksiz, &rsiz)) tcmapput(wring, pkbuf, pksiz, "", 0); } else { tcmapput(res, pkbuf, pksiz, "", 0); } cbuf += step; csiz -= step; } onum++; } } if(wring){ tcmapdel(res); res = wring; } tcxstrprintf(hint, "token occurrence: \"%s\" %d\n", token, onum); cnt++; } return res; } /* Retrieve records by a token inverted index of a table database object. `tdb' specifies the table database object. `idx' specifies the index object. `cond' specifies the condition object. `hint' specifies the hint object. The return value is a map object of the primary keys of the corresponding records. */ static TCMAP *tctdbidxgetbyfts(TCTDB *tdb, TDBIDX *idx, TDBCOND *cond, TCXSTR *hint){ assert(tdb && idx && cond && hint); TDBFTSUNIT *ftsunits = cond->ftsunits; int ftsnum = cond->ftsnum; if(ftsnum < 1) return tcmapnew2(1); if(!ftsunits[0].sign) return tcmapnew2(1); TCMAP *res = tcmapnew(); tctdbidxgetbyftsunion(idx, ftsunits->tokens, true, NULL, res, hint); for(int i = 1; i < ftsnum; i++){ TDBFTSUNIT *ftsunit = ftsunits + i; if(ftsunit->sign){ TCMAP *nres = tcmapnew2(TCMAPRNUM(res) + 1); tctdbidxgetbyftsunion(idx, ftsunit->tokens, true, res, nres, hint); tcmapdel(res); res = nres; } else { tctdbidxgetbyftsunion(idx, ftsunit->tokens, false, res, NULL, hint); } } return res; } /* Retrieve union records by a token inverted index of a table database object. `idx' specifies the index object. `tokens' specifies a list object of the union tokens. `sign' specifies the logical sign. `ores' specifies a map object of old primary keys. `nres' specifies a map object of new primary keys. `hint' specifies the hint object. */ static void tctdbidxgetbyftsunion(TDBIDX *idx, const TCLIST *tokens, bool sign, TCMAP *ores, TCMAP *nres, TCXSTR *hint){ assert(idx && tokens && hint); TCBDB *db = idx->db; TCMAP *cc = idx->cc; int tnum = TCLISTNUM(tokens); for(int i = 0; i < tnum; i++){ const char *word; int wsiz; TCLISTVAL(word, tokens, i, wsiz); uint16_t *ary; TCMALLOC(ary, sizeof(*ary) * (wsiz + TDBIDXQGUNIT)); int anum; tcstrutftoucs(word, ary, &anum); for(int j = 0; j < TDBIDXQGUNIT; j++){ ary[anum+j] = 0; } if(anum >= TDBIDXQGUNIT){ TDBFTSSTROCR *socrs; TCMALLOC(socrs, TDBFTSOCRUNIT * sizeof(*socrs)); int sonum = 0; int soanum = TDBFTSOCRUNIT; int sobase = 0; TDBFTSNUMOCR *nocrs; TCMALLOC(nocrs, TDBFTSOCRUNIT * sizeof(*nocrs)); int nonum = 0; int noanum = TDBFTSOCRUNIT; int nobase = 0; TCBITMAP *pkmap = TCBITMAPNEW(TDBFTSBMNUM); char token[TDBIDXQGUNIT*3+1]; uint16_t seq = 0; for(int j = 0; j < anum; j += TDBIDXQGUNIT){ sobase = sonum; nobase = nonum; int diff = anum - j - TDBIDXQGUNIT; if(diff < 0){ j += diff; diff = -diff; } else { diff = 0; } tcstrucstoutf(ary + j, TDBIDXQGUNIT, token); int tsiz = strlen(token); int csiz; const char *cbuf = tcmapget(cc, token, tsiz, &csiz); if(cbuf){ while(csiz > 0){ const char *pkbuf = NULL; int32_t pksiz = 0; int64_t pkid = 0; if(*cbuf == '\0'){ cbuf++; csiz--; int step; TCREADVNUMBUF(cbuf, pksiz, step); cbuf += step; csiz -= step; pkbuf = cbuf; cbuf += pksiz; csiz -= pksiz; } else { int step; TCREADVNUMBUF64(cbuf, pkid, step); cbuf += step; csiz -= step; } if(csiz > 0){ int off, step; TCREADVNUMBUF(cbuf, off, step); cbuf += step; csiz -= step; off += diff; if(pkbuf){ unsigned int hash = 19780211; for(int k = 0; k < pksiz; k++){ hash = hash * 37 + ((unsigned char *)pkbuf)[k]; } hash = hash % TDBFTSBMNUM; if(j == 0 || TCBITMAPCHECK(pkmap, hash)){ if(sonum >= soanum){ soanum *= 2; TCREALLOC(socrs, socrs, soanum * sizeof(*socrs)); } TDBFTSSTROCR *ocr = socrs + sonum; ocr->pkbuf = pkbuf; ocr->pksiz = pksiz; ocr->off = off; ocr->seq = seq; ocr->hash = hash; sonum++; if(j == 0) TCBITMAPON(pkmap, hash); } } else { unsigned int hash = pkid % TDBFTSBMNUM; if(j == 0 || TCBITMAPCHECK(pkmap, hash)){ if(nonum >= noanum){ noanum *= 2; TCREALLOC(nocrs, nocrs, noanum * sizeof(*nocrs)); } TDBFTSNUMOCR *ocr = nocrs + nonum; ocr->pkid = pkid; ocr->off = off; ocr->seq = seq; ocr->hash = hash; nonum++; if(j == 0) TCBITMAPON(pkmap, hash); } } } } } cbuf = tcbdbget3(db, token, tsiz, &csiz); if(cbuf){ while(csiz > 0){ const char *pkbuf = NULL; int32_t pksiz = 0; int64_t pkid = 0; if(*cbuf == '\0'){ cbuf++; csiz--; int step; TCREADVNUMBUF(cbuf, pksiz, step); cbuf += step; csiz -= step; pkbuf = cbuf; cbuf += pksiz; csiz -= pksiz; } else { int step; TCREADVNUMBUF64(cbuf, pkid, step); cbuf += step; csiz -= step; } if(csiz > 0){ int off, step; TCREADVNUMBUF(cbuf, off, step); cbuf += step; csiz -= step; off += diff; if(pkbuf){ unsigned int hash = 19780211; for(int k = 0; k < pksiz; k++){ hash = hash * 37 + ((unsigned char *)pkbuf)[k]; } hash = hash % TDBFTSBMNUM; if(j == 0 || TCBITMAPCHECK(pkmap, hash)){ if(sonum >= soanum){ soanum *= 2; TCREALLOC(socrs, socrs, soanum * sizeof(*socrs)); } TDBFTSSTROCR *ocr = socrs + sonum; ocr->pkbuf = pkbuf; ocr->pksiz = pksiz; ocr->off = off; ocr->seq = seq; ocr->hash = hash; sonum++; if(j == 0) TCBITMAPON(pkmap, hash); } } else { unsigned int hash = pkid % TDBFTSBMNUM; if(j == 0 || TCBITMAPCHECK(pkmap, hash)){ if(nonum >= noanum){ noanum *= 2; TCREALLOC(nocrs, nocrs, noanum * sizeof(*nocrs)); } TDBFTSNUMOCR *ocr = nocrs + nonum; ocr->pkid = pkid; ocr->off = off; ocr->seq = seq; ocr->hash = hash; nonum++; if(j == 0) TCBITMAPON(pkmap, hash); } } } } } seq++; if(sonum <= sobase && nonum <= nobase){ sonum = 0; nonum = 0; break; } } TCBITMAPDEL(pkmap); if(seq > 1){ if(sonum > UINT16_MAX){ int flnum = sonum * 16 + 1; TCBITMAP *flmap = TCBITMAPNEW(flnum); for(int j = sobase; j < sonum; j++){ TDBFTSSTROCR *ocr = socrs + j; uint32_t hash = (((uint32_t)ocr->off << 16) | ocr->hash) % flnum; TCBITMAPON(flmap, hash); } int wi = 0; for(int j = 0; j < sobase; j++){ TDBFTSSTROCR *ocr = socrs + j; int rem = (seq - ocr->seq - 1) * TDBIDXQGUNIT; uint32_t hash = (((uint32_t)(ocr->off + rem) << 16) | ocr->hash) % flnum; if(TCBITMAPCHECK(flmap, hash)) socrs[wi++] = *ocr; } for(int j = sobase; j < sonum; j++){ socrs[wi++] = socrs[j]; } sonum = wi; TCBITMAPDEL(flmap); } if(sonum > UINT16_MAX * 2){ TDBFTSSTROCR *rocrs; TCMALLOC(rocrs, sizeof(*rocrs) * sonum); uint32_t *counts; TCCALLOC(counts, sizeof(*counts), (UINT16_MAX + 1)); for(int j = 0; j < sonum; j++){ counts[socrs[j].hash]++; } for(int j = 0; j < UINT16_MAX; j++){ counts[j+1] += counts[j]; } for(int j = sonum - 1; j >= 0; j--){ rocrs[--counts[socrs[j].hash]] = socrs[j]; } for(int j = 0; j < UINT16_MAX; j++){ int num = counts[j+1] - counts[j]; if(num > 1) qsort(rocrs + counts[j], num, sizeof(*rocrs), (int (*)(const void *, const void *))tctdbidxftscmpstrocr); } int num = sonum - counts[UINT16_MAX]; if(num > 1) qsort(rocrs + counts[UINT16_MAX], num, sizeof(*rocrs), (int (*)(const void *, const void *))tctdbidxftscmpstrocr); TCFREE(counts); TCFREE(socrs); socrs = rocrs; } else if(sonum > 1){ qsort(socrs, sonum, sizeof(*socrs), (int (*)(const void *, const void *))tctdbidxftscmpstrocr); } if(nonum > UINT16_MAX){ int flnum = nonum * 16 + 1; TCBITMAP *flmap = TCBITMAPNEW(flnum); for(int j = nobase; j < nonum; j++){ TDBFTSNUMOCR *ocr = nocrs + j; uint32_t hash = (((uint32_t)ocr->off << 16) | ocr->hash) % flnum; TCBITMAPON(flmap, hash); } int wi = 0; for(int j = 0; j < nobase; j++){ TDBFTSNUMOCR *ocr = nocrs + j; int rem = (seq - ocr->seq - 1) * TDBIDXQGUNIT; uint32_t hash = (((uint32_t)(ocr->off + rem) << 16) | ocr->hash) % flnum; if(TCBITMAPCHECK(flmap, hash)) nocrs[wi++] = *ocr; } for(int j = nobase; j < nonum; j++){ nocrs[wi++] = nocrs[j]; } nonum = wi; TCBITMAPDEL(flmap); } if(nonum > UINT16_MAX * 2){ TDBFTSNUMOCR *rocrs; TCMALLOC(rocrs, sizeof(*rocrs) * nonum); uint32_t *counts; TCCALLOC(counts, sizeof(*counts), (UINT16_MAX + 1)); for(int j = 0; j < nonum; j++){ counts[nocrs[j].hash]++; } for(int j = 0; j < UINT16_MAX; j++){ counts[j+1] += counts[j]; } for(int j = nonum - 1; j >= 0; j--){ rocrs[--counts[nocrs[j].hash]] = nocrs[j]; } for(int j = 0; j < UINT16_MAX; j++){ int num = counts[j+1] - counts[j]; if(num > 1) qsort(rocrs + counts[j], num, sizeof(*rocrs), (int (*)(const void *, const void *))tctdbidxftscmpnumocr); } int num = nonum - counts[UINT16_MAX]; if(num > 1) qsort(rocrs + counts[UINT16_MAX], num, sizeof(*rocrs), (int (*)(const void *, const void *))tctdbidxftscmpnumocr); TCFREE(counts); TCFREE(nocrs); nocrs = rocrs; } else if(nonum > 1){ qsort(nocrs, nonum, sizeof(*nocrs), (int (*)(const void *, const void *))tctdbidxftscmpnumocr); } } int rem = (seq - 1) * TDBIDXQGUNIT; int onum = 0; int ri = 0; while(ri < sonum){ TDBFTSSTROCR *ocr = socrs + ri; ri++; if(ocr->seq > 0) continue; const char *pkbuf = ocr->pkbuf; int32_t pksiz = ocr->pksiz; int32_t off = ocr->off; uint16_t seq = 1; for(int j = ri; j < sonum; j++){ TDBFTSSTROCR *tocr = socrs + j; if(!tocr->pkbuf || tocr->pksiz != pksiz || memcmp(tocr->pkbuf, pkbuf, pksiz) || tocr->off > off + TDBIDXQGUNIT) break; if(tocr->seq == seq && tocr->off == off + TDBIDXQGUNIT){ off = tocr->off; seq++; } } if(off == ocr->off + rem){ onum++; if(ores){ int rsiz; if(tcmapget(ores, pkbuf, pksiz, &rsiz)){ if(sign){ tcmapputkeep(nres, pkbuf, pksiz, "", 0); } else { tcmapout(ores, pkbuf, pksiz); } } } else { tcmapputkeep(nres, pkbuf, pksiz, "", 0); } while(ri < sonum){ ocr = socrs + ri; if(!ocr->pkbuf || ocr->pksiz != pksiz || memcmp(ocr->pkbuf, pkbuf, pksiz)) break; ri++; } } } ri = 0; while(ri < nonum){ TDBFTSNUMOCR *ocr = nocrs + ri; ri++; if(ocr->seq > 0) continue; int64_t pkid = ocr->pkid; int32_t off = ocr->off; uint16_t seq = 1; for(int j = ri; j < nonum; j++){ TDBFTSNUMOCR *tocr = nocrs + j; if(tocr->pkid != pkid || tocr->off > off + TDBIDXQGUNIT) break; if(tocr->seq == seq && tocr->off == off + TDBIDXQGUNIT){ off = tocr->off; seq++; } } if(off == ocr->off + rem){ onum++; char pkbuf[TCNUMBUFSIZ]; int pksiz = sprintf(pkbuf, "%lld", (long long)pkid); if(ores){ int rsiz; if(tcmapget(ores, pkbuf, pksiz, &rsiz)){ if(sign){ tcmapputkeep(nres, pkbuf, pksiz, "", 0); } else { tcmapout(ores, pkbuf, pksiz); } } } else { tcmapputkeep(nres, pkbuf, pksiz, "", 0); } while(ri < nonum && nocrs[ri].pkid == pkid){ ri++; } } } tcxstrprintf(hint, "token occurrence: \"%s\" %d\n", word, onum); TCFREE(nocrs); TCFREE(socrs); } else { int onum = 0; TCMAP *uniq = (i > 0 || ores) ? tcmapnew2(UINT16_MAX) : NULL; tcmapiterinit(cc); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(cc, &ksiz)) != NULL){ if(ksiz < wsiz || memcmp(kbuf, word, wsiz)) continue; int csiz; const char *cbuf = tcmapiterval(kbuf, &csiz); while(csiz > 0){ const char *pkbuf = NULL; int32_t pksiz = 0; int64_t pkid = 0; if(*cbuf == '\0'){ cbuf++; csiz--; int step; TCREADVNUMBUF(cbuf, pksiz, step); cbuf += step; csiz -= step; pkbuf = cbuf; cbuf += pksiz; csiz -= pksiz; } else { int step; TCREADVNUMBUF64(cbuf, pkid, step); cbuf += step; csiz -= step; } if(csiz > 0){ int off, step; TCREADVNUMBUF(cbuf, off, step); cbuf += step; csiz -= step; if(pkbuf){ if(ores){ int rsiz; if(tcmapget(ores, pkbuf, pksiz, &rsiz)){ if(sign){ tcmapputkeep(nres, pkbuf, pksiz, "", 0); } else { tcmapout(ores, pkbuf, pksiz); } } } else { if(tcmapputkeep(nres, pkbuf, pksiz, "", 0)) onum++; } if(uniq) tcmapputkeep(uniq, pkbuf, pksiz, "", 0); } else { char numbuf[TCNUMBUFSIZ]; int pksiz = sprintf(numbuf, "%lld", (long long)pkid); if(ores){ int rsiz; if(tcmapget(ores, numbuf, pksiz, &rsiz)){ if(sign){ tcmapputkeep(nres, numbuf, pksiz, "", 0); } else { tcmapout(ores, numbuf, pksiz); } } } else { if(tcmapputkeep(nres, numbuf, pksiz, "", 0)) onum++; } if(uniq) tcmapputkeep(uniq, numbuf, pksiz, "", 0); } } } } BDBCUR *cur = tcbdbcurnew(db); tcbdbcurjump(cur, word, wsiz); TCXSTR *key = tcxstrnew(); TCXSTR *val = tcxstrnew(); while(tcbdbcurrec(cur, key, val)){ const char *kbuf = TCXSTRPTR(key); int ksiz = TCXSTRSIZE(key); if(ksiz < wsiz || memcmp(kbuf, word, wsiz)) break; const char *cbuf = TCXSTRPTR(val); int csiz = TCXSTRSIZE(val); while(csiz > 0){ const char *pkbuf = NULL; int32_t pksiz = 0; int64_t pkid = 0; if(*cbuf == '\0'){ cbuf++; csiz--; int step; TCREADVNUMBUF(cbuf, pksiz, step); cbuf += step; csiz -= step; pkbuf = cbuf; cbuf += pksiz; csiz -= pksiz; } else { int step; TCREADVNUMBUF64(cbuf, pkid, step); cbuf += step; csiz -= step; } if(csiz > 0){ int off, step; TCREADVNUMBUF(cbuf, off, step); cbuf += step; csiz -= step; if(pkbuf){ if(ores){ int rsiz; if(tcmapget(ores, pkbuf, pksiz, &rsiz)){ if(sign){ tcmapputkeep(nres, pkbuf, pksiz, "", 0); } else { tcmapout(ores, pkbuf, pksiz); } } } else { if(tcmapputkeep(nres, pkbuf, pksiz, "", 0)) onum++; } if(uniq) tcmapputkeep(uniq, pkbuf, pksiz, "", 0); } else { char numbuf[TCNUMBUFSIZ]; int pksiz = sprintf(numbuf, "%lld", (long long)pkid); if(ores){ int rsiz; if(tcmapget(ores, numbuf, pksiz, &rsiz)){ if(sign){ tcmapputkeep(nres, numbuf, pksiz, "", 0); } else { tcmapout(ores, numbuf, pksiz); } } } else { if(tcmapputkeep(nres, numbuf, pksiz, "", 0)) onum++; } if(uniq) tcmapputkeep(uniq, numbuf, pksiz, "", 0); } } } tcbdbcurnext(cur); } tcxstrdel(val); tcxstrdel(key); tcbdbcurdel(cur); tcxstrprintf(hint, "token occurrence: \"%s\" %d\n", word, uniq ? (int)tcmaprnum(uniq) : onum); if(uniq) tcmapdel(uniq); } TCFREE(ary); } } /* Compare two string occurrences of full-text search in identical order. `a' specifies the pointer to one occurrence. `b' specifies the pointer to the other occurrence. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tctdbidxftscmpstrocr(TDBFTSSTROCR *a, TDBFTSSTROCR *b){ assert(a && b); if(a->pksiz > b->pksiz) return 1; if(a->pksiz < b->pksiz) return -1; int diff = memcmp(a->pkbuf, b->pkbuf, a->pksiz); if(diff != 0) return diff; return a->off - b->off; } /* Compare two number occurrences of full-text search in identical order. `a' specifies the pointer to one occurrence. `b' specifies the pointer to the other occurrence. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tctdbidxftscmpnumocr(TDBFTSNUMOCR *a, TDBFTSNUMOCR *b){ assert(a && b); if(a->pkid > b->pkid) return 1; if(a->pkid < b->pkid) return -1; return a->off - b->off; } /* Parse an expression of full-text search. `expr' specifies the expression. `esiz' specifies the size of the expression. `np' specifies the pointer to a variable into which the number of elements of the return value is assigned. `op' specifies the operation type. The return value is the pointer to the array of full-text search units. */ static TDBFTSUNIT *tctdbftsparseexpr(const char *expr, int esiz, int op, int *np){ assert(expr && esiz >= 0 && np); TDBFTSUNIT *ftsunits; TCMALLOC(ftsunits, TDBFTSUNITMAX * sizeof(*ftsunits)); int ftsnum = 0; uint16_t *ary; TCMALLOC(ary, sizeof(*ary) * esiz + 1); int anum; tcstrutftoucs(expr, ary, &anum); anum = tcstrucsnorm(ary, anum, TCUNSPACE | TCUNLOWER | TCUNNOACC | TCUNWIDTH); char *str; TCMALLOC(str, esiz + 1); tcstrucstoutf(ary, anum, str); if(op == TDBQCFTSPH){ TCLIST *tokens = tclistnew2(1); tclistpush2(tokens, str); ftsunits[ftsnum].tokens = tokens; ftsunits[ftsnum].sign = true; ftsnum++; } else if(op == TDBQCFTSAND){ TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); int tnum = TCLISTNUM(tokens); for(int i = 0; i < tnum && ftsnum < TDBFTSUNITMAX; i++){ const char *token = TCLISTVALPTR(tokens, i); if(*token == '\0') continue; TCLIST *ttokens = tclistnew2(1); tclistpush2(ttokens, token); ftsunits[ftsnum].tokens = ttokens; ftsunits[ftsnum].sign = true; ftsnum++; } tclistdel(tokens); } else if(op == TDBQCFTSOR){ TCLIST *tokens = tcstrsplit(expr, "\t\n\r ,"); int tnum = TCLISTNUM(tokens); TCLIST *ttokens = tclistnew2(tnum); for(int i = 0; i < tnum; i++){ const char *token = TCLISTVALPTR(tokens, i); if(*token == '\0') continue; tclistpush2(ttokens, token); } ftsunits[ftsnum].tokens = ttokens; ftsunits[ftsnum].sign = true; ftsnum++; tclistdel(tokens); } else if(op == TDBQCFTSEX){ TCLIST *tokens = tcstrtokenize(str); int op = 0; for(int i = 0; i < tclistnum(tokens); i++){ const char *token = TCLISTVALPTR(tokens, i); if(!strcmp(token, "&&")){ op = 0; } else if(!strcmp(token, "||")){ op = 1; } else if(!strcmp(token, "!!")){ op = 2; } else { if(op == 0 || op == 2){ if(ftsnum >= TDBFTSUNITMAX) break; TCLIST *ttokens = tclistnew2(2); tclistpush2(ttokens, token); ftsunits[ftsnum].tokens = ttokens; ftsunits[ftsnum].sign = op == 0; ftsnum++; } else if(op == 1){ if(ftsnum < 1){ ftsunits[ftsnum].tokens = tclistnew2(2); ftsunits[ftsnum].sign = op == 0; ftsnum++; } TCLIST *ttokens = ftsunits[ftsnum-1].tokens; tclistpush2(ttokens, token); } op = 0; } } tclistdel(tokens); } TCFREE(str); TCFREE(ary); *np = ftsnum; return ftsunits; } /* Perform dynamic defragmentation of a table database object. `tdb' specifies the table database object. `step' specifie the number of steps. If successful, the return value is true, else, it is false. */ static bool tctdbdefragimpl(TCTDB *tdb, int64_t step){ assert(tdb); bool err = false; TCHDB *hdb = tdb->hdb; TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; if(!tchdbdefrag(hdb, step)) err = true; for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: case TDBITTOKEN: case TDBITQGRAM: if(!tcbdbdefrag(idx->db, step)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } break; } } return !err; } /* Clear the cache of a table tree database object. `tdb' specifies the table tree database object. If successful, the return value is true, else, it is false. */ static bool tctdbcacheclearimpl(TCTDB *tdb){ assert(tdb); bool err = false; TCHDB *hdb = tdb->hdb; TDBIDX *idxs = tdb->idxs; int inum = tdb->inum; if(!tchdbcacheclear(hdb)) err = true; for(int i = 0; i < inum; i++){ TDBIDX *idx = idxs + i; switch(idx->type){ case TDBITLEXICAL: case TDBITDECIMAL: case TDBITTOKEN: case TDBITQGRAM: if(!tcbdbcacheclear(idx->db)){ tctdbsetecode(tdb, tcbdbecode(idx->db), __FILE__, __LINE__, __func__); err = true; } break; } } return !err; } /* Process each record atomically of a table database object. `tdb' specifies the table database object. `func' specifies the pointer to the iterator function called for each record. `op' specifies an arbitrary pointer to be given as a parameter of the iterator function. If successful, the return value is true, else, it is false. */ static bool tctdbforeachimpl(TCTDB *tdb, TCITER iter, void *op){ assert(tdb && iter); TCHDB *hdb = tdb->hdb; char *lkbuf = NULL; int lksiz = 0; char *pkbuf, stack[TDBPAGEBUFSIZ], *rbuf; int pksiz; const char *cbuf; int csiz; while((pkbuf = tchdbgetnext3(hdb, lkbuf, lksiz, &pksiz, &cbuf, &csiz)) != NULL){ if(pksiz < TDBPAGEBUFSIZ){ rbuf = stack; } else { TCMALLOC(rbuf, pksiz + 1); } memcpy(rbuf, pkbuf, pksiz); stack[pksiz] = '\0'; TCMAP *cols = tcmapload(cbuf, csiz); int zsiz; char *zbuf = tcstrjoin4(cols, &zsiz); bool rv = iter(rbuf, pksiz, zbuf, zsiz, op); TCFREE(zbuf); if(rbuf != stack) TCFREE(rbuf); tcmapdel(cols); TCFREE(lkbuf); lkbuf = pkbuf; lksiz = pksiz; if(!rv) break; } TCFREE(lkbuf); return true; } /* Answer to remove for each record of a query. `pkbuf' is ignored. `pksiz' is ignored. `op' is ignored. The return value is always `TDBQPOUT'. */ static int tctdbqryprocoutcb(const void *pkbuf, int pksiz, TCMAP *cols, void *op){ assert(pkbuf && pksiz >= 0 && cols); return TDBQPOUT; } /* 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 tctdblockmethod(TCTDB *tdb, bool wr){ assert(tdb); if(wr ? pthread_rwlock_wrlock(tdb->mmtx) != 0 : pthread_rwlock_rdlock(tdb->mmtx) != 0){ tctdbsetecode(tdb, 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 tctdbunlockmethod(TCTDB *tdb){ assert(tdb); if(pthread_rwlock_unlock(tdb->mmtx) != 0){ tctdbsetecode(tdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /************************************************************************************************* * debugging functions *************************************************************************************************/ /* Print meta data of the header into the debugging output. `tdb' specifies the table database object. */ void tctdbprintmeta(TCTDB *tdb){ assert(tdb); int dbgfd = tchdbdbgfd(tdb->hdb); if(dbgfd < 0) return; if(dbgfd == UINT16_MAX) dbgfd = 1; char buf[TDBPAGEBUFSIZ]; char *wp = buf; wp += sprintf(wp, "META:"); wp += sprintf(wp, " mmtx=%p", (void *)tdb->mmtx); wp += sprintf(wp, " hdb=%p", (void *)tdb->hdb); wp += sprintf(wp, " open=%d", tdb->open); wp += sprintf(wp, " wmode=%d", tdb->wmode); wp += sprintf(wp, " opts=%u", tdb->opts); wp += sprintf(wp, " lcnum=%d", tdb->lcnum); wp += sprintf(wp, " ncnum=%d", tdb->ncnum); wp += sprintf(wp, " iccmax=%lld", (long long)tdb->iccmax); wp += sprintf(wp, " iccsync=%f", tdb->iccsync); wp += sprintf(wp, " idxs=%p", (void *)tdb->idxs); wp += sprintf(wp, " inum=%d", tdb->inum); wp += sprintf(wp, " tran=%d", tdb->tran); *(wp++) = '\n'; tcwrite(dbgfd, buf, wp - buf); } // END OF FILE tokyocabinet-1.4.48/COPYING0000644000175000017500000006347610657566630014376 0ustar mikiomikio 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! tokyocabinet-1.4.48/bros/0000755000175000017500000000000012013574114014250 5ustar mikiomikiotokyocabinet-1.4.48/bros/sqltest.c0000644000175000017500000002501212013574114016113 0ustar mikiomikio#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 */ tokyocabinet-1.4.48/bros/sdbmtest.c0000644000175000017500000001115012013574114016237 0ustar mikiomikio/************************************************************************************************* * 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 */ tokyocabinet-1.4.48/bros/qdbmtest.c0000644000175000017500000002137012013574114016242 0ustar mikiomikio/************************************************************************************************* * 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 */ tokyocabinet-1.4.48/bros/Makefile0000644000175000017500000000666011137221572015723 0ustar mikiomikio# 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 tokyocabinet-1.4.48/bros/reporter0000755000175000017500000001172111043065157016046 0ustar mikiomikio#! /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 tokyocabinet-1.4.48/bros/gdbmtest.c0000644000175000017500000001205312013574114016226 0ustar mikiomikio/************************************************************************************************* * 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 */ tokyocabinet-1.4.48/bros/mapreporter0000755000175000017500000000372011105002406016527 0ustar mikiomikio#! /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 tokyocabinet-1.4.48/bros/ndbmtest.c0000644000175000017500000001116612013574114016241 0ustar mikiomikio/************************************************************************************************* * 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 */ tokyocabinet-1.4.48/bros/tdbtest.c0000644000175000017500000001135112013574114016066 0ustar mikiomikio/************************************************************************************************* * 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 */ tokyocabinet-1.4.48/bros/bdbtest.c0000644000175000017500000002532312013574114016050 0ustar mikiomikio/************************************************************************************************* * 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 */ tokyocabinet-1.4.48/bros/cmpsqltctest.c0000644000175000017500000001407312013574114017147 0ustar mikiomikio/************************************************************************************************* * 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 */ tokyocabinet-1.4.48/bros/result.xls0000644000175000017500000010600011106701752016315 0ustar mikiomikioÐÏࡱá>þÿ CþÿÿÿþÿÿÿBÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  ÍÉÀá°Áâ\p hirabayashi B°aÀ=œ¯¼=à;45,8X@"·Ú1 Üÿ€i-ÿ3ÿ 0ÿ´0·0Ã0¯01 Üÿ€i-ÿ3ÿ 0ÿ´0·0Ã0¯01 Üÿ€i-ÿ3ÿ 0ÿ´0·0Ã0¯01 Üÿ€i-ÿ3ÿ 0ÿ´0·0Ã0¯01 Üÿ€i-ÿ3ÿ 0ÿ´0·0Ã0¯01 xÿ€i-ÿ3ÿ 0ÿ´0·0Ã0¯01  ÿ€i-ÿ3ÿ 0ÿ´0·0Ã0¯01  ÿ€i-ÿ3ÿ 0ÿ´0·0Ã0¯01  ÿ€i-ÿ3ÿ 0ÿ´0·0Ã0¯01  ÿ€i-ÿ3ÿ 0ÿ´0·0Ã0¯01  ÿ€i-ÿ3ÿ 0ÿ´0·0Ã0¯01ÿ¼iCentury1ÈÿiCentury1Üÿ¼iCentury1 ÿiCentury1 Ü €i-ÿ3ÿ 0ÿ´0·0Ã0¯01 Ü$€i-ÿ3ÿ 0ÿ´0·0Ã0¯01 ‚ÿ€i-ÿ3ÿ 0ÿ´0·0Ã0¯01 }ÿ€i-ÿ3ÿ 0ÿ´0·0Ã0¯01 dÿ€i-ÿ3ÿ 0ÿ´0·0Ã0¯01 dÿ€i-ÿ3ÿ 0ÿ´0·0Ã0¯01È7iCentury"\"#,##0;"\"\-#,##0"\"#,##0;[Red]"\"\-#,##0"\"#,##0.00;"\"\-#,##0.00#"\"#,##0.00;[Red]"\"\-#,##0.007*2_ "\"* #,##0_ ;_ "\"* \-#,##0_ ;_ "\"* "-"_ ;_ @_ .))_ * #,##0_ ;_ * \-#,##0_ ;_ * "-"_ ;_ @_ ?,:_ "\"* #,##0.00_ ;_ "\"* \-#,##0.00_ ;_ "\"* "-"??_ ;_ @_ 6+1_ * #,##0.00_ ;_ * \-#,##0.00_ ;_ * "-"??_ ;_ @_ \$#,##0_);\(\$#,##0\)\$#,##0_);[Red]\(\$#,##0\) \$#,##0.00_);\(\$#,##0.00\)% \$#,##0.00_);[Red]\(\$#,##0.00\) °0.00_ ±0_  ²#,##0_  ³0.000_ àõÿ À àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ à À à õÿ øÀ àôÿôÀ à&õÿ øÀ à(õÿ øÀ àõÿ øÀ àõÿ øÀ àôÿôÀ à 1À à1À à 1À à 1À à 1|@ @  à1|@ @  à 1|@ @ 7 à ³#<@ @ À à ²#<@ @ À à³ #<@ @ À à À à1<À “€ÿ“€ÿ“€ÿ“€ÿ“€ÿ“€ÿ“€ÿ“€ ÿ`… šsheetŒQQ®  ;>ÁÁ`iëZðRð3 ð¿ À@@ñ  ÷ü¬))Benchmark Test of DBM BrothersResultNAMEQDBMNDBMSDBMGDBMTDBCDBBDB QDBM-BT-ASC QDBM-BT-RND BDB-BT-ASC BDB-BT-RND DESCRIPTION 5!Substitute Database Manager 1.0.2 5GNU Database Manager 1.8.3 5%B+ tree API of QDBM (ascending order) 5B+ tree API of QDBM (at random) 5 WRITE TIME 5 READ TIME 5Graph of Processing Time 5$B+ tree API of BDB (ascending order) 5B+ tree API of BDB (at random) 5Graph of File Size 5 FILE SIZE 5New Database Manager 5.1 5Trivial Database 1.0.6 5Tiny Constant Database 0.75 5–Unit of time is seconds. Unit of size is bytes. Read time of SDBM can not be calculated because its database is broken when more than 100000 records. 5 Quick Database Manager 1.8.77 5 TC-BT-ASC 5#B+ tree API of TC (ascending order) 5 TC-BT-RND 5B+ tree API of TC (at random) 5Berkeley DB 4.6.21 5ýThis benchmark test is to calculate processing time (real time) and file size of database. Writing test is to store 1,000,000 records. Reading test is to fetch all of its records. Both of the key and the value of each record are such 8-byte strings as `00000001', `00000002', `00000003'... Tuning parameters of each DBM are set to display its best performance. Platform: Linux 2.6.16 kernel, EXT3 file system (writeback), Intel Xeon quad core 2.3GHz CPU, 8GB RAM Compilation: gcc 4.2.3 (using -O3), glibc 2.7 5TC-FIXED 5Fixed-length API of TC 5TC 5Tokyo Cabinet 1.3.5 5ÿ2£ ö _’ û ~íV†ccj  ÍÉÀ .Ø~  dü©ñÒMbP?_*+‚€%ÿÁƒ„&×£p= ×ã?'Âõ(\â?(º\.—Ëåâ?)º\.—Ëåâ?MJPrimoPDFÜlSï š 4YXXA4PRIVB0''''˜lP4(ˆDµMÿÿ˜SMTJˆPrimoPDFResolution600dpiPageSizeA4PageRegionLeadingEdgeInputSlot*UseFormTrayTable¡" YXX ƒÁ`à? ƒÁ`à?U} €} € } }  .•@Z@@x@;@x@ÀÀÀ À À À À ÀÀÀÀÀÀÀÀÀ@Z@Y@x@ý ý !$¾!!!!ý ý ý ý ý ý ý 'ý (ºN#-•·Ù?Z!«[Õ?~ ¢' ý ý ðHP<@–>tA}Ëî?~ RŒ} ý ý  a‰”My@ }‘Жsi @ òÚÈAý ý  züÞ¦?@  *ãßg\8P?~ „ÂAý ý  ~R›8±2@ ‡P¥f @~ €•Aý ý  UDÝà@ ú›Pˆ€Cé?~ € ‰Aý ý  §/ú ÒÖ? eâX·×?~ ƒAý  ý #+Ù±ˆ7"@­Lø¥~Þ@~ €ÿƒAý ý  O;ü5Y£æ?yX¨5Í;ã?~ ìµý !ý "zÅrK+@³$@M-@~ ¶êüý  ý M¾ÙæÆtö?Ñ"Ûù~jî?~ ®J¯ ý  ý ‘'I×L>@óÒo_‡@~ n/Àý  ý ™ðKý¼©õ?/£Xni5÷?~ ¨‹Aý  ý Š«Ê¾+@ŬC9Q@~ p|Aý %ý &ðð&ð( ð ððp’ ð “ ð6¿NƒM¿ÀMÿ?¿ð+ð]` X   ÍÉÀƒ„MJPrimoPDFÜlSï€ š 4dXXA4PRIVB0''''˜lP4(ˆDµMÿÿ˜SMTJˆPrimoPDFResolution600dpiPageSizeLetterPageRegionLeadingEdgeInputSlotOnlyOne¡" dXXü©ñÒMbà?ü©ñÒMbà?3` ‚#Ü` ‚#Ü` ˆ(¥x` º-dè¿LÐß3 df²23 ÿÿ M ÿÿÿNM4 3Q1:  WRITE TIMEQ³ ;Q1 ;Qÿÿ3_ O €€€ÿÿÿ  MM<4E4 3Q1:  READ TIMEQ³ ;Q1 ;Qÿÿ3_ O ÀÀÀÿÿÿ  MM<4E4D$% æÿÿÿ»ÿÿÿ±M  3O&Q4$% æÿÿÿ»ÿÿÿ±M  3O&Q4FA&… 3OSRÂ3 bï&43*$@ð?ð?N±&! ÿÿ M4523 €€€ ÿÿÿNM43–" % Ûæ3O' E% æÿÿÿ»ÿÿÿ±Mð3O& Q423 ÿÿ M ÿÿÿNM44444eee ì~ðv’ ð £ ð<¿NƒM¿ÀMËÿ?¿ð->¦ð]`X   ÍÉÀƒ„MJPrimoPDFÜlSï€ š 4dXXA4PRIVB0''''˜lP4(ˆDµMÿÿ˜SMTJˆPrimoPDFResolution600dpiPageSizeLetterPageRegionLeadingEdgeInputSlotOnlyOne¡" dXXü©ñÒMbà?ü©ñÒMbà?3` ‚#Ü ` ‚#Ü ` {)ix` i-idè¿LÐÿÚ3 d23 ÿÿ M ÿÿÿNM4 3Q1:  FILE SIZEQ² ;Q1 ;Qÿÿ3_ O –––ÿÿÿ7  MM<4E4D$% æÿÿÿ¹ÿÿÿ±M  3O& Q4$% æÿÿÿ¹ÿÿÿ±M  3O& Q4FA&FÏ Í 3Oýw3 bï&43*„×—A&! ÿÿ M4523 €€€ ÿÿÿNM43–" 44% æÿÿÿ¹ÿÿÿñMP<3OQ'44eee >¶@Kdåï5  ÍáÁ¿Àâ\p hirabayashi [B¤bœsheet$ ;ÿÿ>=à;45,8X@"·Ú1Üÿ€i‚l‚r ‚oƒSƒVƒbƒN1Üÿ€i‚l‚r ‚oƒSƒVƒbƒN1Üÿ€i‚l‚r ‚oƒSƒVƒbƒN1Üÿ€i‚l‚r ‚oƒSƒVƒbƒN1Üÿ€i‚l‚r ‚oƒSƒVƒbƒN1xÿ€i‚l‚r ‚oƒSƒVƒbƒN1 ÿ€i‚l‚r ‚oƒSƒVƒbƒN1 ÿ€i‚l‚r ‚oƒSƒVƒbƒN1 ÿ€i‚l‚r ‚oƒSƒVƒbƒN1 ÿ€i‚l‚r ‚oƒSƒVƒbƒN1 ÿ€i‚l‚r ‚oƒSƒVƒbƒN1ÿ¼iCentury1ÈÿiCentury1Üÿ¼iCentury1 ÿiCentury1Ü €i‚l‚r ‚oƒSƒVƒbƒN1Ü$€i‚l‚r ‚oƒSƒVƒbƒN1‚ÿ€i‚l‚r ‚oƒSƒVƒbƒN1}ÿ€i‚l‚r ‚oƒSƒVƒbƒN1dÿ€i‚l‚r ‚oƒSƒVƒbƒN1dÿ€i‚l‚r ‚oƒSƒVƒbƒN1È7iCentury"\"#,##0;"\"\-#,##0"\"#,##0;[Red]"\"\-#,##0"\"#,##0.00;"\"\-#,##0.00!"\"#,##0.00;[Red]"\"\-#,##0.005*2_ "\"* #,##0_ ;_ "\"* \-#,##0_ ;_ "\"* "-"_ ;_ @_ ,))_ * #,##0_ ;_ * \-#,##0_ ;_ * "-"_ ;_ @_ =,:_ "\"* #,##0.00_ ;_ "\"* \-#,##0.00_ ;_ "\"* "-"??_ ;_ @_ 4+1_ * #,##0.00_ ;_ * \-#,##0.00_ ;_ * "-"??_ ;_ @_ \$#,##0_);\(\$#,##0\)\$#,##0_);[Red]\(\$#,##0\)\$#,##0.00_);\(\$#,##0.00\)# \$#,##0.00_);[Red]\(\$#,##0.00\) °0.00_ ±0_  ²#,##0_  ³0.000_ àõÿ À àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ àõÿ ôÀ à À à õÿ øÀ àôÿôÀ à&õÿ øÀ à(õÿ øÀ àõÿ øÀ àõÿ øÀ àôÿôÀ à 1À à1À à 1À à 1À à 1| A€I€@ à1| A€I€@ à 1|7 A€I€@ à ³#<À @€I€@ à ²#<À @€I€@ à³ #<À @€I€@ à À à1<À “€ÿ“ƒnƒCƒp[ƒŠƒ“ƒN“€ÿ“€ÿ“€ÿ“€ÿ“€ÿ“•\Ž¦Ï‚Ý‚ÌƒnƒCƒp[ƒŠƒ“ƒN’â8ÿÿÿÿÿÿÿÿÿÿÿÿ€€€€€€€€€ÀÀÀ€€€™™ÿ™3fÿÿÌÌÿÿffÿ€€fÌÌÌÿ€ÿÿÿÿÿÿ€€€€€ÿÌÿÌÿÿÌÿÌÿÿ™™Ìÿÿ™ÌÌ™ÿããã3fÿ3ÌÌ™ÌÿÌÿ™ÿfff™–––3f3™f333™3™3f33™333… + sheet  Í .1€  dü©ñÒMbP?_*+‚€%ÿŒQQÁƒ„&×£p= ×ã?'Âõ(\â?(º\.—Ëåâ?)º\.—Ëåâ?M PrimoPDFtÿÿÿÿÿÿÿÿœlSï š 4YXXA4ÿÌÌÿÿffÿ€€fÌÌÌÿ€ÿPRIVB0''''˜lP4(ˆDµMÿÿ˜SMTJˆPrimoPDFResolution600dpiPageSizeA4PageRegionLeadingEdgeInputSlot*UseFormTrayTable¡" YXX ƒÁ`à? ƒÁ`à?U} €} € } }   .•@Z@@x@;@x@ÀÀÀ À À À À ÀÀÀÀÀÀÀÀÀ@Z@Y@x@&Benchmark Test of DBM Brothers!ÿThis benchmark test is to calculate processing time (real time) and file size of database. Writing test is to store 1,000,000 records. Reading test is to fetch all of its records. Both of the key and the value of each record are such 8-byte strings as `0¾!!!!Result NAME DESCRIPTION WRITE TIME READ TIME FILE SIZE TCTokyo Cabinet 1.3.5ºN#-•·Ù?Z!«[Õ?~ ¢'  QDBM%Quick Database Manager 1.8.77ðHP<@–>tA}Ëî?~ RŒ}  NDBM New Database Manager 5.1 a‰”My@ }‘Жsi @ òÚÈA SDBM) !Substitute Database Manager 1.0.2 züÞ¦?@  *ãßg\8P?~ „ÂA GDBM" GNU Database Manager 1.8.3 ~R›8±2@ ‡P¥f @~ €•A TDB Trivial Database 1.0.6 UDÝà@ ú›Pˆ€Cé?~ € ‰A CDB# Tiny Constant Database 0.75 §/ú ÒÖ? eâX·×?~ ƒA BDBBerkeley DB 4.6.21+Ù±ˆ7"@­Lø¥~Þ@~ €ÿƒA TC-BT-ASC+#B+ tree API of TC (ascending order)O;ü5Y£æ?yX¨5Í;ã?~ ìµ TC-BT-RND%B+ tree API of TC (at random)zÅrK+@³$@M-@~ ¶êü QDBM-BT-ASC-%B+ tree API of QDBM (ascending order)M¾ÙæÆtö?Ñ"Ûù~jî?~ ®J¯  QDBM-BT-RND'B+ tree API of QDBM (at random)‘'I×L>@óÒo_‡@~ n/À BDB-BT-ASC,$B+ tree API of BDB (ascending order)™ðKý¼©õ?/£Xni5÷?~ ¨‹A BDB-BT-RND&B+ tree API of BDB (at random)Š«Ê¾+@ŬC9Q@~ p|ATC-FIXEDFixed-length API of TC+@A@œ5œW¸   ̓„M PrimoPDF+œlSï€ š 4dXXA4DBM can not be calculated becPRIVB0''''˜lP4(ˆDµMÿÿ˜SMTJˆPrimoPDFResolution600dpiPageSizeLetterPageRegionLeadingEdgeInputSlotOnlyOne¡" dXXü©ñÒMbà?ü©ñÒMbà?3è¿LÐß3 23 ÿÿ  ÿÿÿ4 3Q1:ÿÿ  WRITE TIMEQ³;ÿÿQ1;ÿÿÿÿ3   €€€ÿÿÿ   4E4 3Q1:ÿÿ  READ TIMEQ³;ÿÿQ1;ÿÿÿÿ3   ÀÀÀÿÿÿ   4E4D FA&… 3OSRÂ3 &43*$@ð?ð?N±&! ÿÿ 4523 €€€  ÿÿÿ43–" % Ûæ3O' E%æÿÿÿ»ÿÿÿ±3O& Q423 ÿÿ  ÿÿÿ44$%æÿÿÿ»ÿÿÿ±3O&Q4444  ]>->¦@A@äEpW¸   ̓„M PrimoPDF->¦œlSï€ š 4dXXA4DBM can not be calculated becPRIVB0''''˜lP4(ˆDµMÿÿ˜SMTJˆPrimoPDFResolution600dpiPageSizeLetterPageRegionLeadingEdgeInputSlotOnlyOne¡" dXXü©ñÒMbà?ü©ñÒMbà?3è¿LÐÿÚ3 23 ÿÿ  ÿÿÿ4 3Q1:ÿÿ  FILE SIZEQ²;ÿÿQ1;ÿÿÿÿ3   –––ÿÿÿ   4E4D FA&FÏ Í 3Oýw3 &43*„×—A&! ÿÿ 4523 €€€  ÿÿÿ43–" $%æÿÿÿ¹ÿÿÿ±3O& Q444%æÿÿÿ¹ÿÿÿñ3OQ'44  =à;45,8X> ¶«" àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ þÿà…ŸòùOh«‘+'³Ù0ô PX€œ° È Ô àì¤ Benchmark Test of DBM BrothersMikio Hirabayashi hirabayashiMicrosoft Excel@LýD/EÉ@‹¬h"Ä@€fm]/EÉþÿÕÍÕœ.“—+,ù®0È8@ LT \ ƒ¤ sheetsheet!Print_Area  ƒ[ƒNƒV[ƒg –¼‘O•t‚«ˆê—— þÿÿÿ !"#$%&'()*+,-./01þÿÿÿ3456789þÿÿÿ;<=>?@AþÿÿÿýÿÿÿDþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRoot EntryÿÿÿÿÿÿÿÿÀF€3Ën/EÉþÿÿÿBook ÿÿÿÿÿÿÿÿÿÿÿÿ|,WorkbookÿÿÿÿÿÿÿÿÈ4SummaryInformation(ÿÿÿÿ2DocumentSummaryInformation8ÿÿÿÿÿÿÿÿÿÿÿÿ:ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtokyocabinet-1.4.48/bros/tctest.c0000644000175000017500000004314712013574114015733 0ustar mikiomikio/************************************************************************************************* * 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 */ tokyocabinet-1.4.48/bros/maptest.cc0000644000175000017500000004214712013574114016244 0ustar mikiomikio/************************************************************************************************* * 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 tokyocabinet-1.4.48/bros/cdbtest.c0000644000175000017500000001131212013574114016042 0ustar mikiomikio#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 */ tokyocabinet-1.4.48/tchmgr.c0000644000175000017500000005430612013574446014753 0ustar mikiomikio/************************************************************************************************* * The command line utility of the hash database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" /* global variables */ const char *g_progname; // program name int g_dbgfd; // debugging output /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void printerr(TCHDB *hdb); static int printdata(const char *ptr, int size, bool px); static char *mygetline(FILE *ifp); static int runcreate(int argc, char **argv); static int runinform(int argc, char **argv); static int runput(int argc, char **argv); static int runout(int argc, char **argv); static int runget(int argc, char **argv); static int runlist(int argc, char **argv); static int runoptimize(int argc, char **argv); static int runimporttsv(int argc, char **argv); static int runversion(int argc, char **argv); static int proccreate(const char *path, int bnum, int apow, int fpow, int opts); static int procinform(const char *path, int omode); static int procput(const char *path, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int omode, int dmode); static int procout(const char *path, const char *kbuf, int ksiz, int omode); static int procget(const char *path, const char *kbuf, int ksiz, int omode, bool px, bool pz); static int proclist(const char *path, int omode, int max, bool pv, bool px, const char *fmstr); static int procoptimize(const char *path, int bnum, int apow, int fpow, int opts, int omode, bool df); static int procimporttsv(const char *path, const char *file, int omode, bool sc); static int procversion(void); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; g_dbgfd = -1; const char *ebuf = getenv("TCDBGFD"); if(ebuf) g_dbgfd = tcatoix(ebuf); if(argc < 2) usage(); int rv = 0; if(!strcmp(argv[1], "create")){ rv = runcreate(argc, argv); } else if(!strcmp(argv[1], "inform")){ rv = runinform(argc, argv); } else if(!strcmp(argv[1], "put")){ rv = runput(argc, argv); } else if(!strcmp(argv[1], "out")){ rv = runout(argc, argv); } else if(!strcmp(argv[1], "get")){ rv = runget(argc, argv); } else if(!strcmp(argv[1], "list")){ rv = runlist(argc, argv); } else if(!strcmp(argv[1], "optimize")){ rv = runoptimize(argc, argv); } else if(!strcmp(argv[1], "importtsv")){ rv = runimporttsv(argc, argv); } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){ rv = runversion(argc, argv); } else { usage(); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: the command line utility of the hash database API\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s create [-tl] [-td|-tb|-tt|-tx] path [bnum [apow [fpow]]]\n", g_progname); fprintf(stderr, " %s inform [-nl|-nb] path\n", g_progname); fprintf(stderr, " %s put [-nl|-nb] [-sx] [-dk|-dc|-dai|-dad] path key value\n", g_progname); fprintf(stderr, " %s out [-nl|-nb] [-sx] path key\n", g_progname); fprintf(stderr, " %s get [-nl|-nb] [-sx] [-px] [-pz] path key\n", g_progname); fprintf(stderr, " %s list [-nl|-nb] [-m num] [-pv] [-px] [-fm str] path\n", g_progname); fprintf(stderr, " %s optimize [-tl] [-td|-tb|-tt|-tx] [-tz] [-nl|-nb] [-df]" " path [bnum [apow [fpow]]]\n", g_progname); fprintf(stderr, " %s importtsv [-nl|-nb] [-sc] path [file]\n", g_progname); fprintf(stderr, " %s version\n", g_progname); fprintf(stderr, "\n"); exit(1); } /* print error information */ static void printerr(TCHDB *hdb){ const char *path = tchdbpath(hdb); int ecode = tchdbecode(hdb); fprintf(stderr, "%s: %s: %d: %s\n", g_progname, path ? path : "-", ecode, tchdberrmsg(ecode)); } /* print record data */ static int printdata(const char *ptr, int size, bool px){ int len = 0; while(size-- > 0){ if(px){ if(len > 0) putchar(' '); len += printf("%02X", *(unsigned char *)ptr); } else { putchar(*ptr); len++; } ptr++; } return len; } /* read a line from a file descriptor */ static char *mygetline(FILE *ifp){ int len = 0; int blen = 1024; char *buf = tcmalloc(blen + 1); bool end = true; int c; while((c = fgetc(ifp)) != EOF){ end = false; if(c == '\0') continue; if(blen <= len){ blen *= 2; buf = tcrealloc(buf, blen + 1); } if(c == '\n' || c == '\r') c = '\0'; buf[len++] = c; if(c == '\0') break; } if(end){ tcfree(buf); return NULL; } buf[len] = '\0'; return buf; } /* parse arguments of create command */ static int runcreate(int argc, char **argv){ char *path = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; int opts = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tl")){ opts |= HDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= HDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= HDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= HDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= HDBTEXCODEC; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path) usage(); int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = proccreate(path, bnum, apow, fpow, opts); return rv; } /* parse arguments of inform command */ static int runinform(int argc, char **argv){ char *path = NULL; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procinform(path, omode); return rv; } /* parse arguments of put command */ static int runput(int argc, char **argv){ char *path = NULL; char *key = NULL; char *value = NULL; int omode = 0; int dmode = 0; bool sx = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else if(!strcmp(argv[i], "-dk")){ dmode = -1; } else if(!strcmp(argv[i], "-dc")){ dmode = 1; } else if(!strcmp(argv[i], "-dai")){ dmode = 10; } else if(!strcmp(argv[i], "-dad")){ dmode = 11; } else if(!strcmp(argv[i], "-sx")){ sx = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!key){ key = argv[i]; } else if(!value){ value = argv[i]; } else { usage(); } } if(!path || !key || !value) usage(); char *kbuf, *vbuf; int ksiz, vsiz; if(sx){ kbuf = tchexdecode(key, &ksiz); vbuf = tchexdecode(value, &vsiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); vsiz = strlen(value); vbuf = tcmemdup(value, vsiz); } int rv = procput(path, kbuf, ksiz, vbuf, vsiz, omode, dmode); tcfree(vbuf); tcfree(kbuf); return rv; } /* parse arguments of out command */ static int runout(int argc, char **argv){ char *path = NULL; char *key = NULL; int omode = 0; bool sx = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else if(!strcmp(argv[i], "-sx")){ sx = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!key){ key = argv[i]; } else { usage(); } } if(!path || !key) usage(); int ksiz; char *kbuf; if(sx){ kbuf = tchexdecode(key, &ksiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); } int rv = procout(path, kbuf, ksiz, omode); tcfree(kbuf); return rv; } /* parse arguments of get command */ static int runget(int argc, char **argv){ char *path = NULL; char *key = NULL; int omode = 0; bool sx = false; bool px = false; bool pz = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else if(!strcmp(argv[i], "-sx")){ sx = true; } else if(!strcmp(argv[i], "-px")){ px = true; } else if(!strcmp(argv[i], "-pz")){ pz = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!key){ key = argv[i]; } else { usage(); } } if(!path || !key) usage(); int ksiz; char *kbuf; if(sx){ kbuf = tchexdecode(key, &ksiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); } int rv = procget(path, kbuf, ksiz, omode, px, pz); tcfree(kbuf); return rv; } /* parse arguments of list command */ static int runlist(int argc, char **argv){ char *path = NULL; int omode = 0; int max = -1; bool pv = false; bool px = false; char *fmstr = NULL; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else if(!strcmp(argv[i], "-m")){ if(++i >= argc) usage(); max = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-pv")){ pv = true; } else if(!strcmp(argv[i], "-px")){ px = true; } else if(!strcmp(argv[i], "-fm")){ if(++i >= argc) usage(); fmstr = argv[i]; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = proclist(path, omode, max, pv, px, fmstr); return rv; } /* parse arguments of optimize command */ static int runoptimize(int argc, char **argv){ char *path = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; int opts = UINT8_MAX; int omode = 0; bool df = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tl")){ if(opts == UINT8_MAX) opts = 0; opts |= HDBTLARGE; } else if(!strcmp(argv[i], "-td")){ if(opts == UINT8_MAX) opts = 0; opts |= HDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ if(opts == UINT8_MAX) opts = 0; opts |= HDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ if(opts == UINT8_MAX) opts = 0; opts |= HDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ if(opts == UINT8_MAX) opts = 0; opts |= HDBTEXCODEC; } else if(!strcmp(argv[i], "-tz")){ if(opts == UINT8_MAX) opts = 0; } else if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else if(!strcmp(argv[i], "-df")){ df = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path) usage(); int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = procoptimize(path, bnum, apow, fpow, opts, omode, df); return rv; } /* parse arguments of importtsv command */ static int runimporttsv(int argc, char **argv){ char *path = NULL; char *file = NULL; int omode = 0; bool sc = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else if(!strcmp(argv[i], "-sc")){ sc = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!file){ file = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procimporttsv(path, file, omode, sc); return rv; } /* parse arguments of version command */ static int runversion(int argc, char **argv){ int rv = procversion(); return rv; } /* perform create command */ static int proccreate(const char *path, int bnum, int apow, int fpow, int opts){ TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(hdb); if(!tchdbtune(hdb, bnum, apow, fpow, opts)){ printerr(hdb); tchdbdel(hdb); return 1; } if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC)){ printerr(hdb); tchdbdel(hdb); return 1; } bool err = false; if(!tchdbclose(hdb)){ printerr(hdb); err = true; } tchdbdel(hdb); return err ? 1 : 0; } /* perform inform command */ static int procinform(const char *path, int omode){ TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL); if(!tchdbopen(hdb, path, HDBOREADER | omode)){ printerr(hdb); tchdbdel(hdb); return 1; } bool err = false; const char *npath = tchdbpath(hdb); if(!npath) npath = "(unknown)"; printf("path: %s\n", npath); const char *type = "(unknown)"; switch(tchdbtype(hdb)){ case TCDBTHASH: type = "hash"; break; case TCDBTBTREE: type = "btree"; break; case TCDBTFIXED: type = "fixed"; break; case TCDBTTABLE: type = "table"; break; } printf("database type: %s\n", type); uint8_t flags = tchdbflags(hdb); printf("additional flags:"); if(flags & HDBFOPEN) printf(" open"); if(flags & HDBFFATAL) printf(" fatal"); printf("\n"); printf("bucket number: %llu\n", (unsigned long long)tchdbbnum(hdb)); if(hdb->cnt_writerec >= 0) printf("used bucket number: %lld\n", (long long)tchdbbnumused(hdb)); printf("alignment: %u\n", tchdbalign(hdb)); printf("free block pool: %u\n", tchdbfbpmax(hdb)); printf("inode number: %lld\n", (long long)tchdbinode(hdb)); char date[48]; tcdatestrwww(tchdbmtime(hdb), INT_MAX, date); printf("modified time: %s\n", date); uint8_t opts = tchdbopts(hdb); printf("options:"); if(opts & HDBTLARGE) printf(" large"); if(opts & HDBTDEFLATE) printf(" deflate"); if(opts & HDBTBZIP) printf(" bzip"); if(opts & HDBTTCBS) printf(" tcbs"); if(opts & HDBTEXCODEC) printf(" excodec"); printf("\n"); printf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb)); printf("file size: %llu\n", (unsigned long long)tchdbfsiz(hdb)); if(!tchdbclose(hdb)){ if(!err) printerr(hdb); err = true; } tchdbdel(hdb); return err ? 1 : 0; } /* perform put command */ static int procput(const char *path, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int omode, int dmode){ TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(hdb); if(!tchdbopen(hdb, path, HDBOWRITER | omode)){ printerr(hdb); tchdbdel(hdb); return 1; } bool err = false; int inum; double dnum; switch(dmode){ case -1: if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz)){ printerr(hdb); err = true; } break; case 1: if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){ printerr(hdb); err = true; } break; case 10: inum = tchdbaddint(hdb, kbuf, ksiz, tcatoi(vbuf)); if(inum == INT_MIN){ printerr(hdb); err = true; } else { printf("%d\n", inum); } break; case 11: dnum = tchdbadddouble(hdb, kbuf, ksiz, tcatof(vbuf)); if(isnan(dnum)){ printerr(hdb); err = true; } else { printf("%.6f\n", dnum); } break; default: if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){ printerr(hdb); err = true; } break; } if(!tchdbclose(hdb)){ if(!err) printerr(hdb); err = true; } tchdbdel(hdb); return err ? 1 : 0; } /* perform out command */ static int procout(const char *path, const char *kbuf, int ksiz, int omode){ TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(hdb); if(!tchdbopen(hdb, path, HDBOWRITER | omode)){ printerr(hdb); tchdbdel(hdb); return 1; } bool err = false; if(!tchdbout(hdb, kbuf, ksiz)){ printerr(hdb); err = true; } if(!tchdbclose(hdb)){ if(!err) printerr(hdb); err = true; } tchdbdel(hdb); return err ? 1 : 0; } /* perform get command */ static int procget(const char *path, const char *kbuf, int ksiz, int omode, bool px, bool pz){ TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(hdb); if(!tchdbopen(hdb, path, HDBOREADER | omode)){ printerr(hdb); tchdbdel(hdb); return 1; } bool err = false; int vsiz; char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz); if(vbuf){ printdata(vbuf, vsiz, px); if(!pz) putchar('\n'); tcfree(vbuf); } else { printerr(hdb); err = true; } if(!tchdbclose(hdb)){ if(!err) printerr(hdb); err = true; } tchdbdel(hdb); return err ? 1 : 0; } /* perform list command */ static int proclist(const char *path, int omode, int max, bool pv, bool px, const char *fmstr){ TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(hdb); if(!tchdbopen(hdb, path, HDBOREADER | omode)){ printerr(hdb); tchdbdel(hdb); return 1; } bool err = false; if(fmstr){ TCLIST *keys = tchdbfwmkeys2(hdb, fmstr, max); for(int i = 0; i < tclistnum(keys); i++){ int ksiz; const char *kbuf = tclistval(keys, i, &ksiz); printdata(kbuf, ksiz, px); if(pv){ int vsiz; char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz); if(vbuf){ putchar('\t'); printdata(vbuf, vsiz, px); tcfree(vbuf); } } putchar('\n'); } tclistdel(keys); } else { if(!tchdbiterinit(hdb)){ printerr(hdb); err = true; } TCXSTR *key = tcxstrnew(); TCXSTR *val = tcxstrnew(); int cnt = 0; while(tchdbiternext3(hdb, key, val)){ printdata(tcxstrptr(key), tcxstrsize(key), px); if(pv){ putchar('\t'); printdata(tcxstrptr(val), tcxstrsize(val), px); } putchar('\n'); if(max >= 0 && ++cnt >= max) break; } tcxstrdel(val); tcxstrdel(key); } if(!tchdbclose(hdb)){ if(!err) printerr(hdb); err = true; } tchdbdel(hdb); return err ? 1 : 0; } /* perform optimize command */ static int procoptimize(const char *path, int bnum, int apow, int fpow, int opts, int omode, bool df){ TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(hdb); if(!tchdbopen(hdb, path, HDBOWRITER | omode)){ printerr(hdb); tchdbdel(hdb); return 1; } bool err = false; if(df){ if(!tchdbdefrag(hdb, INT64_MAX)){ printerr(hdb); err = true; } } else { if(!tchdboptimize(hdb, bnum, apow, fpow, opts)){ printerr(hdb); err = true; } } if(!tchdbclose(hdb)){ if(!err) printerr(hdb); err = true; } tchdbdel(hdb); return err ? 1 : 0; } /* perform importtsv command */ static int procimporttsv(const char *path, const char *file, int omode, bool sc){ FILE *ifp = file ? fopen(file, "rb") : stdin; if(!ifp){ fprintf(stderr, "%s: could not open\n", file ? file : "(stdin)"); return 1; } TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(hdb); if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | omode)){ printerr(hdb); tchdbdel(hdb); if(ifp != stdin) fclose(ifp); return 1; } bool err = false; char *line; int cnt = 0; while(!err && (line = mygetline(ifp)) != NULL){ char *pv = strchr(line, '\t'); if(!pv){ tcfree(line); continue; } *pv = '\0'; if(sc) tcstrutfnorm(line, TCUNSPACE | TCUNLOWER | TCUNNOACC | TCUNWIDTH); if(!tchdbput2(hdb, line, pv + 1)){ printerr(hdb); err = true; } tcfree(line); if(cnt > 0 && cnt % 100 == 0){ putchar('.'); fflush(stdout); if(cnt % 5000 == 0) printf(" (%08d)\n", cnt); } cnt++; } printf(" (%08d)\n", cnt); if(!tchdbclose(hdb)){ if(!err) printerr(hdb); err = true; } tchdbdel(hdb); if(ifp != stdin) fclose(ifp); return err ? 1 : 0; } /* perform version command */ static int procversion(void){ printf("Tokyo Cabinet version %s (%d:%s) for %s\n", tcversion, _TC_LIBVER, _TC_FORMATVER, TCSYSNAME); printf("Copyright (C) 2006-2012 FAL Labs\n"); return 0; } // END OF FILE tokyocabinet-1.4.48/tcatest.c0000644000175000017500000015170412013574446015136 0ustar mikiomikio/************************************************************************************************* * The test cases of the abstract database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" #define MULDIVNUM 8 // division number of multiple database #define RECBUFSIZ 48 // buffer for records /* global variables */ const char *g_progname; // program name unsigned int g_randseed; // random seed ADBSKEL g_skeleton; // skeleton database /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void iprintf(const char *format, ...); static void iputchar(int c); static void eprint(TCADB *adb, int line, const char *func); static void sysprint(void); static int myrand(int range); static void setskeltran(ADBSKEL *skel); static void *pdprocfunccmp(const void *vbuf, int vsiz, int *sp, void *op); static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op); static int runwrite(int argc, char **argv); static int runread(int argc, char **argv); static int runremove(int argc, char **argv); static int runrcat(int argc, char **argv); static int runmisc(int argc, char **argv); static int runwicked(int argc, char **argv); static int runcompare(int argc, char **argv); static int procwrite(const char *name, int rnum); static int procread(const char *name); static int procremove(const char *name); static int procrcat(const char *name, int rnum); static int procmisc(const char *name, int rnum); static int procwicked(const char *name, int rnum); static int proccompare(const char *name, int tnum, int rnum); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; const char *ebuf = getenv("TCRNDSEED"); g_randseed = ebuf ? tcatoix(ebuf) : tctime() * 1000; srand(g_randseed); if(argc < 2) usage(); int 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], "remove")){ rv = runremove(argc, argv); } else if(!strcmp(argv[1], "rcat")){ rv = runrcat(argc, argv); } else if(!strcmp(argv[1], "misc")){ rv = runmisc(argc, argv); } else if(!strcmp(argv[1], "wicked")){ rv = runwicked(argc, argv); } else if(!strcmp(argv[1], "compare")){ rv = runcompare(argc, argv); } else { usage(); } if(rv != 0){ printf("FAILED: TCRNDSEED=%u PID=%d", g_randseed, (int)getpid()); for(int i = 0; i < argc; i++){ printf(" %s", argv[i]); } printf("\n\n"); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: test cases of the abstract database API of Tokyo Cabinet\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s write name rnum\n", g_progname); fprintf(stderr, " %s read name\n", g_progname); fprintf(stderr, " %s remove name\n", g_progname); fprintf(stderr, " %s rcat name rnum\n", g_progname); fprintf(stderr, " %s misc name rnum\n", g_progname); fprintf(stderr, " %s wicked name rnum\n", g_progname); fprintf(stderr, " %s compare name tnum 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); } /* print a character and flush the buffer */ static void iputchar(int c){ putchar(c); fflush(stdout); } /* print error message of abstract database */ static void eprint(TCADB *adb, int line, const char *func){ const char *path = adb ? tcadbpath(adb) : NULL; fprintf(stderr, "%s: %s: %d: %s: error\n", g_progname, path ? path : "-", line, func); } /* print system information */ static void sysprint(void){ TCMAP *info = tcsysinfo(); if(info){ tcmapiterinit(info); const char *kbuf; while((kbuf = tcmapiternext2(info)) != NULL){ iprintf("sys_%s: %s\n", kbuf, tcmapiterval2(kbuf)); } tcmapdel(info); } } /* get a random number */ static int myrand(int range){ if(range < 2) return 0; int high = (unsigned int)rand() >> 4; int low = range * (rand() / (RAND_MAX + 1.0)); low &= (unsigned int)INT_MAX >> 4; return (high + low) % range; } /* set the transparent skeleton database */ static void setskeltran(ADBSKEL *skel){ memset(skel, 0, sizeof(*skel)); skel->opq = tcadbnew(); skel->del = (void (*)(void *))tcadbdel; skel->open = (bool (*)(void *, const char *))tcadbopen; skel->close = (bool (*)(void *))tcadbclose; skel->put = (bool (*)(void *, const void *, int, const void *, int))tcadbput; skel->putkeep = (bool (*)(void *, const void *, int, const void *, int))tcadbputkeep; skel->putcat = (bool (*)(void *, const void *, int, const void *, int))tcadbputcat; skel->out = (bool (*)(void *, const void *, int))tcadbout; skel->get = (void *(*)(void *, const void *, int, int *))tcadbget; skel->vsiz = (int (*)(void *, const void *, int))tcadbvsiz; skel->iterinit = (bool (*)(void *))tcadbiterinit; skel->iternext = (void *(*)(void *, int *))tcadbiternext; skel->fwmkeys = (TCLIST *(*)(void *, const void *, int, int))tcadbfwmkeys; skel->addint = (int (*)(void *, const void *, int, int))tcadbaddint; skel->adddouble = (double (*)(void *, const void *, int, double))tcadbadddouble; skel->sync = (bool (*)(void *))tcadbsync; skel->optimize = (bool (*)(void *, const char *))tcadboptimize; skel->vanish = (bool (*)(void *))tcadbvanish; skel->copy = (bool (*)(void *, const char *))tcadbcopy; skel->tranbegin = (bool (*)(void *))tcadbtranbegin; skel->trancommit = (bool (*)(void *))tcadbtrancommit; skel->tranabort = (bool (*)(void *))tcadbtranabort; skel->path = (const char *(*)(void *))tcadbpath; skel->rnum = (uint64_t (*)(void *))tcadbrnum; skel->size = (uint64_t (*)(void *))tcadbsize; skel->misc = (TCLIST *(*)(void *, const char *, const TCLIST *))tcadbmisc; skel->putproc = (bool (*)(void *, const void *, int, const void *, int, TCPDPROC, void *))tcadbputproc; skel->foreach = (bool (*)(void *, TCITER, void *))tcadbforeach; } /* duplication callback function for comparison */ static void *pdprocfunccmp(const void *vbuf, int vsiz, int *sp, void *op){ switch(*(int *)op % 4){ case 1: return NULL; case 2: return (void *)-1; default: break; } *sp = vsiz; return tcmemdup(vbuf, vsiz); } /* iterator function */ static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op){ unsigned int sum = 0; while(--ksiz >= 0){ sum += ((char *)kbuf)[ksiz]; } while(--vsiz >= 0){ sum += ((char *)vbuf)[vsiz]; } return myrand(100 + (sum & 0xff)) > 0; } /* parse arguments of write command */ static int runwrite(int argc, char **argv){ char *name = NULL; char *rstr = NULL; for(int 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(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int rv = procwrite(name, rnum); return rv; } /* parse arguments of read command */ static int runread(int argc, char **argv){ char *name = NULL; for(int i = 2; i < argc; i++){ if(!name && argv[i][0] == '-'){ usage(); } else if(!name){ name = argv[i]; } else { usage(); } } if(!name) usage(); int rv = procread(name); return rv; } /* parse arguments of remove command */ static int runremove(int argc, char **argv){ char *name = NULL; for(int i = 2; i < argc; i++){ if(!name && argv[i][0] == '-'){ usage(); } else if(!name){ name = argv[i]; } else { usage(); } } if(!name) usage(); int rv = procremove(name); return rv; } /* parse arguments of rcat command */ static int runrcat(int argc, char **argv){ char *name = NULL; char *rstr = NULL; for(int 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(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int rv = procrcat(name, rnum); return rv; } /* parse arguments of misc command */ static int runmisc(int argc, char **argv){ char *name = NULL; char *rstr = NULL; for(int 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(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int rv = procmisc(name, rnum); return rv; } /* parse arguments of wicked command */ static int runwicked(int argc, char **argv){ char *name = NULL; char *rstr = NULL; for(int 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(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int rv = procwicked(name, rnum); return rv; } /* parse arguments of compare command */ static int runcompare(int argc, char **argv){ char *name = NULL; char *tstr = NULL; char *rstr = NULL; for(int i = 2; i < argc; i++){ if(!name && argv[i][0] == '-'){ usage(); } else if(!name){ name = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!name || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int rv = proccompare(name, tnum, rnum); return rv; } /* perform write command */ static int procwrite(const char *name, int rnum){ iprintf("\n seed=%u name=%s rnum=%d\n\n", g_randseed, name, rnum); bool err = false; double stime = tctime(); TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ eprint(adb, __LINE__, "tcadbsetskel"); err = true; skel.del(skel.opq); } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, MULDIVNUM)){ eprint(adb, __LINE__, "tcadbsetskelmulti"); err = true; } name++; } if(!tcadbopen(adb, name)){ eprint(adb, __LINE__, "tcadbopen"); err = true; } for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", i); if(!tcadbput(adb, buf, len, buf, len)){ eprint(adb, __LINE__, "tcadbput"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb)); iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb)); sysprint(); if(!tcadbclose(adb)){ eprint(adb, __LINE__, "tcadbclose"); err = true; } tcadbdel(adb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform read command */ static int procread(const char *name){ iprintf("\n seed=%u name=%s\n\n", g_randseed, name); bool err = false; double stime = tctime(); TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ eprint(adb, __LINE__, "tcadbsetskel"); err = true; skel.del(skel.opq); } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, MULDIVNUM)){ eprint(adb, __LINE__, "tcadbsetskelmulti"); err = true; } name++; } if(!tcadbopen(adb, name)){ eprint(adb, __LINE__, "tcadbopen"); err = true; } int rnum = tcadbrnum(adb); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", i); int vsiz; char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz); if(!vbuf){ eprint(adb, __LINE__, "tcadbget"); err = true; break; } tcfree(vbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb)); iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb)); sysprint(); if(!tcadbclose(adb)){ eprint(adb, __LINE__, "tcadbclose"); err = true; } tcadbdel(adb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform remove command */ static int procremove(const char *name){ iprintf("\n seed=%u name=%s\n\n", g_randseed, name); bool err = false; double stime = tctime(); TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ eprint(adb, __LINE__, "tcadbsetskel"); err = true; skel.del(skel.opq); } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, MULDIVNUM)){ eprint(adb, __LINE__, "tcadbsetskelmulti"); err = true; } name++; } if(!tcadbopen(adb, name)){ eprint(adb, __LINE__, "tcadbopen"); err = true; } int rnum = tcadbrnum(adb); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", i); if(!tcadbout(adb, kbuf, ksiz)){ eprint(adb, __LINE__, "tcadbout"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb)); iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb)); sysprint(); if(!tcadbclose(adb)){ eprint(adb, __LINE__, "tcadbclose"); err = true; } tcadbdel(adb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform rcat command */ static int procrcat(const char *name, int rnum){ iprintf("\n seed=%u name=%s rnum=%d\n\n", g_randseed, name, rnum); int pnum = rnum / 5 + 1; bool err = false; double stime = tctime(); TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ eprint(adb, __LINE__, "tcadbsetskel"); err = true; skel.del(skel.opq); } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, MULDIVNUM)){ eprint(adb, __LINE__, "tcadbsetskelmulti"); err = true; } name++; } if(!tcadbopen(adb, name)){ eprint(adb, __LINE__, "tcadbopen"); err = true; } for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(pnum) + 1); if(!tcadbputcat(adb, kbuf, ksiz, kbuf, ksiz)){ eprint(adb, __LINE__, "tcadbputcat"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb)); iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb)); sysprint(); if(!tcadbclose(adb)){ eprint(adb, __LINE__, "tcadbclose"); err = true; } tcadbdel(adb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform misc command */ static int procmisc(const char *name, int rnum){ iprintf("\n seed=%u name=%s rnum=%d\n\n", g_randseed, name, rnum); bool err = false; double stime = tctime(); TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ eprint(adb, __LINE__, "tcadbsetskel"); err = true; skel.del(skel.opq); } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, MULDIVNUM)){ eprint(adb, __LINE__, "tcadbsetskelmulti"); err = true; } name++; } if(!tcadbopen(adb, name)){ eprint(adb, __LINE__, "tcadbopen"); err = true; } iprintf("writing:\n"); for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", i); if(!tcadbputkeep(adb, buf, len, buf, len)){ eprint(adb, __LINE__, "tcadbputkeep"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("reading:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", i); int vsiz; char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz); if(!vbuf){ eprint(adb, __LINE__, "tcadbget"); err = true; break; } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){ eprint(adb, __LINE__, "(validation)"); err = true; tcfree(vbuf); break; } tcfree(vbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(tcadbrnum(adb) != rnum){ eprint(adb, __LINE__, "(validation)"); err = true; } iprintf("random writing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum) + 1); char vbuf[RECBUFSIZ]; int vsiz = myrand(RECBUFSIZ); memset(vbuf, '*', vsiz); if(!tcadbput(adb, kbuf, ksiz, vbuf, vsiz)){ eprint(adb, __LINE__, "tcadbput"); err = true; break; } int rsiz; char *rbuf = tcadbget(adb, kbuf, ksiz, &rsiz); if(!rbuf){ eprint(adb, __LINE__, "tcadbget"); err = true; break; } if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(adb, __LINE__, "(validation)"); err = true; tcfree(rbuf); break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } tcfree(rbuf); } iprintf("word writing:\n"); const char *words[] = { "a", "A", "bb", "BB", "ccc", "CCC", "dddd", "DDDD", "eeeee", "EEEEEE", "mikio", "hirabayashi", "tokyo", "cabinet", "hyper", "estraier", "19780211", "birth day", "one", "first", "two", "second", "three", "third", "four", "fourth", "five", "fifth", "_[1]_", "uno", "_[2]_", "dos", "_[3]_", "tres", "_[4]_", "cuatro", "_[5]_", "cinco", "[\xe5\xb9\xb3\xe6\x9e\x97\xe5\xb9\xb9\xe9\x9b\x84]", "[\xe9\xa6\xac\xe9\xb9\xbf]", NULL }; for(int i = 0; words[i] != NULL; i += 2){ const char *kbuf = words[i]; int ksiz = strlen(kbuf); const char *vbuf = words[i+1]; int vsiz = strlen(vbuf); if(!tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz)){ eprint(adb, __LINE__, "tcadbputkeep"); err = true; break; } if(rnum > 250) iputchar('.'); } if(rnum > 250) iprintf(" (%08d)\n", (int)(sizeof(words) / sizeof(*words))); iprintf("random erasing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); tcadbout(adb, kbuf, ksiz); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("writing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "[%d]", i); char vbuf[RECBUFSIZ]; int vsiz = i % RECBUFSIZ; memset(vbuf, '*', vsiz); if(!tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz)){ eprint(adb, __LINE__, "tcadbputkeep"); err = true; break; } if(vsiz < 1){ char tbuf[PATH_MAX]; for(int j = 0; j < PATH_MAX; j++){ tbuf[j] = myrand(0x100); } if(!tcadbput(adb, kbuf, ksiz, tbuf, PATH_MAX)){ eprint(adb, __LINE__, "tcadbput"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("erasing:\n"); for(int i = 1; i <= rnum; i++){ if(i % 2 == 1){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "[%d]", i); if(!tcadbout(adb, kbuf, ksiz)){ eprint(adb, __LINE__, "tcadbout"); err = true; break; } tcadbout(adb, kbuf, ksiz); } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("checking iterator:\n"); if(!tcadbiterinit(adb)){ eprint(adb, __LINE__, "tcadbiterinit"); err = true; } char *kbuf; int ksiz; int inum = 0; for(int i = 1; (kbuf = tcadbiternext(adb, &ksiz)) != NULL; i++, inum++){ int vsiz; char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz); if(!vbuf){ eprint(adb, __LINE__, "tcadbget"); err = true; tcfree(kbuf); break; } tcfree(vbuf); tcfree(kbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); if(inum != tcadbrnum(adb)){ eprint(adb, __LINE__, "(validation)"); err = true; } iprintf("checking versatile functions:\n"); TCLIST *args = tclistnew(); for(int i = 1; i <= rnum; i++){ if(myrand(10) == 0){ const char *name; switch(myrand(3)){ default: name = "putlist"; break; case 1: name = "outlist"; break; case 2: name = "getlist"; break; } tclistclear(args); for(int j = myrand(4) * 2; j > 0; j--){ char abuf[RECBUFSIZ]; int asiz = sprintf(abuf, "%d", myrand(rnum) + 1); tclistpush(args, abuf, asiz); } TCLIST *rv = tcadbmisc(adb, name, args); if(rv){ tclistdel(rv); } else { eprint(adb, __LINE__, "tcadbmisc"); err = true; break; } } else { char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "(%d)", i); tclistpush(args, kbuf, ksiz); } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } tclistdel(args); args = tclistnew2(1); if(myrand(10) == 0){ TCLIST *rv = tcadbmisc(adb, "sync", args); if(rv){ tclistdel(rv); } else { eprint(adb, __LINE__, "tcadbmisc"); err = true; } } if(myrand(10) == 0){ TCLIST *rv = tcadbmisc(adb, "optimize", args); if(rv){ tclistdel(rv); } else { eprint(adb, __LINE__, "tcadbmisc"); err = true; } } if(myrand(10) == 0){ TCLIST *rv = tcadbmisc(adb, "vanish", args); if(rv){ tclistdel(rv); } else { eprint(adb, __LINE__, "tcadbmisc"); err = true; } } tclistdel(args); if(myrand(10) == 0 && !tcadbsync(adb)){ eprint(adb, __LINE__, "tcadbsync"); err = true; } if(myrand(10) == 0 && !tcadboptimize(adb, NULL)){ eprint(adb, __LINE__, "tcadboptimize"); err = true; } if(!tcadbvanish(adb)){ eprint(adb, __LINE__, "tcadbvanish"); err = true; } int omode = tcadbomode(adb); if(omode == ADBOHDB || omode == ADBOBDB || omode == ADBOFDB){ TCMAP *map = tcmapnew(); iprintf("random writing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); char vbuf[RECBUFSIZ]; int vsiz = sprintf(vbuf, "%d", myrand(rnum)); switch(myrand(4)){ case 0: if(!tcadbput(adb, kbuf, ksiz, vbuf, vsiz)){ eprint(adb, __LINE__, "tcadbput"); err = true; } tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz); tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 2: tcadbputcat(adb, kbuf, ksiz, vbuf, vsiz); tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 3: tcadbout(adb, kbuf, ksiz); tcmapout(map, kbuf, ksiz); break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("checking transaction commit:\n"); if(!tcadbtranbegin(adb)){ eprint(adb, __LINE__, "tcadbtranbegin"); err = true; } for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); char vbuf[RECBUFSIZ]; int vsiz = sprintf(vbuf, "[%d]", myrand(rnum)); switch(myrand(6)){ case 0: if(!tcadbput(adb, kbuf, ksiz, vbuf, vsiz)){ eprint(adb, __LINE__, "tcadbput"); err = true; } tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz); tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 2: tcadbputcat(adb, kbuf, ksiz, vbuf, vsiz); tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 3: tcadbaddint(adb, kbuf, ksiz, 1); tcmapaddint(map, kbuf, ksiz, 1); break; case 4: tcadbadddouble(adb, kbuf, ksiz, 1.0); tcmapadddouble(map, kbuf, ksiz, 1.0); break; case 5: tcadbout(adb, kbuf, ksiz); tcmapout(map, kbuf, ksiz); break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tcadbtrancommit(adb)){ eprint(adb, __LINE__, "tcadbtrancommit"); err = true; } iprintf("checking transaction abort:\n"); uint64_t ornum = tcadbrnum(adb); uint64_t osize = tcadbsize(adb); if(!tcadbtranbegin(adb)){ eprint(adb, __LINE__, "tcadbtranbegin"); err = true; } for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); char vbuf[RECBUFSIZ]; int vsiz = sprintf(vbuf, "((%d))", myrand(rnum)); switch(myrand(6)){ case 0: if(!tcadbput(adb, kbuf, ksiz, vbuf, vsiz)){ eprint(adb, __LINE__, "tcadbput"); err = true; } break; case 1: tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz); break; case 2: tcadbputcat(adb, kbuf, ksiz, vbuf, vsiz); break; case 3: tcadbaddint(adb, kbuf, ksiz, 1); break; case 4: tcadbadddouble(adb, kbuf, ksiz, 1.0); break; case 5: tcadbout(adb, kbuf, ksiz); break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tcadbtranabort(adb)){ eprint(adb, __LINE__, "tcadbtranabort"); err = true; } iprintf("checking consistency:\n"); if(tcadbrnum(adb) != ornum || tcadbsize(adb) != osize || tcadbrnum(adb) != tcmaprnum(map)){ eprint(adb, __LINE__, "(validation)"); err = true; } inum = 0; tcmapiterinit(map); const char *tkbuf; int tksiz; for(int i = 1; (tkbuf = tcmapiternext(map, &tksiz)) != NULL; i++, inum++){ int tvsiz; const char *tvbuf = tcmapiterval(tkbuf, &tvsiz); int rsiz; char *rbuf = tcadbget(adb, tkbuf, tksiz, &rsiz); if(!rbuf || rsiz != tvsiz || memcmp(rbuf, tvbuf, rsiz)){ eprint(adb, __LINE__, "(validation)"); err = true; tcfree(rbuf); break; } tcfree(rbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); inum = 0; if(!tcadbiterinit(adb)){ eprint(adb, __LINE__, "tcadbiterinit"); err = true; } for(int i = 1; (kbuf = tcadbiternext(adb, &ksiz)) != NULL; i++, inum++){ int vsiz; char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz); int rsiz; const char *rbuf = tcmapget(map, kbuf, ksiz, &rsiz); if(!rbuf || rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(adb, __LINE__, "(validation)"); err = true; tcfree(vbuf); tcfree(kbuf); break; } tcfree(vbuf); tcfree(kbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); tcmapdel(map); if(!tcadbvanish(adb)){ eprint(adb, __LINE__, "tcadbvanish"); err = true; } } if(!tcadbput2(adb, "mikio", "hirabayashi")){ eprint(adb, __LINE__, "tcadbput2"); err = true; } for(int i = 0; i < 10; i++){ char buf[RECBUFSIZ]; int size = sprintf(buf, "%d", myrand(rnum)); if(!tcadbput(adb, buf, size, buf, size)){ eprint(adb, __LINE__, "tcadbput"); err = true; } } for(int i = myrand(3) + 1; i < PATH_MAX; i = i * 2 + myrand(3)){ char vbuf[i]; memset(vbuf, '@', i - 1); vbuf[i-1] = '\0'; if(!tcadbput2(adb, "mikio", vbuf)){ eprint(adb, __LINE__, "tcadbput2"); err = true; } } if(!tcadbforeach(adb, iterfunc, NULL)){ eprint(adb, __LINE__, "tcadbforeach"); err = true; } iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb)); iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb)); sysprint(); if(!tcadbclose(adb)){ eprint(adb, __LINE__, "tcadbclose"); err = true; } tcadbdel(adb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform wicked command */ static int procwicked(const char *name, int rnum){ iprintf("\n seed=%u name=%s rnum=%d\n\n", g_randseed, name, rnum); bool err = false; double stime = tctime(); TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ eprint(adb, __LINE__, "tcadbsetskel"); err = true; skel.del(skel.opq); } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, MULDIVNUM)){ eprint(adb, __LINE__, "tcadbsetskelmulti"); err = true; } name++; } if(!tcadbopen(adb, name)){ eprint(adb, __LINE__, "tcadbopen"); err = true; } TCMAP *map = tcmapnew2(rnum / 5); for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum) + 1); char vbuf[RECBUFSIZ]; int vsiz = myrand(RECBUFSIZ); memset(vbuf, '*', vsiz); vbuf[vsiz] = '\0'; char *rbuf; switch(myrand(16)){ case 0: iputchar('0'); if(!tcadbput(adb, kbuf, ksiz, vbuf, vsiz)){ eprint(adb, __LINE__, "tcadbput"); err = true; } tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: iputchar('1'); if(!tcadbput2(adb, kbuf, vbuf)){ eprint(adb, __LINE__, "tcadbput2"); err = true; } tcmapput2(map, kbuf, vbuf); break; case 2: iputchar('2'); tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz); tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 3: iputchar('3'); tcadbputkeep2(adb, kbuf, vbuf); tcmapputkeep2(map, kbuf, vbuf); break; case 4: iputchar('4'); if(!tcadbputcat(adb, kbuf, ksiz, vbuf, vsiz)){ eprint(adb, __LINE__, "tcadbputcat"); err = true; } tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 5: iputchar('5'); if(!tcadbputcat2(adb, kbuf, vbuf)){ eprint(adb, __LINE__, "tcadbputcat2"); err = true; } tcmapputcat2(map, kbuf, vbuf); break; case 6: iputchar('6'); if(myrand(10) == 0){ tcadbout(adb, kbuf, ksiz); tcmapout(map, kbuf, ksiz); } break; case 7: iputchar('7'); if(myrand(10) == 0){ tcadbout2(adb, kbuf); tcmapout2(map, kbuf); } break; case 8: iputchar('8'); if((rbuf = tcadbget(adb, kbuf, ksiz, &vsiz)) != NULL) tcfree(rbuf); break; case 9: iputchar('9'); if((rbuf = tcadbget2(adb, kbuf)) != NULL) tcfree(rbuf); break; case 10: iputchar('A'); tcadbvsiz(adb, kbuf, ksiz); break; case 11: iputchar('B'); tcadbvsiz2(adb, kbuf); break; case 12: iputchar('E'); if(myrand(rnum / 50) == 0){ if(!tcadbiterinit(adb)){ eprint(adb, __LINE__, "tcadbiterinit"); err = true; } } for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){ int iksiz; char *ikbuf = tcadbiternext(adb, &iksiz); if(ikbuf) tcfree(ikbuf); } break; default: iputchar('@'); if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX); break; } if(i % 50 == 0) iprintf(" (%08d)\n", i); } if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum); tcadbsync(adb); if(tcadbrnum(adb) != tcmaprnum(map)){ eprint(adb, __LINE__, "(validation)"); err = true; } for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", i - 1); int vsiz; const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz); int rsiz; char *rbuf = tcadbget(adb, kbuf, ksiz, &rsiz); if(vbuf){ iputchar('.'); if(!rbuf){ eprint(adb, __LINE__, "tcadbget"); err = true; } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(adb, __LINE__, "(validation)"); err = true; } } else { iputchar('*'); if(rbuf){ eprint(adb, __LINE__, "(validation)"); err = true; } } tcfree(rbuf); if(i % 50 == 0) iprintf(" (%08d)\n", i); } if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum); tcmapiterinit(map); int ksiz; const char *kbuf; for(int i = 1; (kbuf = tcmapiternext(map, &ksiz)) != NULL; i++){ iputchar('+'); int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); int rsiz; char *rbuf = tcadbget(adb, kbuf, ksiz, &rsiz); if(!rbuf){ eprint(adb, __LINE__, "tcadbget"); err = true; } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(adb, __LINE__, "(validation)"); err = true; } tcfree(rbuf); if(!tcadbout(adb, kbuf, ksiz)){ eprint(adb, __LINE__, "tcadbout"); err = true; } if(i % 50 == 0) iprintf(" (%08d)\n", i); } int mrnum = tcmaprnum(map); if(mrnum % 50 > 0) iprintf(" (%08d)\n", mrnum); if(tcadbrnum(adb) != 0){ eprint(adb, __LINE__, "(validation)"); err = true; } iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb)); iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb)); sysprint(); tcmapdel(map); if(!tcadbclose(adb)){ eprint(adb, __LINE__, "tcadbclose"); err = true; } tcadbdel(adb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform compare command */ static int proccompare(const char *name, int tnum, int rnum){ iprintf("\n seed=%u name=%s tnum=%d rnum=%d\n\n", g_randseed, name, tnum, rnum); bool err = false; double stime = tctime(); char path[PATH_MAX]; TCMDB *mdb = tcmdbnew2(rnum / 2); TCNDB *ndb = tcndbnew(); TCHDB *hdb = tchdbnew(); tchdbsetdbgfd(hdb, UINT16_MAX); int hopts = 0; if(myrand(2) == 1) hopts |= HDBTLARGE; if(myrand(2) == 1) hopts |= HDBTBZIP; if(!tchdbtune(hdb, rnum / 2, -1, -1, hopts)){ eprint(NULL, __LINE__, "tchdbtune"); err = true; } if(!tchdbsetcache(hdb, rnum / 10)){ eprint(NULL, __LINE__, "tchdbsetcache"); err = true; } if(!tchdbsetxmsiz(hdb, 4096)){ eprint(NULL, __LINE__, "tchdbsetxmsiz"); err = true; } if(!tchdbsetdfunit(hdb, 8)){ eprint(NULL, __LINE__, "tchdbsetdfunit"); err = true; } sprintf(path, "%s.tch", name); int homode = HDBOWRITER | HDBOCREAT | HDBOTRUNC; if(myrand(100) == 1) homode |= HDBOTSYNC; if(!tchdbopen(hdb, path, homode)){ eprint(NULL, __LINE__, "tchdbopen"); err = true; } TCBDB *bdb = tcbdbnew(); tcbdbsetdbgfd(bdb, UINT16_MAX); int bopts = 0; if(myrand(2) == 1) bopts |= BDBTLARGE; if(myrand(2) == 1) bopts |= BDBTBZIP; if(!tcbdbtune(bdb, 5, 5, rnum / 10, -1, -1, bopts)){ eprint(NULL, __LINE__, "tcbdbtune"); err = true; } if(!tcbdbsetxmsiz(bdb, 4096)){ eprint(NULL, __LINE__, "tcbdbsetxmsiz"); err = true; } if(!tcbdbsetdfunit(bdb, 8)){ eprint(NULL, __LINE__, "tcbdbsetdfunit"); err = true; } sprintf(path, "%s.tcb", name); int bomode = BDBOWRITER | BDBOCREAT | BDBOTRUNC; if(myrand(100) == 1) bomode |= BDBOTSYNC; if(!tcbdbopen(bdb, path, bomode)){ eprint(NULL, __LINE__, "tcbdbopen"); err = true; } BDBCUR *cur = tcbdbcurnew(bdb); TCADB *adb = tcadbnew(); switch(myrand(4)){ case 0: sprintf(path, "%s.adb.tch#mode=wct#bnum=%d", name, rnum); break; case 1: sprintf(path, "%s.adb.tcb#mode=wct#lmemb=256#nmemb=512", name); break; case 2: sprintf(path, "+"); break; default: sprintf(path, "*"); break; } if(!tcadbopen(adb, path)){ eprint(NULL, __LINE__, "tcbdbopen"); err = true; } for(int t = 1; !err && t <= tnum; t++){ bool commit = myrand(2) == 0; iprintf("transaction %d (%s):\n", t, commit ? "commit" : "abort"); if(!tchdbtranbegin(hdb)){ eprint(NULL, __LINE__, "tchdbtranbegin"); err = true; } if(!tcbdbtranbegin(bdb)){ eprint(NULL, __LINE__, "tcbdbtranbegin"); err = true; } if(myrand(tnum) == 0){ bool all = myrand(2) == 0; for(int i = 1; !err && i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", all ? i : myrand(i) + 1); if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){ eprint(NULL, __LINE__, "tchdbout"); err = true; } if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){ eprint(NULL, __LINE__, "tcbdbout"); err = true; } tcadbout(adb, kbuf, ksiz); if(tchdbout(hdb, kbuf, ksiz) || tchdbecode(hdb) != TCENOREC){ eprint(NULL, __LINE__, "(validation)"); err = true; } if(tcbdbout(bdb, kbuf, ksiz) || tcbdbecode(bdb) != TCENOREC){ eprint(NULL, __LINE__, "(validation)"); err = true; } if(tcadbout(adb, kbuf, ksiz)){ eprint(NULL, __LINE__, "(validation)"); err = true; } if(commit){ tcmdbout(mdb, kbuf, ksiz); tcndbout(ndb, kbuf, ksiz); } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } } else { if(myrand(tnum + 1) == 0){ if(!tchdbcacheclear(hdb)){ eprint(NULL, __LINE__, "tchdbcacheclear"); err = true; } if(!tcbdbcacheclear(bdb)){ eprint(NULL, __LINE__, "tcbdbcacheclear"); err = true; } } int cldeno = tnum * (rnum / 2) + 1; int act = myrand(7); for(int i = 1; !err && i <= rnum; i++){ if(myrand(cldeno) == 0){ if(!tchdbcacheclear(hdb)){ eprint(NULL, __LINE__, "tchdbcacheclear"); err = true; } if(!tcbdbcacheclear(bdb)){ eprint(NULL, __LINE__, "tcbdbcacheclear"); err = true; } } if(myrand(10) == 0) act = myrand(7); char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(i) + 1); char vbuf[RECBUFSIZ+256]; int vsiz = sprintf(vbuf, "%64d:%d:%d", t, i, myrand(i)); switch(act){ case 0: if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(NULL, __LINE__, "tchdbput"); err = true; } if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(NULL, __LINE__, "tcbdbput"); err = true; } if(!tcadbput(adb, kbuf, ksiz, vbuf, vsiz)){ eprint(NULL, __LINE__, "tcadbput"); err = true; } if(commit){ tcmdbput(mdb, kbuf, ksiz, vbuf, vsiz); tcndbput(ndb, kbuf, ksiz, vbuf, vsiz); } break; case 1: if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz) && tchdbecode(hdb) != TCEKEEP){ eprint(NULL, __LINE__, "tchdbputkeep"); err = true; } if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz) && tcbdbecode(bdb) != TCEKEEP){ eprint(NULL, __LINE__, "tcbdbputkeep"); err = true; } tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz); if(commit){ tcmdbputkeep(mdb, kbuf, ksiz, vbuf, vsiz); tcndbputkeep(ndb, kbuf, ksiz, vbuf, vsiz); } break; case 2: if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(NULL, __LINE__, "tchdbputcat"); err = true; } if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(NULL, __LINE__, "tcbdbputcat"); err = true; } if(!tcadbputcat(adb, kbuf, ksiz, vbuf, vsiz)){ eprint(NULL, __LINE__, "tcadbputcat"); err = true; } if(commit){ tcmdbputcat(mdb, kbuf, ksiz, vbuf, vsiz); tcndbputcat(ndb, kbuf, ksiz, vbuf, vsiz); } break; case 3: if(tchdbaddint(hdb, kbuf, ksiz, 1) == INT_MIN && tchdbecode(hdb) != TCEKEEP){ eprint(NULL, __LINE__, "tchdbaddint"); err = true; } if(tcbdbaddint(bdb, kbuf, ksiz, 1) == INT_MIN && tcbdbecode(bdb) != TCEKEEP){ eprint(NULL, __LINE__, "tchdbaddint"); err = true; } tcadbaddint(adb, kbuf, ksiz, 1); if(commit){ tcmdbaddint(mdb, kbuf, ksiz, 1); tcndbaddint(ndb, kbuf, ksiz, 1); } break; case 4: if(isnan(tchdbadddouble(hdb, kbuf, ksiz, 1.0)) && tchdbecode(hdb) != TCEKEEP){ eprint(NULL, __LINE__, "tchdbadddouble"); err = true; } if(isnan(tcbdbadddouble(bdb, kbuf, ksiz, 1.0)) && tcbdbecode(bdb) != TCEKEEP){ eprint(NULL, __LINE__, "tchdbadddouble"); err = true; } tcadbadddouble(adb, kbuf, ksiz, 1.0); if(commit){ tcmdbadddouble(mdb, kbuf, ksiz, 1.0); tcndbadddouble(ndb, kbuf, ksiz, 1.0); } break; case 5: if(myrand(2) == 0){ if(!tchdbputproc(hdb, kbuf, ksiz, vbuf, vsiz, pdprocfunccmp, &i) && tchdbecode(hdb) != TCEKEEP){ eprint(NULL, __LINE__, "tchdbputproc"); err = true; } if(!tcbdbputproc(bdb, kbuf, ksiz, vbuf, vsiz, pdprocfunccmp, &i) && tcbdbecode(bdb) != TCEKEEP){ eprint(NULL, __LINE__, "tcbdbputproc"); err = true; } tcadbputproc(adb, kbuf, ksiz, vbuf, vsiz, pdprocfunccmp, &i); if(commit){ tcmdbputproc(mdb, kbuf, ksiz, vbuf, vsiz, pdprocfunccmp, &i); tcndbputproc(ndb, kbuf, ksiz, vbuf, vsiz, pdprocfunccmp, &i); } } else { if(!tchdbputproc(hdb, kbuf, ksiz, NULL, 0, pdprocfunccmp, &i) && tchdbecode(hdb) != TCEKEEP && tchdbecode(hdb) != TCENOREC){ eprint(NULL, __LINE__, "tchdbputproc"); err = true; } if(!tcbdbputproc(bdb, kbuf, ksiz, NULL, 0, pdprocfunccmp, &i) && tcbdbecode(bdb) != TCEKEEP && tchdbecode(hdb) != TCENOREC){ eprint(NULL, __LINE__, "tcbdbputproc"); err = true; } tcadbputproc(adb, kbuf, ksiz, NULL, 0, pdprocfunccmp, &i); if(commit){ tcmdbputproc(mdb, kbuf, ksiz, NULL, 0, pdprocfunccmp, &i); tcndbputproc(ndb, kbuf, ksiz, NULL, 0, pdprocfunccmp, &i); } } break; default: if(myrand(20) == 0){ if(!tcbdbcurjump(cur, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){ eprint(NULL, __LINE__, "tcbdbcurjump"); err = true; } char *cbuf; int csiz; while((cbuf = tcbdbcurkey(cur, &csiz)) != NULL){ if(!tchdbout(hdb, cbuf, csiz)){ eprint(NULL, __LINE__, "tchdbout"); err = true; } if(!tcbdbout(bdb, cbuf, csiz)){ eprint(NULL, __LINE__, "tcbdbout"); err = true; } tcadbout(adb, cbuf, csiz); if(commit){ tcmdbout(mdb, cbuf, csiz); tcndbout(ndb, cbuf, csiz); } tcfree(cbuf); if(myrand(10) == 0) break; switch(myrand(3)){ case 1: if(!tcbdbcurprev(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(NULL, __LINE__, "tcbdbcurprev"); err = true; } break; case 2: if(!tcbdbcurnext(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(NULL, __LINE__, "tcbdbcurprev"); err = true; } break; } } } else { if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){ eprint(NULL, __LINE__, "tchdbout"); err = true; } if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){ eprint(NULL, __LINE__, "tcbdbout"); err = true; } tcadbout(adb, kbuf, ksiz); if(commit){ tcmdbout(mdb, kbuf, ksiz); tcndbout(ndb, kbuf, ksiz); } } break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } } if(commit){ if(!tchdbtrancommit(hdb)){ eprint(NULL, __LINE__, "tchdbcommit"); err = true; } if(!tcbdbtrancommit(bdb)){ eprint(NULL, __LINE__, "tcbdbcommit"); err = true; } } else { if(myrand(5) == 0){ if(!tchdbclose(hdb)){ eprint(NULL, __LINE__, "tchdbclose"); err = true; } sprintf(path, "%s.tch", name); if(!tchdbopen(hdb, path, HDBOWRITER)){ eprint(NULL, __LINE__, "tchdbopen"); err = true; } if(!tcbdbclose(bdb)){ eprint(NULL, __LINE__, "tcbdbclose"); err = true; } sprintf(path, "%s.tcb", name); if(!tcbdbopen(bdb, path, BDBOWRITER)){ eprint(NULL, __LINE__, "tcbdbopen"); err = true; } } else { if(!tchdbtranabort(hdb)){ eprint(NULL, __LINE__, "tchdbtranabort"); err = true; } if(!tcbdbtranabort(bdb)){ eprint(NULL, __LINE__, "tcbdbtranabort"); err = true; } } } } iprintf("checking consistency of range:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", i); int vsiz; char *vbuf = tcmdbget(mdb, kbuf, ksiz, &vsiz); if(vbuf){ int rsiz; char *rbuf = tcndbget(ndb, kbuf, ksiz, &rsiz); if(rbuf){ tcfree(rbuf); } else { eprint(NULL, __LINE__, "tcndbget"); err = true; } rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz); if(rbuf){ tcfree(rbuf); } else { eprint(NULL, __LINE__, "tchdbget"); err = true; } rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz); if(rbuf){ tcfree(rbuf); } else { eprint(NULL, __LINE__, "tcbdbget"); err = true; } tcfree(vbuf); } else { int rsiz; char *rbuf = tcndbget(ndb, kbuf, ksiz, &rsiz); if(rbuf){ eprint(NULL, __LINE__, "tcndbget"); tcfree(rbuf); err = true; } rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz); if(rbuf){ eprint(NULL, __LINE__, "tchdbget"); err = true; tcfree(rbuf); } rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz); if(rbuf){ eprint(NULL, __LINE__, "tcbdbget"); err = true; tcfree(rbuf); } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("checking consistency on memory:\n"); if(tchdbrnum(hdb) != tcbdbrnum(bdb)){ eprint(NULL, __LINE__, "(validation)"); err = true; } int inum = 0; tcmdbiterinit(mdb); char *kbuf; int ksiz; for(int i = 1; (kbuf = tcmdbiternext(mdb, &ksiz)) != NULL; i++, inum++){ int vsiz; char *vbuf = tcmdbget(mdb, kbuf, ksiz, &vsiz); int rsiz; char *rbuf = tcndbget(ndb, kbuf, ksiz, &rsiz); if(!vbuf || !rbuf || rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(NULL, __LINE__, "tcndbget"); err = true; } tcfree(rbuf); rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz); if(!rbuf || rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(NULL, __LINE__, "tchdbget"); err = true; } tcfree(rbuf); rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz); if(!rbuf || rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(NULL, __LINE__, "tcbdbget"); err = true; } tcfree(rbuf); tcfree(vbuf); tcfree(kbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); iprintf("checking consistency of hash:\n"); inum = 0; if(!tchdbiterinit(hdb)){ eprint(NULL, __LINE__, "tchdbiterinit"); err = true; } for(int i = 1; (kbuf = tchdbiternext(hdb, &ksiz)) != NULL; i++, inum++){ int vsiz; char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz); int rsiz; char *rbuf = tcmdbget(mdb, kbuf, ksiz, &rsiz); if(!vbuf || !rbuf || rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(NULL, __LINE__, "(validation)"); err = true; } tcfree(rbuf); tcfree(vbuf); tcfree(kbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); iprintf("checking consistency of tree:\n"); if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(NULL, __LINE__, "tcbdbcurfirst"); err = true; } for(int i = 1; (kbuf = tcbdbcurkey(cur, &ksiz)) != NULL; i++, inum++){ int vsiz; char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz); int rsiz; char *rbuf = tcndbget(ndb, kbuf, ksiz, &rsiz); if(!vbuf || !rbuf || rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(NULL, __LINE__, "(validation)"); err = true; } tcfree(rbuf); tcfree(vbuf); tcfree(kbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } tcbdbcurnext(cur); } if(rnum > 250) iprintf(" (%08d)\n", inum); if(!tcadbclose(adb)){ eprint(NULL, __LINE__, "tcadbclose"); err = true; } tcbdbcurdel(cur); if(!tcbdbclose(bdb)){ eprint(NULL, __LINE__, "tcbdbclose"); err = true; } if(!tchdbclose(hdb)){ eprint(NULL, __LINE__, "tcbdbclose"); err = true; } tcadbdel(adb); tcmdbdel(mdb); tcndbdel(ndb); tchdbdel(hdb); tcbdbdel(bdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } // END OF FILE tokyocabinet-1.4.48/tcbmgr.c0000644000175000017500000006753512013574446014755 0ustar mikiomikio/************************************************************************************************* * The command line utility of the B+ tree database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" /* global variables */ const char *g_progname; // program name int g_dbgfd; // debugging output /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void printerr(TCBDB *bdb); static int printdata(const char *ptr, int size, bool px); static char *mygetline(FILE *ifp); static int mycmpfunc(const char *aptr, int asiz, const char *bptr, int bsiz, void *op); static int runcreate(int argc, char **argv); static int runinform(int argc, char **argv); static int runput(int argc, char **argv); static int runout(int argc, char **argv); static int runget(int argc, char **argv); static int runlist(int argc, char **argv); static int runoptimize(int argc, char **argv); static int runimporttsv(int argc, char **argv); static int runversion(int argc, char **argv); static int proccreate(const char *path, int lmemb, int nmemb, int bnum, int apow, int fpow, TCCMP cmp, int opts); static int procinform(const char *path, int omode); static int procput(const char *path, const char *kbuf, int ksiz, const char *vbuf, int vsiz, TCCMP cmp, int omode, int dmode); static int procout(const char *path, const char *kbuf, int ksiz, TCCMP cmp, int omode); static int procget(const char *path, const char *kbuf, int ksiz, TCCMP cmp, int omode, bool px, bool pz); static int proclist(const char *path, TCCMP cmp, int omode, int max, bool pv, bool px, bool bk, const char *jstr, const char *bstr, const char *estr, const char *fmstr); static int procoptimize(const char *path, int lmemb, int nmemb, int bnum, int apow, int fpow, TCCMP cmp, int opts, int omode, bool df); static int procimporttsv(const char *path, const char *file, int omode, bool sc); static int procversion(void); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; g_dbgfd = -1; const char *ebuf = getenv("TCDBGFD"); if(ebuf) g_dbgfd = tcatoix(ebuf); if(argc < 2) usage(); int rv = 0; if(!strcmp(argv[1], "create")){ rv = runcreate(argc, argv); } else if(!strcmp(argv[1], "inform")){ rv = runinform(argc, argv); } else if(!strcmp(argv[1], "put")){ rv = runput(argc, argv); } else if(!strcmp(argv[1], "out")){ rv = runout(argc, argv); } else if(!strcmp(argv[1], "get")){ rv = runget(argc, argv); } else if(!strcmp(argv[1], "list")){ rv = runlist(argc, argv); } else if(!strcmp(argv[1], "optimize")){ rv = runoptimize(argc, argv); } else if(!strcmp(argv[1], "importtsv")){ rv = runimporttsv(argc, argv); } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){ rv = runversion(argc, argv); } else { usage(); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: the command line utility of the B+ tree database API\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s create [-cd|-ci|-cj] [-tl] [-td|-tb|-tt|-tx] path" " [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname); fprintf(stderr, " %s inform [-nl|-nb] path\n", g_progname); fprintf(stderr, " %s put [-cd|-ci|-cj] [-nl|-nb] [-sx] [-dk|-dc|-dd|-db|-dai|-dad] path" " key value\n", g_progname); fprintf(stderr, " %s out [-cd|-ci|-cj] [-nl|-nb] [-sx] path key\n", g_progname); fprintf(stderr, " %s get [-cd|-ci|-cj] [-nl|-nb] [-sx] [-px] [-pz] path key\n", g_progname); fprintf(stderr, " %s list [-cd|-ci|-cj] [-nl|-nb] [-m num] [-bk] [-pv] [-px] [-j str]" " [-rb bkey ekey] [-fm str] path\n", g_progname); fprintf(stderr, " %s optimize [-cd|-ci|-cj] [-tl] [-td|-tb|-tt|-tx] [-tz] [-nl|-nb] [-df]" " path [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname); fprintf(stderr, " %s importtsv [-nl|-nb] [-sc] path [file]\n", g_progname); fprintf(stderr, " %s version\n", g_progname); fprintf(stderr, "\n"); exit(1); } /* print error information */ static void printerr(TCBDB *bdb){ const char *path = tcbdbpath(bdb); int ecode = tcbdbecode(bdb); fprintf(stderr, "%s: %s: %d: %s\n", g_progname, path ? path : "-", ecode, tcbdberrmsg(ecode)); } /* print record data */ static int printdata(const char *ptr, int size, bool px){ int len = 0; while(size-- > 0){ if(px){ if(len > 0) putchar(' '); len += printf("%02X", *(unsigned char *)ptr); } else { putchar(*ptr); len++; } ptr++; } return len; } /* read a line from a file descriptor */ static char *mygetline(FILE *ifp){ int len = 0; int blen = 1024; char *buf = tcmalloc(blen + 1); bool end = true; int c; while((c = fgetc(ifp)) != EOF){ end = false; if(c == '\0') continue; if(blen <= len){ blen *= 2; buf = tcrealloc(buf, blen + 1); } if(c == '\n' || c == '\r') c = '\0'; buf[len++] = c; if(c == '\0') break; } if(end){ tcfree(buf); return NULL; } buf[len] = '\0'; return buf; } /* dummy comparison function */ static int mycmpfunc(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){ return 0; } /* parse arguments of create command */ static int runcreate(int argc, char **argv){ char *path = NULL; char *lmstr = NULL; char *nmstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; TCCMP cmp = NULL; int opts = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-cd")){ cmp = tccmpdecimal; } else if(!strcmp(argv[i], "-ci")){ cmp = tccmpint32; } else if(!strcmp(argv[i], "-cj")){ cmp = tccmpint64; } else if(!strcmp(argv[i], "-tl")){ opts |= BDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= BDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= BDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= BDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= BDBTEXCODEC; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!lmstr){ lmstr = argv[i]; } else if(!nmstr){ nmstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path) usage(); int lmemb = lmstr ? tcatoix(lmstr) : -1; int nmemb = nmstr ? tcatoix(nmstr) : -1; int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = proccreate(path, lmemb, nmemb, bnum, apow, fpow, cmp, opts); return rv; } /* parse arguments of inform command */ static int runinform(int argc, char **argv){ char *path = NULL; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procinform(path, omode); return rv; } /* parse arguments of put command */ static int runput(int argc, char **argv){ char *path = NULL; char *key = NULL; char *value = NULL; TCCMP cmp = NULL; int omode = 0; int dmode = 0; bool sx = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-cd")){ cmp = tccmpdecimal; } else if(!strcmp(argv[i], "-ci")){ cmp = tccmpint32; } else if(!strcmp(argv[i], "-cj")){ cmp = tccmpint64; } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else if(!strcmp(argv[i], "-dk")){ dmode = -1; } else if(!strcmp(argv[i], "-dc")){ dmode = 1; } else if(!strcmp(argv[i], "-dd")){ dmode = 2; } else if(!strcmp(argv[i], "-db")){ dmode = 3; } else if(!strcmp(argv[i], "-dai")){ dmode = 10; } else if(!strcmp(argv[i], "-dad")){ dmode = 11; } else if(!strcmp(argv[i], "-sx")){ sx = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!key){ key = argv[i]; } else if(!value){ value = argv[i]; } else { usage(); } } if(!path || !key || !value) usage(); char *kbuf, *vbuf; int ksiz, vsiz; if(sx){ kbuf = tchexdecode(key, &ksiz); vbuf = tchexdecode(value, &vsiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); vsiz = strlen(value); vbuf = tcmemdup(value, vsiz); } int rv = procput(path, kbuf, ksiz, vbuf, vsiz, cmp, omode, dmode); tcfree(vbuf); tcfree(kbuf); return rv; } /* parse arguments of out command */ static int runout(int argc, char **argv){ char *path = NULL; char *key = NULL; TCCMP cmp = NULL; int omode = 0; bool sx = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-cd")){ cmp = tccmpdecimal; } else if(!strcmp(argv[i], "-ci")){ cmp = tccmpint32; } else if(!strcmp(argv[i], "-cj")){ cmp = tccmpint64; } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else if(!strcmp(argv[i], "-sx")){ sx = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!key){ key = argv[i]; } else { usage(); } } if(!path || !key) usage(); int ksiz; char *kbuf; if(sx){ kbuf = tchexdecode(key, &ksiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); } int rv = procout(path, kbuf, ksiz, cmp, omode); tcfree(kbuf); return rv; } /* parse arguments of get command */ static int runget(int argc, char **argv){ char *path = NULL; char *key = NULL; TCCMP cmp = NULL; int omode = 0; bool sx = false; bool px = false; bool pz = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-cd")){ cmp = tccmpdecimal; } else if(!strcmp(argv[i], "-ci")){ cmp = tccmpint32; } else if(!strcmp(argv[i], "-cj")){ cmp = tccmpint64; } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else if(!strcmp(argv[i], "-sx")){ sx = true; } else if(!strcmp(argv[i], "-px")){ px = true; } else if(!strcmp(argv[i], "-pz")){ pz = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!key){ key = argv[i]; } else { usage(); } } if(!path || !key) usage(); int ksiz; char *kbuf; if(sx){ kbuf = tchexdecode(key, &ksiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); } int rv = procget(path, kbuf, ksiz, cmp, omode, px, pz); tcfree(kbuf); return rv; } /* parse arguments of list command */ static int runlist(int argc, char **argv){ char *path = NULL; TCCMP cmp = NULL; int omode = 0; int max = -1; bool pv = false; bool px = false; bool bk = false; char *jstr = NULL; char *bstr = NULL; char *estr = NULL; char *fmstr = NULL; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-cd")){ cmp = tccmpdecimal; } else if(!strcmp(argv[i], "-ci")){ cmp = tccmpint32; } else if(!strcmp(argv[i], "-cj")){ cmp = tccmpint64; } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else if(!strcmp(argv[i], "-m")){ if(++i >= argc) usage(); max = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-bk")){ bk = true; } else if(!strcmp(argv[i], "-pv")){ pv = true; } else if(!strcmp(argv[i], "-px")){ px = true; } else if(!strcmp(argv[i], "-j")){ if(++i >= argc) usage(); jstr = argv[i]; } else if(!strcmp(argv[i], "-rb")){ if(++i >= argc) usage(); bstr = argv[i]; if(++i >= argc) usage(); estr = argv[i]; } else if(!strcmp(argv[i], "-fm")){ if(++i >= argc) usage(); fmstr = argv[i]; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = proclist(path, cmp, omode, max, pv, px, bk, jstr, bstr, estr, fmstr); return rv; } /* parse arguments of optimize command */ static int runoptimize(int argc, char **argv){ char *path = NULL; char *lmstr = NULL; char *nmstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; TCCMP cmp = NULL; int opts = UINT8_MAX; int omode = 0; bool df = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-cd")){ cmp = tccmpdecimal; } else if(!strcmp(argv[i], "-ci")){ cmp = tccmpint32; } else if(!strcmp(argv[i], "-cj")){ cmp = tccmpint64; } else if(!strcmp(argv[i], "-tl")){ if(opts == UINT8_MAX) opts = 0; opts |= BDBTLARGE; } else if(!strcmp(argv[i], "-td")){ if(opts == UINT8_MAX) opts = 0; opts |= BDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ if(opts == UINT8_MAX) opts = 0; opts |= BDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ if(opts == UINT8_MAX) opts = 0; opts |= BDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ if(opts == UINT8_MAX) opts = 0; opts |= BDBTEXCODEC; } else if(!strcmp(argv[i], "-tz")){ if(opts == UINT8_MAX) opts = 0; } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else if(!strcmp(argv[i], "-df")){ df = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!lmstr){ lmstr = argv[i]; } else if(!nmstr){ nmstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path) usage(); int lmemb = lmstr ? tcatoix(lmstr) : -1; int nmemb = nmstr ? tcatoix(nmstr) : -1; int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = procoptimize(path, lmemb, nmemb, bnum, apow, fpow, cmp, opts, omode, df); return rv; } /* parse arguments of importtsv command */ static int runimporttsv(int argc, char **argv){ char *path = NULL; char *file = NULL; int omode = 0; bool sc = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else if(!strcmp(argv[i], "-sc")){ sc = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!file){ file = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procimporttsv(path, file, omode, sc); return rv; } /* parse arguments of version command */ static int runversion(int argc, char **argv){ int rv = procversion(); return rv; } /* perform create command */ static int proccreate(const char *path, int lmemb, int nmemb, int bnum, int apow, int fpow, TCCMP cmp, int opts){ TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb); if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(bdb); if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){ printerr(bdb); tcbdbdel(bdb); return 1; } if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC)){ printerr(bdb); tcbdbdel(bdb); return 1; } bool err = false; if(!tcbdbclose(bdb)){ printerr(bdb); err = true; } tcbdbdel(bdb); return err ? 1 : 0; } /* perform inform command */ static int procinform(const char *path, int omode){ TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); tcbdbsetcmpfunc(bdb, mycmpfunc, NULL); tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL); if(!tcbdbopen(bdb, path, BDBOREADER | omode)){ printerr(bdb); tcbdbdel(bdb); return 1; } bool err = false; const char *npath = tcbdbpath(bdb); if(!npath) npath = "(unknown)"; printf("path: %s\n", npath); printf("database type: btree\n"); uint8_t flags = tcbdbflags(bdb); printf("additional flags:"); if(flags & BDBFOPEN) printf(" open"); if(flags & BDBFFATAL) printf(" fatal"); printf("\n"); TCCMP cmp = tcbdbcmpfunc(bdb); printf("comparison function: "); if(cmp == tccmplexical){ printf("lexical"); } else if(cmp == tccmpdecimal){ printf("decimal"); } else if(cmp == tccmpint32){ printf("int32"); } else if(cmp == tccmpint64){ printf("int64"); } else { printf("custom"); } printf("\n"); printf("max leaf member: %d\n", tcbdblmemb(bdb)); printf("max node member: %d\n", tcbdbnmemb(bdb)); printf("leaf number: %llu\n", (unsigned long long)tcbdblnum(bdb)); printf("node number: %llu\n", (unsigned long long)tcbdbnnum(bdb)); printf("bucket number: %llu\n", (unsigned long long)tcbdbbnum(bdb)); if(bdb->hdb->cnt_writerec >= 0) printf("used bucket number: %lld\n", (long long)tcbdbbnumused(bdb)); printf("alignment: %u\n", tcbdbalign(bdb)); printf("free block pool: %u\n", tcbdbfbpmax(bdb)); printf("inode number: %lld\n", (long long)tcbdbinode(bdb)); char date[48]; tcdatestrwww(tcbdbmtime(bdb), INT_MAX, date); printf("modified time: %s\n", date); uint8_t opts = tcbdbopts(bdb); printf("options:"); if(opts & BDBTLARGE) printf(" large"); if(opts & BDBTDEFLATE) printf(" deflate"); if(opts & BDBTBZIP) printf(" bzip"); if(opts & BDBTTCBS) printf(" tcbs"); if(opts & BDBTEXCODEC) printf(" excodec"); printf("\n"); printf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb)); printf("file size: %llu\n", (unsigned long long)tcbdbfsiz(bdb)); if(!tcbdbclose(bdb)){ if(!err) printerr(bdb); err = true; } tcbdbdel(bdb); return err ? 1 : 0; } /* perform put command */ static int procput(const char *path, const char *kbuf, int ksiz, const char *vbuf, int vsiz, TCCMP cmp, int omode, int dmode){ TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb); if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(bdb); if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){ printerr(bdb); tcbdbdel(bdb); return 1; } bool err = false; int inum; double dnum; switch(dmode){ case -1: if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz)){ printerr(bdb); err = true; } break; case 1: if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){ printerr(bdb); err = true; } break; case 2: if(!tcbdbputdup(bdb, kbuf, ksiz, vbuf, vsiz)){ printerr(bdb); err = true; } break; case 3: if(!tcbdbputdupback(bdb, kbuf, ksiz, vbuf, vsiz)){ printerr(bdb); err = true; } break; case 10: inum = tcbdbaddint(bdb, kbuf, ksiz, tcatoi(vbuf)); if(inum == INT_MIN){ printerr(bdb); err = true; } else { printf("%d\n", inum); } break; case 11: dnum = tcbdbadddouble(bdb, kbuf, ksiz, tcatof(vbuf)); if(isnan(dnum)){ printerr(bdb); err = true; } else { printf("%.6f\n", dnum); } break; default: if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){ printerr(bdb); err = true; } break; } if(!tcbdbclose(bdb)){ if(!err) printerr(bdb); err = true; } tcbdbdel(bdb); return err ? 1 : 0; } /* perform out command */ static int procout(const char *path, const char *kbuf, int ksiz, TCCMP cmp, int omode){ TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb); if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(bdb); if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){ printerr(bdb); tcbdbdel(bdb); return 1; } bool err = false; if(!tcbdbout(bdb, kbuf, ksiz)){ printerr(bdb); err = true; } if(!tcbdbclose(bdb)){ if(!err) printerr(bdb); err = true; } tcbdbdel(bdb); return err ? 1 : 0; } /* perform get command */ static int procget(const char *path, const char *kbuf, int ksiz, TCCMP cmp, int omode, bool px, bool pz){ TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb); if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(bdb); if(!tcbdbopen(bdb, path, BDBOREADER | omode)){ printerr(bdb); tcbdbdel(bdb); return 1; } bool err = false; int vsiz; char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz); if(vbuf){ printdata(vbuf, vsiz, px); if(!pz) putchar('\n'); tcfree(vbuf); } else { printerr(bdb); err = true; } if(!tcbdbclose(bdb)){ if(!err) printerr(bdb); err = true; } tcbdbdel(bdb); return err ? 1 : 0; } /* perform list command */ static int proclist(const char *path, TCCMP cmp, int omode, int max, bool pv, bool px, bool bk, const char *jstr, const char *bstr, const char *estr, const char *fmstr){ TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb); if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(bdb); if(!tcbdbopen(bdb, path, BDBOREADER | omode)){ printerr(bdb); tcbdbdel(bdb); return 1; } bool err = false; if(bstr || fmstr){ TCLIST *keys = fmstr ? tcbdbfwmkeys2(bdb, fmstr, max) : tcbdbrange(bdb, bstr, strlen(bstr), true, estr, strlen(estr), true, max); int cnt = 0; for(int i = 0; i < tclistnum(keys); i++){ int ksiz; const char *kbuf = tclistval(keys, i, &ksiz); if(pv){ TCLIST *vals = tcbdbget4(bdb, kbuf, ksiz); if(vals){ for(int j = 0; j < tclistnum(vals); j++){ int vsiz; const char *vbuf = tclistval(vals, j, &vsiz); printdata(kbuf, ksiz, px); putchar('\t'); printdata(vbuf, vsiz, px); putchar('\n'); if(max >= 0 && ++cnt >= max) break; } tclistdel(vals); } } else { int num = tcbdbvnum(bdb, kbuf, ksiz); for(int j = 0; j < num; j++){ printdata(kbuf, ksiz, px); putchar('\n'); if(max >= 0 && ++cnt >= max) break; } } if(max >= 0 && cnt >= max) break; } tclistdel(keys); } else { BDBCUR *cur = tcbdbcurnew(bdb); if(bk){ if(jstr){ if(!tcbdbcurjumpback(cur, jstr, strlen(jstr)) && tcbdbecode(bdb) != TCENOREC){ printerr(bdb); err = true; } } else { if(!tcbdbcurlast(cur) && tcbdbecode(bdb) != TCENOREC){ printerr(bdb); err = true; } } } else { if(jstr){ if(!tcbdbcurjump(cur, jstr, strlen(jstr)) && tcbdbecode(bdb) != TCENOREC){ printerr(bdb); err = true; } } else { if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){ printerr(bdb); err = true; } } } TCXSTR *key = tcxstrnew(); TCXSTR *val = tcxstrnew(); int cnt = 0; while(tcbdbcurrec(cur, key, val)){ printdata(tcxstrptr(key), tcxstrsize(key), px); if(pv){ putchar('\t'); printdata(tcxstrptr(val), tcxstrsize(val), px); } putchar('\n'); if(bk){ if(!tcbdbcurprev(cur) && tcbdbecode(bdb) != TCENOREC){ printerr(bdb); err = true; } } else { if(!tcbdbcurnext(cur) && tcbdbecode(bdb) != TCENOREC){ printerr(bdb); err = true; } } if(max >= 0 && ++cnt >= max) break; } tcxstrdel(val); tcxstrdel(key); tcbdbcurdel(cur); } if(!tcbdbclose(bdb)){ if(!err) printerr(bdb); err = true; } tcbdbdel(bdb); return err ? 1 : 0; } /* perform optimize command */ static int procoptimize(const char *path, int lmemb, int nmemb, int bnum, int apow, int fpow, TCCMP cmp, int opts, int omode, bool df){ TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)) printerr(bdb); if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(bdb); if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){ printerr(bdb); tcbdbdel(bdb); return 1; } bool err = false; if(df){ if(!tcbdbdefrag(bdb, INT64_MAX)){ printerr(bdb); err = true; } } else { if(!tcbdboptimize(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){ printerr(bdb); err = true; } } if(!tcbdbclose(bdb)){ if(!err) printerr(bdb); err = true; } tcbdbdel(bdb); return err ? 1 : 0; } /* perform importtsv command */ static int procimporttsv(const char *path, const char *file, int omode, bool sc){ FILE *ifp = file ? fopen(file, "rb") : stdin; if(!ifp){ fprintf(stderr, "%s: could not open\n", file ? file : "(stdin)"); return 1; } TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)) printerr(bdb); if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | omode)){ printerr(bdb); tcbdbdel(bdb); if(ifp != stdin) fclose(ifp); return 1; } bool err = false; char *line; int cnt = 0; while(!err && (line = mygetline(ifp)) != NULL){ char *pv = strchr(line, '\t'); if(!pv){ tcfree(line); continue; } *pv = '\0'; if(sc) tcstrutfnorm(line, TCUNSPACE | TCUNLOWER | TCUNNOACC | TCUNWIDTH); if(!tcbdbputdup2(bdb, line, pv + 1)){ printerr(bdb); err = true; } tcfree(line); if(cnt > 0 && cnt % 100 == 0){ putchar('.'); fflush(stdout); if(cnt % 5000 == 0) printf(" (%08d)\n", cnt); } cnt++; } printf(" (%08d)\n", cnt); if(!tcbdbclose(bdb)){ if(!err) printerr(bdb); err = true; } tcbdbdel(bdb); if(ifp != stdin) fclose(ifp); return err ? 1 : 0; } /* perform version command */ static int procversion(void){ printf("Tokyo Cabinet version %s (%d:%s) for %s\n", tcversion, _TC_LIBVER, _TC_FORMATVER, TCSYSNAME); printf("Copyright (C) 2006-2012 FAL Labs\n"); return 0; } // END OF FILE tokyocabinet-1.4.48/md5.c0000644000175000017500000003122412013574446014146 0ustar mikiomikio/* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.c is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order either statically or dynamically; added missing #include in library. 2002-03-11 lpd Corrected argument list for main(), and added int return type, in test program and T value program. 2002-02-21 lpd Added missing #include in test program. 2000-07-03 lpd Patched to eliminate warnings about "constant is unsigned in ANSI C, signed in traditional"; made test program self-checking. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). 1999-05-03 lpd Original version. */ #include "md5.h" #include #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #ifdef ARCH_IS_BIG_ENDIAN # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) #else # define BYTE_ORDER 0 #endif #define T_MASK ((md5_word_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) #define T3 0x242070db #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) #define T6 0x4787c62a #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) #define T9 0x698098d8 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) #define T13 0x6b901122 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) #define T16 0x49b40821 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) #define T19 0x265e5a51 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) #define T22 0x02441453 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) #define T25 0x21e1cde6 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) #define T28 0x455a14ed #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) #define T31 0x676f02d9 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) #define T35 0x6d9d6122 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) #define T38 0x4bdecfa9 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) #define T41 0x289b7ec6 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) #define T44 0x04881d05 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) #define T47 0x1fa27cf8 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) #define T50 0x432aff97 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) #define T53 0x655b59c3 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) #define T57 0x6fa87e4f #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) #define T60 0x4e0811a1 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) #define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs. */ md5_word_t xbuf[16]; const md5_word_t *X; #endif { #if BYTE_ORDER == 0 /* * Determine dynamically whether this is a big-endian or * little-endian machine, since we can use a more efficient * algorithm on the latter. */ static const int w = 1; if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ #endif #if BYTE_ORDER <= 0 /* little-endian */ { /* * On little-endian machines, we can process properly aligned * data without copying it. */ if (!((data - (const md5_byte_t *)0) & 3)) { /* data are properly aligned */ X = (const md5_word_t *)data; } else { /* not aligned */ memcpy(xbuf, data, 64); X = xbuf; } } #endif #if BYTE_ORDER == 0 else /* dynamic big-endian */ #endif #if BYTE_ORDER >= 0 /* big-endian */ { /* * On big-endian machines, we must arrange the bytes in the * right order. */ const md5_byte_t *xp = data; int i; # if BYTE_ORDER == 0 X = xbuf; /* (dynamic only) */ # else # define xbuf X /* (static only) */ # endif for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } #endif } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1. */ /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + F(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 7, T1); SET(d, a, b, c, 1, 12, T2); SET(c, d, a, b, 2, 17, T3); SET(b, c, d, a, 3, 22, T4); SET(a, b, c, d, 4, 7, T5); SET(d, a, b, c, 5, 12, T6); SET(c, d, a, b, 6, 17, T7); SET(b, c, d, a, 7, 22, T8); SET(a, b, c, d, 8, 7, T9); SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12); SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + G(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 1, 5, T17); SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19); SET(b, c, d, a, 0, 20, T20); SET(a, b, c, d, 5, 5, T21); SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23); SET(b, c, d, a, 4, 20, T24); SET(a, b, c, d, 9, 5, T25); SET(d, a, b, c, 14, 9, T26); SET(c, d, a, b, 3, 14, T27); SET(b, c, d, a, 8, 20, T28); SET(a, b, c, d, 13, 5, T29); SET(d, a, b, c, 2, 9, T30); SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32); #undef SET /* Round 3. */ /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) #define SET(a, b, c, d, k, s, Ti)\ t = a + H(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 5, 4, T33); SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36); SET(a, b, c, d, 1, 4, T37); SET(d, a, b, c, 4, 11, T38); SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40); SET(a, b, c, d, 13, 4, T41); SET(d, a, b, c, 0, 11, T42); SET(c, d, a, b, 3, 16, T43); SET(b, c, d, a, 6, 23, T44); SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47); SET(b, c, d, a, 2, 23, T48); #undef SET /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + I(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 6, T49); SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51); SET(b, c, d, a, 5, 21, T52); SET(a, b, c, d, 12, 6, T53); SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55); SET(b, c, d, a, 1, 21, T56); SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58); SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60); SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62); SET(c, d, a, b, 2, 15, T63); SET(b, c, d, a, 9, 21, T64); #undef SET /* Then perform the following additions. (That is increment each of the four registers by the value it had before this block was started.) */ pms->abcd[0] += a; pms->abcd[1] += b; pms->abcd[2] += c; pms->abcd[3] += d; } void md5_init(md5_state_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; pms->abcd[3] = 0x10325476; } void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) pms->count[1]++; /* Process an initial partial block. */ if (offset) { int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); memcpy(pms->buf + offset, p, copy); if (offset + copy < 64) return; p += copy; left -= copy; md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) md5_process(pms, p); /* Process a final partial block. */ if (left) memcpy(pms->buf, p, left); } void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { 0x80, 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 }; md5_byte_t data[8]; int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ md5_append(pms, data, 8); for (i = 0; i < 16; ++i) digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } tokyocabinet-1.4.48/tcadb.h0000644000175000017500000006636312013574446014557 0ustar mikiomikio/************************************************************************************************* * The abstract database API of Tokyo Cabinet * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #ifndef _TCADB_H /* duplication check */ #define _TCADB_H #if defined(__cplusplus) #define __TCADB_CLINKAGEBEGIN extern "C" { #define __TCADB_CLINKAGEEND } #else #define __TCADB_CLINKAGEBEGIN #define __TCADB_CLINKAGEEND #endif __TCADB_CLINKAGEBEGIN #include #include #include #include #include /************************************************************************************************* * API *************************************************************************************************/ typedef struct { /* type of structure for an abstract database */ int omode; /* open mode */ TCMDB *mdb; /* on-memory hash database object */ TCNDB *ndb; /* on-memory tree database object */ TCHDB *hdb; /* hash database object */ TCBDB *bdb; /* B+ tree database object */ TCFDB *fdb; /* fixed-length databae object */ TCTDB *tdb; /* table database object */ int64_t capnum; /* capacity number of records */ int64_t capsiz; /* capacity size of using memory */ uint32_t capcnt; /* count for capacity check */ BDBCUR *cur; /* cursor of B+ tree */ void *skel; /* skeleton database */ } TCADB; enum { /* enumeration for open modes */ ADBOVOID, /* not opened */ ADBOMDB, /* on-memory hash database */ ADBONDB, /* on-memory tree database */ ADBOHDB, /* hash database */ ADBOBDB, /* B+ tree database */ ADBOFDB, /* fixed-length database */ ADBOTDB, /* table database */ ADBOSKEL /* skeleton database */ }; /* Create an abstract database object. The return value is the new abstract database object. */ TCADB *tcadbnew(void); /* Delete an abstract database object. `adb' specifies the abstract database object. */ void tcadbdel(TCADB *adb); /* Open an abstract database. `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. */ bool tcadbopen(TCADB *adb, const char *name); /* Close an abstract database object. `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. */ bool tcadbclose(TCADB *adb); /* Store a record into an abstract database object. `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. */ bool tcadbput(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a string record into an abstract object. `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. */ bool tcadbput2(TCADB *adb, const char *kstr, const char *vstr); /* Store a new record into an abstract database object. `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. */ bool tcadbputkeep(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a new string record into an abstract database object. `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. */ bool tcadbputkeep2(TCADB *adb, const char *kstr, const char *vstr); /* Concatenate a value at the end of the existing record in an abstract database object. `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. */ bool tcadbputcat(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Concatenate a string value at the end of the existing record in an abstract database object. `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. */ bool tcadbputcat2(TCADB *adb, const char *kstr, const char *vstr); /* Remove a record of an abstract database object. `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. */ bool tcadbout(TCADB *adb, const void *kbuf, int ksiz); /* Remove a string record of an abstract database object. `adb' specifies the abstract database object. `kstr' specifies the string of the key. If successful, the return value is true, else, it is false. */ bool tcadbout2(TCADB *adb, const char *kstr); /* Retrieve a record in an abstract database object. `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. */ void *tcadbget(TCADB *adb, const void *kbuf, int ksiz, int *sp); /* Retrieve a string record in an abstract database object. `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. */ char *tcadbget2(TCADB *adb, const char *kstr); /* Get the size of the value of a record in an abstract database object. `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. */ int tcadbvsiz(TCADB *adb, const void *kbuf, int ksiz); /* Get the size of the value of a string record in an abstract database object. `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. */ int tcadbvsiz2(TCADB *adb, const char *kstr); /* Initialize the iterator of an abstract database object. `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. */ bool tcadbiterinit(TCADB *adb); /* Get the next key of the iterator of an abstract database object. `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. */ void *tcadbiternext(TCADB *adb, int *sp); /* Get the next key string of the iterator of an abstract database object. `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. */ char *tcadbiternext2(TCADB *adb); /* Get forward matching keys in an abstract database object. `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. */ TCLIST *tcadbfwmkeys(TCADB *adb, const void *pbuf, int psiz, int max); /* Get forward matching string keys in an abstract database object. `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. */ TCLIST *tcadbfwmkeys2(TCADB *adb, const char *pstr, int max); /* Add an integer to a record in an abstract database object. `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. */ int tcadbaddint(TCADB *adb, const void *kbuf, int ksiz, int num); /* Add a real number to a record in an abstract database object. `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. */ double tcadbadddouble(TCADB *adb, const void *kbuf, int ksiz, double num); /* Synchronize updated contents of an abstract database object with the file and the device. `adb' specifies the abstract database object. If successful, the return value is true, else, it is false. */ bool tcadbsync(TCADB *adb); /* Optimize the storage of an abstract database object. `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. */ bool tcadboptimize(TCADB *adb, const char *params); /* Remove all records of an abstract database object. `adb' specifies the abstract database object. If successful, the return value is true, else, it is false. */ bool tcadbvanish(TCADB *adb); /* Copy the database file of an abstract database object. `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. */ bool tcadbcopy(TCADB *adb, const char *path); /* Begin the transaction of an abstract database object. `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. */ bool tcadbtranbegin(TCADB *adb); /* Commit the transaction of an abstract database object. `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. */ bool tcadbtrancommit(TCADB *adb); /* Abort the transaction of an abstract database object. `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. */ bool tcadbtranabort(TCADB *adb); /* Get the file path of an abstract database object. `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. */ const char *tcadbpath(TCADB *adb); /* Get the number of records of an abstract database object. `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. */ uint64_t tcadbrnum(TCADB *adb); /* Get the size of the database of an abstract database object. `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. */ uint64_t tcadbsize(TCADB *adb); /* Call a versatile function for miscellaneous operations of an abstract database object. `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. */ TCLIST *tcadbmisc(TCADB *adb, const char *name, const TCLIST *args); /************************************************************************************************* * features for experts *************************************************************************************************/ typedef struct { /* type of structure for a extra database skeleton */ void *opq; /* opaque pointer */ void (*del)(void *); /* destructor */ bool (*open)(void *, const char *); bool (*close)(void *); bool (*put)(void *, const void *, int, const void *, int); bool (*putkeep)(void *, const void *, int, const void *, int); bool (*putcat)(void *, const void *, int, const void *, int); bool (*out)(void *, const void *, int); void *(*get)(void *, const void *, int, int *); int (*vsiz)(void *, const void *, int); bool (*iterinit)(void *); void *(*iternext)(void *, int *); TCLIST *(*fwmkeys)(void *, const void *, int, int); int (*addint)(void *, const void *, int, int); double (*adddouble)(void *, const void *, int, double); bool (*sync)(void *); bool (*optimize)(void *, const char *); bool (*vanish)(void *); bool (*copy)(void *, const char *); bool (*tranbegin)(void *); bool (*trancommit)(void *); bool (*tranabort)(void *); const char *(*path)(void *); uint64_t (*rnum)(void *); uint64_t (*size)(void *); TCLIST *(*misc)(void *, const char *, const TCLIST *); bool (*putproc)(void *, const void *, int, const void *, int, TCPDPROC, void *); bool (*foreach)(void *, TCITER, void *); } ADBSKEL; /* type of the pointer to a mapping function. `map' specifies the pointer to the destination manager. `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. `op' specifies the pointer to the optional opaque object. The return value is true to continue iteration or false to stop iteration. */ typedef bool (*ADBMAPPROC)(void *map, const char *kbuf, int ksiz, const char *vbuf, int vsiz, void *op); /* Set an extra database sleleton to an abstract database object. `adb' specifies the abstract database object. `skel' specifies the extra database skeleton. If successful, the return value is true, else, it is false. */ bool tcadbsetskel(TCADB *adb, ADBSKEL *skel); /* Set the multiple database skeleton to an abstract database object. `adb' specifies the abstract database object. `num' specifies the number of inner databases. If successful, the return value is true, else, it is false. */ bool tcadbsetskelmulti(TCADB *adb, int num); /* Get the open mode of an abstract database object. `adb' specifies the abstract database object. The return value is `ADBOVOID' for not opened database, `ADBOMDB' for on-memory hash database, `ADBONDB' for on-memory tree database, `ADBOHDB' for hash database, `ADBOBDB' for B+ tree database, `ADBOFDB' for fixed-length database, `ADBOTDB' for table database. */ int tcadbomode(TCADB *adb); /* Get the concrete database object of an abstract database object. `adb' specifies the abstract database object. The return value is the concrete database object depend on the open mode or 0 if the object does not connect to any database instance. */ void *tcadbreveal(TCADB *adb); /* Store a record into an abstract database object with a duplication handler. `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. `proc' specifies the pointer to the callback function to process duplication. `op' specifies an arbitrary pointer to be given as a parameter of the callback function. If it is not needed, `NULL' can be specified. If successful, the return value is true, else, it is false. This function does not work for the table database. */ bool tcadbputproc(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op); /* Process each record atomically of an abstract database object. `adb' specifies the abstract database object. `iter' specifies the pointer to the iterator function called for each record. `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. */ bool tcadbforeach(TCADB *adb, TCITER iter, void *op); /* Map records of an abstract database object into another B+ tree database. `adb' specifies the abstract database object. `keys' specifies a list object of the keys of the target records. If it is `NULL', every record is processed. `bdb' specifies the B+ tree database object into which records emitted by the mapping function are stored. `proc' specifies the pointer to the mapping function called for each record. `op' specifies specifies the pointer to the optional opaque object for the mapping function. `csiz' specifies the size of the cache to sort emitted records. If it is negative, the default size is specified. The default size is 268435456. If successful, the return value is true, else, it is false. */ bool tcadbmapbdb(TCADB *adb, TCLIST *keys, TCBDB *bdb, ADBMAPPROC proc, void *op, int64_t csiz); /* Emit records generated by the mapping function into the result map. `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. */ bool tcadbmapbdbemit(void *map, const char *kbuf, int ksiz, const char *vbuf, int vsiz); __TCADB_CLINKAGEEND #endif /* duplication check */ /* END OF FILE */ tokyocabinet-1.4.48/example/0000755000175000017500000000000012013574114014736 5ustar mikiomikiotokyocabinet-1.4.48/example/tchdbex.c0000644000175000017500000000241312013574114016523 0ustar mikiomikio#include #include #include #include #include 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; } tokyocabinet-1.4.48/example/tctchat.c0000644000175000017500000000750412013574114016542 0ustar mikiomikio#include #include #include #include #include #include #include /* function prototypes */ int main(int argc, char **argv); static void proc(TCTMPL *tmpl, TCMPOOL *mpool); /* main routine */ int main(int argc, char **argv){ TCTMPL *tmpl = tctmplnew(); tctmplload2(tmpl, "tctchat.tmpl"); TCMPOOL *mpool = tcmpoolnew(); proc(tmpl, mpool); tcmpooldel(mpool); tctmpldel(tmpl); return 0; } /* process each session */ static void proc(TCTMPL *tmpl, TCMPOOL *mpool){ TCMAP *params = tcmpoolmapnew(mpool); char *query = getenv("QUERY_STRING"); const char *rp = getenv("CONTENT_LENGTH"); if(rp){ int clen = tclmin(tcatoi(rp), 1024 * 1024); query = tcmpoolmalloc(mpool, clen + 1); if(fread(query, 1, clen, stdin) != clen) clen = 0; query[clen] = '\0'; } if(query) tcwwwformdecode(query, params); const char *type = tcstrskipspc(tcmapget4(params, "type", "")); const char *author = tcstrskipspc(tcmapget4(params, "author", "")); const char *text = tcstrskipspc(tcmapget4(params, "text", "")); const char *search = tcstrskipspc(tcmapget4(params, "search", "")); int page = tcatoi(tcmapget4(params, "page", "1")); const char *dbpath = tctmplconf(tmpl, "dbpath"); if(!dbpath) dbpath = "tctchat.tct"; TCLIST *msgs = tcmpoollistnew(mpool); TCTDB *tdb = tcmpoolpush(mpool, tctdbnew(), (void (*)(void *))tctdbdel); rp = getenv("REQUEST_METHOD"); if(rp && !strcmp(rp, "POST") && *author != '\0' && *text != '\0' && strlen(author) <= 32 && strlen(text) <= 1024){ if(!tctdbopen(tdb, dbpath, TDBOWRITER | TDBOCREAT)) tclistprintf(msgs, "The database could not be opened (%s).", tctdberrmsg(tctdbecode(tdb))); tctdbsetindex(tdb, "", TDBITDECIMAL | TDBITKEEP); char pkbuf[64]; int pksiz = sprintf(pkbuf, "%.0f", tctime() * 1000); TCMAP *cols = tcmpoolmapnew(mpool); tcmapput2(cols, "a", author); tcmapput2(cols, "t", text); tctdbtranbegin(tdb); if(!tctdbputkeep(tdb, pkbuf, pksiz, cols)) tclistprintf(msgs, "The message is ignored."); tctdbtrancommit(tdb); } else { if(!tctdbopen(tdb, dbpath, TDBOREADER)) tclistprintf(msgs, "The database could not be opened (%s).", tctdberrmsg(tctdbecode(tdb))); } TCLIST *logs = tcmpoollistnew(mpool); TDBQRY *qry = tcmpoolpush(mpool, tctdbqrynew(tdb), (void (*)(void *))tctdbqrydel); if(*search != '\0') tctdbqryaddcond(qry, "t", TDBQCFTSEX, search); tctdbqrysetorder(qry, "", TDBQONUMDESC); tctdbqrysetlimit(qry, 16, page > 0 ? (page - 1) * 16 : 0); TCLIST *res = tcmpoolpushlist(mpool, tctdbqrysearch(qry)); int rnum = tclistnum(res); for(int i = rnum - 1; i >= 0; i--){ int pksiz; const char *pkbuf = tclistval(res, i, &pksiz); TCMAP *cols = tcmpoolpushmap(mpool, tctdbget(tdb, pkbuf, pksiz)); if(cols){ tcmapprintf(cols, "pk", "%s", pkbuf); char date[64]; tcdatestrwww(tcatoi(pkbuf) / 1000, INT_MAX, date); tcmapput2(cols, "d", date); const char *astr = tcmapget4(cols, "a", ""); tcmapprintf(cols, "c", "c%02u", tcgetcrc(astr, strlen(astr)) % 12 + 1); tclistpushmap(logs, cols); } } TCMAP *vars = tcmpoolmapnew(mpool); if(tclistnum(msgs) > 0) tcmapputlist(vars, "msgs", msgs); if(tclistnum(logs) > 0) tcmapputlist(vars, "logs", logs); tcmapprintf(vars, "author", "%s", author); tcmapprintf(vars, "search", "%s", search); if(page > 1) tcmapprintf(vars, "prev", "%d", page - 1); if(rnum >= 16 && tctdbrnum(tdb) > page * 16) tcmapprintf(vars, "next", "%d", page + 1); char *str = tcmpoolpushptr(mpool, tctmpldump(tmpl, vars)); printf("Content-Type: %s\r\n", !strcmp(type, "xml") ? "application/xml" : !strcmp(type, "xhtml") ? "application/xhtml+xml" : "text/html; charset=UTF-8"); printf("Cache-Control: no-cache\r\n"); printf("\r\n"); fwrite(str, 1, strlen(str), stdout); } tokyocabinet-1.4.48/example/tcadbex.c0000644000175000017500000000202212013574114016510 0ustar mikiomikio#include #include #include #include #include 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; } tokyocabinet-1.4.48/example/tcutilex.c0000644000175000017500000000357512013574114016755 0ustar mikiomikio#include #include #include #include #include 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; } tokyocabinet-1.4.48/example/Makefile0000644000175000017500000000502711232265233016403 0ustar mikiomikio# Makefile for sample programs of Tokyo Cabinet #================================================================ # Setting Variables #================================================================ # Generic settings SHELL = /bin/sh # Targets MYBINS = tcutilex tchdbex tcbdbex tcfdbex tctdbex tcadbex tctchat.cgi tctsearch.cgi # Building binaries CC = gcc CFLAGS = -I. -I.. -ansi -Wall -pedantic -fsigned-char -O2 C99FLAGS = -I. -I.. -std=c99 -Wall -pedantic -fsigned-char -O2 LDFLAGS = LIBS = -L. -L.. -ltokyocabinet -lz -lbz2 -lpthread -lm -lc LDENV = LD_RUN_PATH=/lib:/usr/lib:$(HOME)/lib:/usr/local/lib:.:.. #================================================================ # Suffix rules #================================================================ .SUFFIXES : .SUFFIXES : .c .o .c.o : $(CC) -c $(CFLAGS) $< #================================================================ # Actions #================================================================ all : $(MYBINS) clean : rm -rf $(MYBINS) *.exe *.o a.out check.out gmon.out leak.log casket* *.tct *.idx.* *~ static : make LDFLAGS="$(LDFLAGS) -static" .PHONY : all clean static tctchat.tct tctsearch.tct #================================================================ # Building binaries #================================================================ tcutilex : tcutilex.o $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) tchdbex : tchdbex.o $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) tcbdbex : tcbdbex.o $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) tcfdbex : tcfdbex.o $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) tctdbex : tctdbex.o $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) tcadbex : tcadbex.o $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(LIBS) tctchat.cgi : tctchat.c $(LDENV) $(CC) $(C99FLAGS) -o $@ $< $(LDFLAGS) $(LIBS) tctsearch.cgi : tctsearch.c $(LDENV) $(CC) $(C99FLAGS) -o $@ $< $(LDFLAGS) $(LIBS) tctchat.tct : rm -rf tctchat.tct* LD_LIBRARY_PATH=/usr/local/lib:.:.. ../tctmgr create tctchat.tct LD_LIBRARY_PATH=/usr/local/lib:.:.. ../tctmgr setindex -it qgram tctchat.tct "t" chmod 666 tctchat.tct* tctsearch.tct : rm -rf tctsearch.tct* LD_LIBRARY_PATH=/usr/local/lib:.:.. ../tctmgr create tctsearch.tct LD_LIBRARY_PATH=/usr/local/lib:.:.. ../tctmgr setindex -it qgram tctsearch.tct "text" LD_LIBRARY_PATH=/usr/local/lib:.:.. ../tctmgr setindex -it qgram tctsearch.tct "title" ../lab/htmltotsv .. | \ LD_LIBRARY_PATH=/usr/local/lib:.:.. ../tctmgr importtsv tctsearch.tct # END OF FILE tokyocabinet-1.4.48/example/tcfdbex.c0000644000175000017500000000242212013574114016521 0ustar mikiomikio#include #include #include #include #include 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; } tokyocabinet-1.4.48/example/tctdbex.c0000644000175000017500000000425112013574114016541 0ustar mikiomikio#include #include #include #include #include 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; } tokyocabinet-1.4.48/example/tcbdbex.c0000644000175000017500000000253312013574114016520 0ustar mikiomikio#include #include #include #include #include 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; } tokyocabinet-1.4.48/example/tctsearch.c0000644000175000017500000000732512013574114017071 0ustar mikiomikio#include #include #include #include #include #include #include /* function prototypes */ int main(int argc, char **argv); static void proc(TCTMPL *tmpl, TCMPOOL *mpool); /* main routine */ int main(int argc, char **argv){ TCTMPL *tmpl = tctmplnew(); tctmplload2(tmpl, "tctsearch.tmpl"); TCMPOOL *mpool = tcmpoolnew(); proc(tmpl, mpool); tcmpooldel(mpool); tctmpldel(tmpl); return 0; } /* process each session */ static void proc(TCTMPL *tmpl, TCMPOOL *mpool){ TCMAP *params = tcmpoolmapnew(mpool); char *query = getenv("QUERY_STRING"); if(query) tcwwwformdecode(query, params); char *expr = tcmpoolpushptr(mpool, tcstrdup(tcmapget4(params, "expr", ""))); expr = tcstrsqzspc(tcstrutfnorm(expr, TCUNSPACE)); int page = tclmax(tcatoi(tcmapget4(params, "page", "1")), 1); const char *dbpath = tctmplconf(tmpl, "dbpath"); if(!dbpath) dbpath = "tctsearch.tct"; const char *replace = tctmplconf(tmpl, "replace"); const char *rbef = replace; const char *raft = rbef ? strchr(rbef, ' ') : NULL; if(raft){ rbef = tcmpoolpushptr(mpool, tcmemdup(rbef, raft - rbef)); raft++; } TCMAP *vars = tcmpoolmapnew(mpool); if(*expr != '\0'){ TCTDB *tdb = tcmpoolpush(mpool, tctdbnew(), (void (*)(void *))tctdbdel); tctdbopen(tdb, dbpath, TDBOREADER); TDBQRY *qrys[2]; for(int i = 0; i < 2; i++){ qrys[i] = tcmpoolpush(mpool, tctdbqrynew(tdb), (void (*)(void *))tctdbqrydel); tctdbqrysetorder(qrys[i], "title", TDBQOSTRASC); } tctdbqryaddcond(qrys[0], "title", TDBQCFTSEX, expr); tctdbqryaddcond(qrys[1], "body", TDBQCFTSEX, expr); double stime = tctime(); TCLIST *res = tcmpoolpushlist(mpool, tctdbmetasearch(qrys, 2, TDBMSUNION)); int rnum = tclistnum(res); TCLIST *docs = tcmpoollistnew(mpool); for(int i = (page - 1) * 10; i < rnum && i < page * 10; i++){ int pksiz; const char *pkbuf = tclistval(res, i, &pksiz); TCMAP *cols = tcmpoolpushmap(mpool, tctdbget(tdb, pkbuf, pksiz)); if(cols){ tcmapputkeep2(cols, "title", "(no title)"); TCXSTR *snip = tcmpoolxstrnew(mpool); TCLIST *kwic = tctdbqrykwic(qrys[1], cols, NULL, 30, TCKWMUTAB | TCKWNOOVER | TCKWPULEAD); tcmpoolpushlist(mpool, kwic); for(int j = 0; j < tclistnum(kwic) && tcxstrsize(snip) < 400; j++){ if(j > 0) tcxstrcat2(snip, " ... "); tcxstrcat2(snip, ""); tcxstrcat2(snip, tcmpoolpushptr(mpool, tcxmlescape(tclistval2(kwic, j)))); tcxstrcat2(snip, ""); } tcxstrcat2(snip, " ..."); char *hlstr = tcregexreplace(tcxstrptr(snip), "\t([^\t]*)\t", "\\1"); tcmapput2(cols, "snippet", tcmpoolpushptr(mpool, hlstr)); const char *url = tcmapget4(cols, "url", "/"); url = tcmpoolpushptr(mpool, tcregexreplace(url, "*^[a-z]+://", "")); if(rbef) url = tcmpoolpushptr(mpool, tcregexreplace(url, rbef, raft ? raft : "")); if(url) tcmapprintf(cols, "url", "%s", url); tclistpushmap(docs, cols); } } tcmapprintf(vars, "etime", "%.3f", tctime() - stime); tcmapprintf(vars, "min", "%d", (page - 1) * 10 + 1); tcmapprintf(vars, "max", "%d", page * 10); tcmapprintf(vars, "expr", "%s", expr); tcmapprintf(vars, "hitnum", "%d", rnum); if(tclistnum(docs) > 0) tcmapputlist(vars, "docs", docs); if(page > 1) tcmapprintf(vars, "prev", "%d", page - 1); if(rnum > page * 10) tcmapprintf(vars, "next", "%d", page + 1); } char *str = tcmpoolpushptr(mpool, tctmpldump(tmpl, vars)); printf("Content-Type: text/html; charset=UTF-8\r\n"); printf("Cache-Control: no-cache\r\n"); printf("\r\n"); fwrite(str, 1, strlen(str), stdout); } tokyocabinet-1.4.48/example/tctchat.tmpl0000644000175000017500000001257311221657607017307 0ustar mikiomikio[% CONF dbpath "tctchat.tct" \%] [% CONF url "tctchat.cgi" \%] [% CONF title "A Tiny Chat System by Tokyo Cabinet" \%] [%title ENC XML%]

[%title ENC XML%]

[% FOREACH msgs msg \%]
[%msg ENC XML%]
[% END \%]
[% IF prev %]PREV[% ELSE %]PREV[% END %] [% IF next %]NEXT[% ELSE %]NEXT[% END %]
[% FOREACH logs log \%]
[%log.d ENC XML%] [%log.a ENC XML%] [%log.t ENC XML%]
[% END \%]


tokyocabinet-1.4.48/example/tctsearch.tmpl0000644000175000017500000001305611246742771017636 0ustar mikiomikio[% CONF dbpath "tctsearch.tct" \%] [% CONF replace "^/home/mikio/public_html localhost" \%] [% CONF url "tctsearch.cgi" \%] [% CONF title "A Tiny Search System by Tokyo Cabinet" \%] [%title ENC XML%] [% IF expr NOT \%]

[%title ENC XML%]

[% END \%] [% IF expr \%]

Results [%min%] - [%max%] of about [%hitnum%] for [%expr ENC XML%]. ([%etime%] seconds)

[% IF docs \%] [% FOREACH docs doc \%]

[%doc.title ENC XML%]

[%doc.snippet%]
[%doc.url%]
[% END \%]
[% IF prev %]Prev[% ELSE %]Prev[% END %] [% IF next %]Next[% ELSE %]Next[% END %]
[% ELSE \%]

No pages containing all your search terms were found.

[% END \%] [% END \%] tokyocabinet-1.4.48/Makefile.in0000644000175000017500000010713611414044004015352 0ustar mikiomikio# Makefile for Tokyo Cabinet #================================================================ # Setting Variables #================================================================ # Generic settings SHELL = @SHELL@ # Package information PACKAGE = @PACKAGE_NAME@ VERSION = @PACKAGE_VERSION@ PACKAGEDIR = $(PACKAGE)-$(VERSION) 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) -ltokyocabinet @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 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) cp -Rf $(HEADERFILES) $(DESTDIR)$(INCLUDEDIR) 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.\n' @printf '#================================================================\n' install-strip : make DESTDIR=$(DESTDIR) install cd $(DESTDIR)$(BINDIR) && strip $(COMMANDFILES) uninstall : cd $(DESTDIR)$(INCLUDEDIR) && 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 tokyocabinet.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 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-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-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 #================================================================ libtokyocabinet.a : $(LIBOBJFILES) $(AR) $(ARFLAGS) $@ $(LIBOBJFILES) libtokyocabinet.so.$(LIBVER).$(LIBREV).0 : $(LIBOBJFILES) if uname -a | egrep -i 'SunOS' > /dev/null ; \ then \ $(CC) $(CFLAGS) -shared -Wl,-G,-h,libtokyocabinet.so.$(LIBVER) -o $@ \ $(LIBOBJFILES) $(LDFLAGS) $(LIBS) ; \ else \ $(CC) $(CFLAGS) -shared -Wl,-soname,libtokyocabinet.so.$(LIBVER) -o $@ \ $(LIBOBJFILES) $(LDFLAGS) $(LIBS) ; \ fi libtokyocabinet.so.$(LIBVER) : libtokyocabinet.so.$(LIBVER).$(LIBREV).0 ln -f -s libtokyocabinet.so.$(LIBVER).$(LIBREV).0 $@ libtokyocabinet.so : libtokyocabinet.so.$(LIBVER).$(LIBREV).0 ln -f -s libtokyocabinet.so.$(LIBVER).$(LIBREV).0 $@ libtokyocabinet.$(LIBVER).$(LIBREV).0.dylib : $(LIBOBJFILES) $(CC) $(CFLAGS) -dynamiclib -o $@ \ -install_name $(LIBDIR)/libtokyocabinet.$(LIBVER).dylib \ -current_version $(LIBVER).$(LIBREV).0 -compatibility_version $(LIBVER) \ $(LIBOBJFILES) $(LDFLAGS) $(LIBS) libtokyocabinet.$(LIBVER).dylib : libtokyocabinet.$(LIBVER).$(LIBREV).0.dylib ln -f -s libtokyocabinet.$(LIBVER).$(LIBREV).0.dylib $@ libtokyocabinet.dylib : libtokyocabinet.$(LIBVER).$(LIBREV).0.dylib ln -f -s libtokyocabinet.$(LIBVER).$(LIBREV).0.dylib $@ tcutest : tcutest.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tcumttest : tcumttest.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tcucodec : tcucodec.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tchtest : tchtest.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tchmttest : tchmttest.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tchmgr : tchmgr.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tcbtest : tcbtest.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tcbmttest : tcbmttest.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tcbmgr : tcbmgr.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tcftest : tcftest.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tcfmttest : tcfmttest.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tcfmgr : tcfmgr.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tcttest : tcttest.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tctmttest : tctmttest.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tctmgr : tctmgr.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tcatest : tcatest.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tcamttest : tcamttest.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tcamgr : tcamgr.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(LIBS) tcawmgr.cgi : tcawmgr.o $(LIBRARYFILES) $(LDENV) $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) $(CMDLDFLAGS) -ltokyocabinet $(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 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 tokyocabinet-1.4.48/tcadb.c0000644000175000017500000040042512013574446014541 0ustar mikiomikio/************************************************************************************************* * The abstract database API of Tokyo Cabinet * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include "tcutil.h" #include "tchdb.h" #include "tcbdb.h" #include "tcfdb.h" #include "tctdb.h" #include "tcadb.h" #include "myconf.h" #define ADBDIRMODE 00755 // permission of created directories #define ADBMULPREFIX "adbmul-" // prefix of multiple database files typedef struct { // type of structure for multiple database TCADB **adbs; // inner database objects int num; // number of inner databases int iter; // index of the iterator char *path; // path of the base directory } ADBMUL; typedef struct { // type of structure for mapper to B+ tree database TCADB *adb; // source database object TCBDB *bdb; // destination database object TCLIST *recs; // cached records int64_t rsiz; // total size of cached records int64_t csiz; // capacity of cached records ADBMAPPROC proc; // mapping function void *op; // opaque object for the mapping function } ADBMAPBDB; /* private function prototypes */ static ADBMUL *tcadbmulnew(int num); static void tcadbmuldel(ADBMUL *mul); static bool tcadbmulopen(ADBMUL *mul, const char *name); static bool tcadbmulclose(ADBMUL *mul); static bool tcadbmulput(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz); static bool tcadbmulputkeep(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz); static bool tcadbmulputcat(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz); static bool tcadbmulout(ADBMUL *mul, const void *kbuf, int ksiz); static void *tcadbmulget(ADBMUL *mul, const void *kbuf, int ksiz, int *sp); static int tcadbmulvsiz(ADBMUL *mul, const void *kbuf, int ksiz); static bool tcadbmuliterinit(ADBMUL *mul); static void *tcadbmuliternext(ADBMUL *mul, int *sp); static TCLIST *tcadbmulfwmkeys(ADBMUL *mul, const void *pbuf, int psiz, int max); static int tcadbmuladdint(ADBMUL *mul, const void *kbuf, int ksiz, int num); static double tcadbmuladddouble(ADBMUL *mul, const void *kbuf, int ksiz, double num); static bool tcadbmulsync(ADBMUL *mul); static bool tcadbmuloptimize(ADBMUL *mul, const char *params); static bool tcadbmulvanish(ADBMUL *mul); static bool tcadbmulcopy(ADBMUL *mul, const char *path); static bool tcadbmultranbegin(ADBMUL *mul); static bool tcadbmultrancommit(ADBMUL *mul); static bool tcadbmultranabort(ADBMUL *mul); static const char *tcadbmulpath(ADBMUL *mul); static uint64_t tcadbmulrnum(ADBMUL *mul); static uint64_t tcadbmulsize(ADBMUL *mul); static TCLIST *tcadbmulmisc(ADBMUL *mul, const char *name, const TCLIST *args); static bool tcadbmulputproc(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op); static bool tcadbmulforeach(ADBMUL *mul, TCITER iter, void *op); static int tcadbmulidx(ADBMUL *mul, const void *kbuf, int ksiz); static bool tcadbmapbdbiter(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op); static bool tcadbmapbdbdump(ADBMAPBDB *map); static int tcadbmapreccmplexical(const TCLISTDATUM *a, const TCLISTDATUM *b); static int tcadbmapreccmpdecimal(const TCLISTDATUM *a, const TCLISTDATUM *b); static int tcadbmapreccmpint32(const TCLISTDATUM *a, const TCLISTDATUM *b); static int tcadbmapreccmpint64(const TCLISTDATUM *a, const TCLISTDATUM *b); static int tcadbtdbqrygetout(const void *pkbuf, int pksiz, TCMAP *cols, void *op); /************************************************************************************************* * API *************************************************************************************************/ /* Create an abstract database object. */ TCADB *tcadbnew(void){ TCADB *adb; TCMALLOC(adb, sizeof(*adb)); adb->omode = ADBOVOID; adb->mdb = NULL; adb->ndb = NULL; adb->hdb = NULL; adb->bdb = NULL; adb->fdb = NULL; adb->tdb = NULL; adb->capnum = -1; adb->capsiz = -1; adb->capcnt = 0; adb->cur = NULL; adb->skel = NULL; return adb; } /* Delete an abstract database object. */ void tcadbdel(TCADB *adb){ assert(adb); if(adb->omode != ADBOVOID) tcadbclose(adb); if(adb->skel){ ADBSKEL *skel = adb->skel; if(skel->del) skel->del(skel->opq); TCFREE(skel); } TCFREE(adb); } /* Open an abstract database. */ bool tcadbopen(TCADB *adb, const char *name){ assert(adb && name); if(adb->omode != ADBOVOID) return false; TCLIST *elems = tcstrsplit(name, "#"); char *path = tclistshift2(elems); if(!path){ tclistdel(elems); return false; } int dbgfd = -1; int64_t bnum = -1; int64_t capnum = -1; int64_t capsiz = -1; bool owmode = true; bool ocmode = true; bool otmode = false; bool onlmode = false; bool onbmode = false; int8_t apow = -1; int8_t fpow = -1; bool tlmode = false; bool tdmode = false; bool tbmode = false; bool ttmode = false; int32_t rcnum = -1; int64_t xmsiz = -1; int32_t dfunit = -1; int32_t lmemb = -1; int32_t nmemb = -1; int32_t lcnum = -1; int32_t ncnum = -1; int32_t width = -1; int64_t limsiz = -1; TCLIST *idxs = NULL; int ln = TCLISTNUM(elems); for(int i = 0; i < ln; i++){ const char *elem = TCLISTVALPTR(elems, i); char *pv = strchr(elem, '='); if(!pv) continue; *(pv++) = '\0'; if(!tcstricmp(elem, "dbgfd")){ dbgfd = tcatoi(pv); } else if(!tcstricmp(elem, "bnum")){ bnum = tcatoix(pv); } else if(!tcstricmp(elem, "capnum")){ capnum = tcatoix(pv); } else if(!tcstricmp(elem, "capsiz")){ capsiz = tcatoix(pv); } else if(!tcstricmp(elem, "mode")){ owmode = strchr(pv, 'w') || strchr(pv, 'W'); ocmode = strchr(pv, 'c') || strchr(pv, 'C'); otmode = strchr(pv, 't') || strchr(pv, 'T'); onlmode = strchr(pv, 'e') || strchr(pv, 'E'); onbmode = strchr(pv, 'f') || strchr(pv, 'F'); } else if(!tcstricmp(elem, "apow")){ apow = tcatoix(pv); } else if(!tcstricmp(elem, "fpow")){ fpow = tcatoix(pv); } else if(!tcstricmp(elem, "opts")){ if(strchr(pv, 'l') || strchr(pv, 'L')) tlmode = true; if(strchr(pv, 'd') || strchr(pv, 'D')) tdmode = true; if(strchr(pv, 'b') || strchr(pv, 'B')) tbmode = true; if(strchr(pv, 't') || strchr(pv, 'T')) ttmode = true; } else if(!tcstricmp(elem, "rcnum")){ rcnum = tcatoix(pv); } else if(!tcstricmp(elem, "xmsiz")){ xmsiz = tcatoix(pv); } else if(!tcstricmp(elem, "dfunit")){ dfunit = tcatoix(pv); } else if(!tcstricmp(elem, "lmemb")){ lmemb = tcatoix(pv); } else if(!tcstricmp(elem, "nmemb")){ nmemb = tcatoix(pv); } else if(!tcstricmp(elem, "lcnum")){ lcnum = tcatoix(pv); } else if(!tcstricmp(elem, "ncnum")){ ncnum = tcatoix(pv); } else if(!tcstricmp(elem, "width")){ width = tcatoix(pv); } else if(!tcstricmp(elem, "limsiz")){ limsiz = tcatoix(pv); } else if(!tcstricmp(elem, "idx")){ if(!idxs) idxs = tclistnew(); TCLISTPUSH(idxs, pv, strlen(pv)); } } tclistdel(elems); adb->omode = ADBOVOID; if(adb->skel){ ADBSKEL *skel = adb->skel; if(!skel->open || !skel->open(skel->opq, name)){ if(idxs) tclistdel(idxs); TCFREE(path); return false; } adb->omode = ADBOSKEL; } else if(!tcstricmp(path, "*")){ adb->mdb = bnum > 0 ? tcmdbnew2(bnum) : tcmdbnew(); adb->capnum = capnum; adb->capsiz = capsiz; adb->capcnt = 0; adb->omode = ADBOMDB; } else if(!tcstricmp(path, "+")){ adb->ndb = tcndbnew(); adb->capnum = capnum; adb->capsiz = capsiz; adb->capcnt = 0; adb->omode = ADBONDB; } else if(tcstribwm(path, ".tch") || tcstribwm(path, ".hdb")){ TCHDB *hdb = tchdbnew(); if(dbgfd >= 0) tchdbsetdbgfd(hdb, dbgfd); tchdbsetmutex(hdb); int opts = 0; if(tlmode) opts |= HDBTLARGE; if(tdmode) opts |= HDBTDEFLATE; if(tbmode) opts |= HDBTBZIP; if(ttmode) opts |= HDBTTCBS; tchdbtune(hdb, bnum, apow, fpow, opts); tchdbsetcache(hdb, rcnum); if(xmsiz >= 0) tchdbsetxmsiz(hdb, xmsiz); if(dfunit >= 0) tchdbsetdfunit(hdb, dfunit); int omode = owmode ? HDBOWRITER : HDBOREADER; if(ocmode) omode |= HDBOCREAT; if(otmode) omode |= HDBOTRUNC; if(onlmode) omode |= HDBONOLCK; if(onbmode) omode |= HDBOLCKNB; if(!tchdbopen(hdb, path, omode)){ tchdbdel(hdb); if(idxs) tclistdel(idxs); TCFREE(path); return false; } adb->hdb = hdb; adb->omode = ADBOHDB; } else if(tcstribwm(path, ".tcb") || tcstribwm(path, ".bdb")){ TCBDB *bdb = tcbdbnew(); if(dbgfd >= 0) tcbdbsetdbgfd(bdb, dbgfd); tcbdbsetmutex(bdb); int opts = 0; if(tlmode) opts |= BDBTLARGE; if(tdmode) opts |= BDBTDEFLATE; if(tbmode) opts |= BDBTBZIP; if(ttmode) opts |= BDBTTCBS; tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts); tcbdbsetcache(bdb, lcnum, ncnum); if(xmsiz >= 0) tcbdbsetxmsiz(bdb, xmsiz); if(dfunit >= 0) tcbdbsetdfunit(bdb, dfunit); if(capnum > 0) tcbdbsetcapnum(bdb, capnum); int omode = owmode ? BDBOWRITER : BDBOREADER; if(ocmode) omode |= BDBOCREAT; if(otmode) omode |= BDBOTRUNC; if(onlmode) omode |= BDBONOLCK; if(onbmode) omode |= BDBOLCKNB; if(!tcbdbopen(bdb, path, omode)){ tcbdbdel(bdb); if(idxs) tclistdel(idxs); TCFREE(path); return false; } adb->bdb = bdb; adb->cur = tcbdbcurnew(bdb); adb->omode = ADBOBDB; } else if(tcstribwm(path, ".tcf") || tcstribwm(path, ".fdb")){ TCFDB *fdb = tcfdbnew(); if(dbgfd >= 0) tcfdbsetdbgfd(fdb, dbgfd); tcfdbsetmutex(fdb); tcfdbtune(fdb, width, limsiz); int omode = owmode ? FDBOWRITER : FDBOREADER; if(ocmode) omode |= FDBOCREAT; if(otmode) omode |= FDBOTRUNC; if(onlmode) omode |= FDBONOLCK; if(onbmode) omode |= FDBOLCKNB; if(!tcfdbopen(fdb, path, omode)){ tcfdbdel(fdb); if(idxs) tclistdel(idxs); TCFREE(path); return false; } adb->fdb = fdb; adb->omode = ADBOFDB; } else if(tcstribwm(path, ".tct") || tcstribwm(path, ".tdb")){ TCTDB *tdb = tctdbnew(); if(dbgfd >= 0) tctdbsetdbgfd(tdb, dbgfd); tctdbsetmutex(tdb); int opts = 0; if(tlmode) opts |= TDBTLARGE; if(tdmode) opts |= TDBTDEFLATE; if(tbmode) opts |= TDBTBZIP; if(ttmode) opts |= TDBTTCBS; tctdbtune(tdb, bnum, apow, fpow, opts); tctdbsetcache(tdb, rcnum, lcnum, ncnum); if(xmsiz >= 0) tctdbsetxmsiz(tdb, xmsiz); if(dfunit >= 0) tctdbsetdfunit(tdb, dfunit); int omode = owmode ? TDBOWRITER : TDBOREADER; if(ocmode) omode |= TDBOCREAT; if(otmode) omode |= TDBOTRUNC; if(onlmode) omode |= TDBONOLCK; if(onbmode) omode |= TDBOLCKNB; if(!tctdbopen(tdb, path, omode)){ tctdbdel(tdb); if(idxs) tclistdel(idxs); TCFREE(path); return false; } if(idxs){ int xnum = TCLISTNUM(idxs); for(int i = 0; i < xnum; i++){ const char *expr = TCLISTVALPTR(idxs, i); int type = TDBITLEXICAL; char *pv = strchr(expr, ':'); if(pv){ *(pv++) = '\0'; type = tctdbstrtoindextype(pv); } if(type >= 0) tctdbsetindex(tdb, expr, type | TDBITKEEP); } } adb->tdb = tdb; adb->omode = ADBOTDB; } if(idxs) tclistdel(idxs); TCFREE(path); if(adb->omode == ADBOVOID) return false; return true; } /* Close an abstract database object. */ bool tcadbclose(TCADB *adb){ assert(adb); int err = false; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: tcmdbdel(adb->mdb); adb->mdb = NULL; break; case ADBONDB: tcndbdel(adb->ndb); adb->ndb = NULL; break; case ADBOHDB: if(!tchdbclose(adb->hdb)) err = true; tchdbdel(adb->hdb); adb->hdb = NULL; break; case ADBOBDB: tcbdbcurdel(adb->cur); if(!tcbdbclose(adb->bdb)) err = true; tcbdbdel(adb->bdb); adb->bdb = NULL; break; case ADBOFDB: if(!tcfdbclose(adb->fdb)) err = true; tcfdbdel(adb->fdb); adb->fdb = NULL; break; case ADBOTDB: if(!tctdbclose(adb->tdb)) err = true; tctdbdel(adb->tdb); adb->tdb = NULL; break; case ADBOSKEL: skel = adb->skel; if(skel->close){ if(!skel->close(skel->opq)) err = true; } else { err = true; } break; default: err = true; break; } adb->omode = ADBOVOID; return !err; } /* Store a record into an abstract database object. */ bool tcadbput(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); bool err = false; char numbuf[TCNUMBUFSIZ]; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: if(adb->capnum > 0 || adb->capsiz > 0){ tcmdbput3(adb->mdb, kbuf, ksiz, vbuf, vsiz); adb->capcnt++; if((adb->capcnt & 0xff) == 0){ if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum + 0x100) tcmdbcutfront(adb->mdb, 0x100); if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz) tcmdbcutfront(adb->mdb, 0x200); } } else { tcmdbput(adb->mdb, kbuf, ksiz, vbuf, vsiz); } break; case ADBONDB: tcndbput(adb->ndb, kbuf, ksiz, vbuf, vsiz); if(adb->capnum > 0 || adb->capsiz > 0){ adb->capcnt++; if((adb->capcnt & 0xff) == 0){ if(adb->capnum > 0 && tcndbrnum(adb->ndb) > adb->capnum + 0x100) tcndbcutfringe(adb->ndb, 0x100); if(adb->capsiz > 0 && tcndbmsiz(adb->ndb) > adb->capsiz) tcndbcutfringe(adb->ndb, 0x200); } } break; case ADBOHDB: if(!tchdbput(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true; break; case ADBOBDB: if(!tcbdbput(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true; break; case ADBOFDB: if(!tcfdbput2(adb->fdb, kbuf, ksiz, vbuf, vsiz)) err = true; break; case ADBOTDB: if(ksiz < 1){ ksiz = sprintf(numbuf, "%lld", (long long)tctdbgenuid(adb->tdb)); kbuf = numbuf; } if(!tctdbput2(adb->tdb, kbuf, ksiz, vbuf, vsiz)) err = true; break; case ADBOSKEL: skel = adb->skel; if(skel->put){ if(!skel->put(skel->opq, kbuf, ksiz, vbuf, vsiz)) err = true; } else { err = true; } break; default: err = true; break; } return !err; } /* Store a string record into an abstract object. */ bool tcadbput2(TCADB *adb, const char *kstr, const char *vstr){ assert(adb && kstr && vstr); return tcadbput(adb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Store a new record into an abstract database object. */ bool tcadbputkeep(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); bool err = false; char numbuf[TCNUMBUFSIZ]; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: if(tcmdbputkeep(adb->mdb, kbuf, ksiz, vbuf, vsiz)){ if(adb->capnum > 0 || adb->capsiz > 0){ adb->capcnt++; if((adb->capcnt & 0xff) == 0){ if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum + 0x100) tcmdbcutfront(adb->mdb, 0x100); if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz) tcmdbcutfront(adb->mdb, 0x200); } } } else { err = true; } break; case ADBONDB: if(tcndbputkeep(adb->ndb, kbuf, ksiz, vbuf, vsiz)){ if(adb->capnum > 0 || adb->capsiz > 0){ adb->capcnt++; if((adb->capcnt & 0xff) == 0){ if(adb->capnum > 0 && tcndbrnum(adb->ndb) > adb->capnum + 0x100) tcndbcutfringe(adb->ndb, 0x100); if(adb->capsiz > 0 && tcndbmsiz(adb->ndb) > adb->capsiz) tcndbcutfringe(adb->ndb, 0x200); } } } else { err = true; } break; case ADBOHDB: if(!tchdbputkeep(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true; break; case ADBOBDB: if(!tcbdbputkeep(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true; break; case ADBOFDB: if(!tcfdbputkeep2(adb->fdb, kbuf, ksiz, vbuf, vsiz)) err = true; break; case ADBOTDB: if(ksiz < 1){ ksiz = sprintf(numbuf, "%lld", (long long)tctdbgenuid(adb->tdb)); kbuf = numbuf; } if(!tctdbputkeep2(adb->tdb, kbuf, ksiz, vbuf, vsiz)) err = true; break; case ADBOSKEL: skel = adb->skel; if(skel->putkeep){ if(!skel->putkeep(skel->opq, kbuf, ksiz, vbuf, vsiz)) err = true; } else { err = true; } break; default: err = true; break; } return !err; } /* Store a new string record into an abstract database object. */ bool tcadbputkeep2(TCADB *adb, const char *kstr, const char *vstr){ assert(adb && kstr && vstr); return tcadbputkeep(adb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* 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){ assert(adb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); bool err = false; char numbuf[TCNUMBUFSIZ]; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: if(adb->capnum > 0 || adb->capsiz > 0){ tcmdbputcat3(adb->mdb, kbuf, ksiz, vbuf, vsiz); adb->capcnt++; if((adb->capcnt & 0xff) == 0){ if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum + 0x100) tcmdbcutfront(adb->mdb, 0x100); if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz) tcmdbcutfront(adb->mdb, 0x200); } } else { tcmdbputcat(adb->mdb, kbuf, ksiz, vbuf, vsiz); } break; case ADBONDB: tcndbputcat(adb->ndb, kbuf, ksiz, vbuf, vsiz); if(adb->capnum > 0 || adb->capsiz > 0){ adb->capcnt++; if((adb->capcnt & 0xff) == 0){ if(adb->capnum > 0 && tcndbrnum(adb->ndb) > adb->capnum + 0x100) tcndbcutfringe(adb->ndb, 0x100); if(adb->capsiz > 0 && tcndbmsiz(adb->ndb) > adb->capsiz) tcndbcutfringe(adb->ndb, 0x200); } } break; case ADBOHDB: if(!tchdbputcat(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true; break; case ADBOBDB: if(!tcbdbputcat(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true; break; case ADBOFDB: if(!tcfdbputcat2(adb->fdb, kbuf, ksiz, vbuf, vsiz)) err = true; break; case ADBOTDB: if(ksiz < 1){ ksiz = sprintf(numbuf, "%lld", (long long)tctdbgenuid(adb->tdb)); kbuf = numbuf; } if(!tctdbputcat2(adb->tdb, kbuf, ksiz, vbuf, vsiz)) err = true; break; case ADBOSKEL: skel = adb->skel; if(skel->putcat){ if(!skel->putcat(skel->opq, kbuf, ksiz, vbuf, vsiz)) err = true; } else { err = true; } break; default: err = true; break; } return !err; } /* 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){ assert(adb && kstr && vstr); return tcadbputcat(adb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Remove a record of an abstract database object. */ bool tcadbout(TCADB *adb, const void *kbuf, int ksiz){ assert(adb && kbuf && ksiz >= 0); bool err = false; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: if(!tcmdbout(adb->mdb, kbuf, ksiz)) err = true; break; case ADBONDB: if(!tcndbout(adb->ndb, kbuf, ksiz)) err = true; break; case ADBOHDB: if(!tchdbout(adb->hdb, kbuf, ksiz)) err = true; break; case ADBOBDB: if(!tcbdbout(adb->bdb, kbuf, ksiz)) err = true; break; case ADBOFDB: if(!tcfdbout2(adb->fdb, kbuf, ksiz)) err = true; break; case ADBOTDB: if(!tctdbout(adb->tdb, kbuf, ksiz)) err = true; break; case ADBOSKEL: skel = adb->skel; if(skel->out){ if(!skel->out(skel->opq, kbuf, ksiz)) err = true; } else { err = true; } break; default: err = true; break; } return !err; } /* Remove a string record of an abstract database object. */ bool tcadbout2(TCADB *adb, const char *kstr){ assert(adb && kstr); return tcadbout(adb, kstr, strlen(kstr)); } /* Retrieve a record in an abstract database object. */ void *tcadbget(TCADB *adb, const void *kbuf, int ksiz, int *sp){ assert(adb && kbuf && ksiz >= 0 && sp); char *rv; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: rv = tcmdbget(adb->mdb, kbuf, ksiz, sp); break; case ADBONDB: rv = tcndbget(adb->ndb, kbuf, ksiz, sp); break; case ADBOHDB: rv = tchdbget(adb->hdb, kbuf, ksiz, sp); break; case ADBOBDB: rv = tcbdbget(adb->bdb, kbuf, ksiz, sp); break; case ADBOFDB: rv = tcfdbget2(adb->fdb, kbuf, ksiz, sp); break; case ADBOTDB: rv = tctdbget2(adb->tdb, kbuf, ksiz, sp); break; case ADBOSKEL: skel = adb->skel; if(skel->get){ rv = skel->get(skel->opq, kbuf, ksiz, sp); } else { rv = NULL; } break; default: rv = NULL; break; } return rv; } /* Retrieve a string record in an abstract database object. */ char *tcadbget2(TCADB *adb, const char *kstr){ assert(adb && kstr); int vsiz; return tcadbget(adb, kstr, strlen(kstr), &vsiz); } /* Get the size of the value of a record in an abstract database object. */ int tcadbvsiz(TCADB *adb, const void *kbuf, int ksiz){ assert(adb && kbuf && ksiz >= 0); int rv; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: rv = tcmdbvsiz(adb->mdb, kbuf, ksiz); break; case ADBONDB: rv = tcndbvsiz(adb->ndb, kbuf, ksiz); break; case ADBOHDB: rv = tchdbvsiz(adb->hdb, kbuf, ksiz); break; case ADBOBDB: rv = tcbdbvsiz(adb->bdb, kbuf, ksiz); break; case ADBOFDB: rv = tcfdbvsiz2(adb->fdb, kbuf, ksiz); break; case ADBOTDB: rv = tctdbvsiz(adb->tdb, kbuf, ksiz); break; case ADBOSKEL: skel = adb->skel; if(skel->vsiz){ rv = skel->vsiz(skel->opq, kbuf, ksiz); } else { rv = -1; } break; default: rv = -1; break; } return rv; } /* Get the size of the value of a string record in an abstract database object. */ int tcadbvsiz2(TCADB *adb, const char *kstr){ assert(adb && kstr); return tcadbvsiz(adb, kstr, strlen(kstr)); } /* Initialize the iterator of an abstract database object. */ bool tcadbiterinit(TCADB *adb){ assert(adb); bool err = false; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: tcmdbiterinit(adb->mdb); break; case ADBONDB: tcndbiterinit(adb->ndb); break; case ADBOHDB: if(!tchdbiterinit(adb->hdb)) err = true; break; case ADBOBDB: if(!tcbdbcurfirst(adb->cur)){ int ecode = tcbdbecode(adb->bdb); if(ecode != TCESUCCESS && ecode != TCEINVALID && ecode != TCEKEEP && ecode != TCENOREC) err = true; } break; case ADBOFDB: if(!tcfdbiterinit(adb->fdb)) err = true; break; case ADBOTDB: if(!tctdbiterinit(adb->tdb)) err = true; break; case ADBOSKEL: skel = adb->skel; if(skel->iterinit){ if(!skel->iterinit(skel->opq)) err = true; } else { err = true; } break; default: err = true; break; } return !err; } /* Get the next key of the iterator of an abstract database object. */ void *tcadbiternext(TCADB *adb, int *sp){ assert(adb && sp); char *rv; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: rv = tcmdbiternext(adb->mdb, sp); break; case ADBONDB: rv = tcndbiternext(adb->ndb, sp); break; case ADBOHDB: rv = tchdbiternext(adb->hdb, sp); break; case ADBOBDB: rv = tcbdbcurkey(adb->cur, sp); tcbdbcurnext(adb->cur); break; case ADBOFDB: rv = tcfdbiternext2(adb->fdb, sp); break; case ADBOTDB: rv = tctdbiternext(adb->tdb, sp); break; case ADBOSKEL: skel = adb->skel; if(skel->iternext){ rv = skel->iternext(skel->opq, sp); } else { rv = NULL; } break; default: rv = NULL; break; } return rv; } /* Get the next key string of the iterator of an abstract database object. */ char *tcadbiternext2(TCADB *adb){ assert(adb); int vsiz; return tcadbiternext(adb, &vsiz); } /* Get forward matching keys in an abstract database object. */ TCLIST *tcadbfwmkeys(TCADB *adb, const void *pbuf, int psiz, int max){ assert(adb && pbuf && psiz >= 0); TCLIST *rv; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: rv = tcmdbfwmkeys(adb->mdb, pbuf, psiz, max); break; case ADBONDB: rv = tcndbfwmkeys(adb->ndb, pbuf, psiz, max); break; case ADBOHDB: rv = tchdbfwmkeys(adb->hdb, pbuf, psiz, max); break; case ADBOBDB: rv = tcbdbfwmkeys(adb->bdb, pbuf, psiz, max); break; case ADBOFDB: rv = tcfdbrange4(adb->fdb, pbuf, psiz, max); break; case ADBOTDB: rv = tctdbfwmkeys(adb->tdb, pbuf, psiz, max); break; case ADBOSKEL: skel = adb->skel; if(skel->fwmkeys){ rv = skel->fwmkeys(skel->opq, pbuf, psiz, max); } else { rv = NULL; } break; default: rv = tclistnew(); break; } return rv; } /* Get forward matching string keys in an abstract database object. */ TCLIST *tcadbfwmkeys2(TCADB *adb, const char *pstr, int max){ assert(adb && pstr); return tcadbfwmkeys(adb, pstr, strlen(pstr), max); } /* Add an integer to a record in an abstract database object. */ int tcadbaddint(TCADB *adb, const void *kbuf, int ksiz, int num){ assert(adb && kbuf && ksiz >= 0); int rv; char numbuf[TCNUMBUFSIZ]; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: rv = tcmdbaddint(adb->mdb, kbuf, ksiz, num); if(adb->capnum > 0 || adb->capsiz > 0){ adb->capcnt++; if((adb->capcnt & 0xff) == 0){ if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum + 0x100) tcmdbcutfront(adb->mdb, 0x100); if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz) tcmdbcutfront(adb->mdb, 0x200); } } break; case ADBONDB: rv = tcndbaddint(adb->ndb, kbuf, ksiz, num); if(adb->capnum > 0 || adb->capsiz > 0){ adb->capcnt++; if((adb->capcnt & 0xff) == 0){ if(adb->capnum > 0 && tcndbrnum(adb->ndb) > adb->capnum + 0x100) tcndbcutfringe(adb->ndb, 0x100); if(adb->capsiz > 0 && tcndbmsiz(adb->ndb) > adb->capsiz) tcndbcutfringe(adb->ndb, 0x200); } } break; case ADBOHDB: rv = tchdbaddint(adb->hdb, kbuf, ksiz, num); break; case ADBOBDB: rv = tcbdbaddint(adb->bdb, kbuf, ksiz, num); break; case ADBOFDB: rv = tcfdbaddint(adb->fdb, tcfdbkeytoid(kbuf, ksiz), num); break; case ADBOTDB: if(ksiz < 1){ ksiz = sprintf(numbuf, "%lld", (long long)tctdbgenuid(adb->tdb)); kbuf = numbuf; } rv = tctdbaddint(adb->tdb, kbuf, ksiz, num); break; case ADBOSKEL: skel = adb->skel; if(skel->addint){ rv = skel->addint(skel->opq, kbuf, ksiz, num); } else { rv = INT_MIN; } break; default: rv = INT_MIN; break; } return rv; } /* Add a real number to a record in an abstract database object. */ double tcadbadddouble(TCADB *adb, const void *kbuf, int ksiz, double num){ assert(adb && kbuf && ksiz >= 0); double rv; char numbuf[TCNUMBUFSIZ]; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: rv = tcmdbadddouble(adb->mdb, kbuf, ksiz, num); if(adb->capnum > 0 || adb->capsiz > 0){ adb->capcnt++; if((adb->capcnt & 0xff) == 0){ if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum + 0x100) tcmdbcutfront(adb->mdb, 0x100); if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz) tcmdbcutfront(adb->mdb, 0x200); } } break; case ADBONDB: rv = tcndbadddouble(adb->ndb, kbuf, ksiz, num); if(adb->capnum > 0 || adb->capsiz > 0){ adb->capcnt++; if((adb->capcnt & 0xff) == 0){ if(adb->capnum > 0 && tcndbrnum(adb->ndb) > adb->capnum + 0x100) tcndbcutfringe(adb->ndb, 0x100); if(adb->capsiz > 0 && tcndbmsiz(adb->ndb) > adb->capsiz) tcndbcutfringe(adb->ndb, 0x200); } } break; case ADBOHDB: rv = tchdbadddouble(adb->hdb, kbuf, ksiz, num); break; case ADBOBDB: rv = tcbdbadddouble(adb->bdb, kbuf, ksiz, num); break; case ADBOFDB: rv = tcfdbadddouble(adb->fdb, tcfdbkeytoid(kbuf, ksiz), num); break; case ADBOTDB: if(ksiz < 1){ ksiz = sprintf(numbuf, "%lld", (long long)tctdbgenuid(adb->tdb)); kbuf = numbuf; } rv = tctdbadddouble(adb->tdb, kbuf, ksiz, num); break; case ADBOSKEL: skel = adb->skel; if(skel->adddouble){ rv = skel->adddouble(skel->opq, kbuf, ksiz, num); } else { rv = nan(""); } break; default: rv = nan(""); break; } return rv; } /* Synchronize updated contents of an abstract database object with the file and the device. */ bool tcadbsync(TCADB *adb){ assert(adb); bool err = false; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: if(adb->capnum > 0){ while(tcmdbrnum(adb->mdb) > adb->capnum){ tcmdbcutfront(adb->mdb, 1); } } if(adb->capsiz > 0){ while(tcmdbmsiz(adb->mdb) > adb->capsiz && tcmdbrnum(adb->mdb) > 0){ tcmdbcutfront(adb->mdb, 1); } } adb->capcnt = 0; break; case ADBONDB: if(adb->capnum > 0 && tcndbrnum(adb->ndb) > adb->capnum) tcndbcutfringe(adb->ndb, tcndbrnum(adb->ndb) - adb->capnum); if(adb->capsiz > 0){ while(tcndbmsiz(adb->ndb) > adb->capsiz && tcndbrnum(adb->ndb) > 0){ tcndbcutfringe(adb->ndb, 0x100); } } adb->capcnt = 0; break; case ADBOHDB: if(!tchdbsync(adb->hdb)) err = true; break; case ADBOBDB: if(!tcbdbsync(adb->bdb)) err = true; break; case ADBOFDB: if(!tcfdbsync(adb->fdb)) err = true; break; case ADBOTDB: if(!tctdbsync(adb->tdb)) err = true; break; case ADBOSKEL: skel = adb->skel; if(skel->sync){ if(!skel->sync(skel->opq)) err = true; } else { err = true; } break; default: err = true; break; } return !err; } /* Optimize the storage of an abstract database object. */ bool tcadboptimize(TCADB *adb, const char *params){ assert(adb); TCLIST *elems = params ? tcstrsplit(params, "#") : tclistnew(); int64_t bnum = -1; int64_t capnum = -1; int64_t capsiz = -1; int8_t apow = -1; int8_t fpow = -1; bool tdefault = true; bool tlmode = false; bool tdmode = false; bool tbmode = false; bool ttmode = false; int32_t lmemb = -1; int32_t nmemb = -1; int32_t width = -1; int64_t limsiz = -1; int ln = TCLISTNUM(elems); for(int i = 0; i < ln; i++){ const char *elem = TCLISTVALPTR(elems, i); char *pv = strchr(elem, '='); if(!pv) continue; *(pv++) = '\0'; if(!tcstricmp(elem, "bnum")){ bnum = tcatoix(pv); } else if(!tcstricmp(elem, "capnum")){ capnum = tcatoix(pv); } else if(!tcstricmp(elem, "capsiz")){ capsiz = tcatoix(pv); } else if(!tcstricmp(elem, "apow")){ apow = tcatoix(pv); } else if(!tcstricmp(elem, "fpow")){ fpow = tcatoix(pv); } else if(!tcstricmp(elem, "opts")){ tdefault = false; if(strchr(pv, 'l') || strchr(pv, 'L')) tlmode = true; if(strchr(pv, 'd') || strchr(pv, 'D')) tdmode = true; if(strchr(pv, 'b') || strchr(pv, 'B')) tbmode = true; if(strchr(pv, 't') || strchr(pv, 'T')) ttmode = true; } else if(!tcstricmp(elem, "lmemb")){ lmemb = tcatoix(pv); } else if(!tcstricmp(elem, "nmemb")){ nmemb = tcatoix(pv); } else if(!tcstricmp(elem, "width")){ width = tcatoix(pv); } else if(!tcstricmp(elem, "limsiz")){ limsiz = tcatoix(pv); } } tclistdel(elems); bool err = false; int opts; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: adb->capnum = capnum; adb->capsiz = capsiz; tcadbsync(adb); break; case ADBONDB: adb->capnum = capnum; adb->capsiz = capsiz; tcadbsync(adb); break; case ADBOHDB: opts = 0; if(tdefault){ opts = UINT8_MAX; } else { if(tlmode) opts |= HDBTLARGE; if(tdmode) opts |= HDBTDEFLATE; if(tbmode) opts |= HDBTBZIP; if(ttmode) opts |= HDBTTCBS; } if(!tchdboptimize(adb->hdb, bnum, apow, fpow, opts)) err = true; break; case ADBOBDB: opts = 0; if(tdefault){ opts = UINT8_MAX; } else { if(tlmode) opts |= BDBTLARGE; if(tdmode) opts |= BDBTDEFLATE; if(tbmode) opts |= BDBTBZIP; if(ttmode) opts |= BDBTTCBS; } if(!tcbdboptimize(adb->bdb, lmemb, nmemb, bnum, apow, fpow, opts)) err = true; break; case ADBOFDB: if(!tcfdboptimize(adb->fdb, width, limsiz)) err = true; break; case ADBOTDB: opts = 0; if(tdefault){ opts = UINT8_MAX; } else { if(tlmode) opts |= TDBTLARGE; if(tdmode) opts |= TDBTDEFLATE; if(tbmode) opts |= TDBTBZIP; if(ttmode) opts |= TDBTTCBS; } if(!tctdboptimize(adb->tdb, bnum, apow, fpow, opts)) err = true; break; case ADBOSKEL: skel = adb->skel; if(skel->optimize){ if(!skel->optimize(skel->opq, params)) err = true; } else { err = true; } break; default: err = true; break; } return !err; } /* Remove all records of an abstract database object. */ bool tcadbvanish(TCADB *adb){ assert(adb); bool err = false; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: tcmdbvanish(adb->mdb); break; case ADBONDB: tcndbvanish(adb->ndb); break; case ADBOHDB: if(!tchdbvanish(adb->hdb)) err = true; break; case ADBOBDB: if(!tcbdbvanish(adb->bdb)) err = true; break; case ADBOFDB: if(!tcfdbvanish(adb->fdb)) err = true; break; case ADBOTDB: if(!tctdbvanish(adb->tdb)) err = true; break; case ADBOSKEL: skel = adb->skel; if(skel->vanish){ if(!skel->vanish(skel->opq)) err = true; } else { err = true; } break; default: err = true; break; } return !err; } /* Copy the database file of an abstract database object. */ bool tcadbcopy(TCADB *adb, const char *path){ assert(adb && path); bool err = false; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: case ADBONDB: if(*path == '@'){ char tsbuf[TCNUMBUFSIZ]; sprintf(tsbuf, "%llu", (unsigned long long)(tctime() * 1000000)); const char *args[2]; args[0] = path + 1; args[1] = tsbuf; if(tcsystem(args, sizeof(args) / sizeof(*args)) != 0) err = true; } else { TCADB *tadb = tcadbnew(); if(tcadbopen(tadb, path)){ tcadbiterinit(adb); char *kbuf; int ksiz; while((kbuf = tcadbiternext(adb, &ksiz)) != NULL){ int vsiz; char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz); if(vbuf){ if(!tcadbput(tadb, kbuf, ksiz, vbuf, vsiz)) err = true; TCFREE(vbuf); } TCFREE(kbuf); } if(!tcadbclose(tadb)) err = true; } else { err = true; } tcadbdel(tadb); } break; case ADBOHDB: if(!tchdbcopy(adb->hdb, path)) err = true; break; case ADBOBDB: if(!tcbdbcopy(adb->bdb, path)) err = true; break; case ADBOFDB: if(!tcfdbcopy(adb->fdb, path)) err = true; break; case ADBOTDB: if(!tctdbcopy(adb->tdb, path)) err = true; break; case ADBOSKEL: skel = adb->skel; if(skel->copy){ if(!skel->copy(skel->opq, path)) err = true; } else { err = true; } break; default: err = true; break; } return !err; } /* Begin the transaction of an abstract database object. */ bool tcadbtranbegin(TCADB *adb){ assert(adb); bool err = false; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: err = true; break; case ADBONDB: err = true; break; case ADBOHDB: if(!tchdbtranbegin(adb->hdb)) err = true; break; case ADBOBDB: if(!tcbdbtranbegin(adb->bdb)) err = true; break; case ADBOFDB: if(!tcfdbtranbegin(adb->fdb)) err = true; break; case ADBOTDB: if(!tctdbtranbegin(adb->tdb)) err = true; break; case ADBOSKEL: skel = adb->skel; if(skel->tranbegin){ if(!skel->tranbegin(skel->opq)) err = true; } else { err = true; } break; default: err = true; break; } return !err; } /* Commit the transaction of an abstract database object. */ bool tcadbtrancommit(TCADB *adb){ assert(adb); bool err = false; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: err = true; break; case ADBONDB: err = true; break; case ADBOHDB: if(!tchdbtrancommit(adb->hdb)) err = true; break; case ADBOBDB: if(!tcbdbtrancommit(adb->bdb)) err = true; break; case ADBOFDB: if(!tcfdbtrancommit(adb->fdb)) err = true; break; case ADBOTDB: if(!tctdbtrancommit(adb->tdb)) err = true; break; case ADBOSKEL: skel = adb->skel; if(skel->trancommit){ if(!skel->trancommit(skel->opq)) err = true; } else { err = true; } break; default: err = true; break; } return !err; } /* Abort the transaction of an abstract database object. */ bool tcadbtranabort(TCADB *adb){ assert(adb); bool err = false; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: err = true; break; case ADBONDB: err = true; break; case ADBOHDB: if(!tchdbtranabort(adb->hdb)) err = true; break; case ADBOBDB: if(!tcbdbtranabort(adb->bdb)) err = true; break; case ADBOFDB: if(!tcfdbtranabort(adb->fdb)) err = true; break; case ADBOTDB: if(!tctdbtranabort(adb->tdb)) err = true; break; case ADBOSKEL: skel = adb->skel; if(skel->tranabort){ if(!skel->tranabort(skel->opq)) err = true; } else { err = true; } break; default: err = true; break; } return !err; } /* Get the file path of an abstract database object. */ const char *tcadbpath(TCADB *adb){ assert(adb); const char *rv; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: rv = "*"; break; case ADBONDB: rv = "+"; break; case ADBOHDB: rv = tchdbpath(adb->hdb); break; case ADBOBDB: rv = tcbdbpath(adb->bdb); break; case ADBOFDB: rv = tcfdbpath(adb->fdb); break; case ADBOTDB: rv = tctdbpath(adb->tdb); break; case ADBOSKEL: skel = adb->skel; if(skel->path){ rv = skel->path(skel->opq); } else { rv = NULL; } break; default: rv = NULL; break; } return rv; } /* Get the number of records of an abstract database object. */ uint64_t tcadbrnum(TCADB *adb){ assert(adb); uint64_t rv; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: rv = tcmdbrnum(adb->mdb); break; case ADBONDB: rv = tcndbrnum(adb->ndb); break; case ADBOHDB: rv = tchdbrnum(adb->hdb); break; case ADBOBDB: rv = tcbdbrnum(adb->bdb); break; case ADBOFDB: rv = tcfdbrnum(adb->fdb); break; case ADBOTDB: rv = tctdbrnum(adb->tdb); break; case ADBOSKEL: skel = adb->skel; if(skel->rnum){ rv = skel->rnum(skel->opq); } else { rv = 0; } break; default: rv = 0; break; } return rv; } /* Get the size of the database of an abstract database object. */ uint64_t tcadbsize(TCADB *adb){ assert(adb); uint64_t rv; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: rv = tcmdbmsiz(adb->mdb); break; case ADBONDB: rv = tcndbmsiz(adb->ndb); break; case ADBOHDB: rv = tchdbfsiz(adb->hdb); break; case ADBOBDB: rv = tcbdbfsiz(adb->bdb); break; case ADBOFDB: rv = tcfdbfsiz(adb->fdb); break; case ADBOTDB: rv = tctdbfsiz(adb->tdb); break; case ADBOSKEL: skel = adb->skel; if(skel->size){ rv = skel->size(skel->opq); } else { rv = 0; } break; default: rv = 0; break; } return rv; } /* Call a versatile function for miscellaneous operations of an abstract database object. */ TCLIST *tcadbmisc(TCADB *adb, const char *name, const TCLIST *args){ assert(adb && name && args); int argc = TCLISTNUM(args); TCLIST *rv; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: if(!strcmp(name, "put") || !strcmp(name, "putkeep") || !strcmp(name, "putcat")){ if(argc > 1){ rv = tclistnew2(1); const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); const char *vbuf; int vsiz; TCLISTVAL(vbuf, args, 1, vsiz); bool err = false; if(!strcmp(name, "put")){ tcmdbput(adb->mdb, kbuf, ksiz, vbuf, vsiz); } else if(!strcmp(name, "putkeep")){ if(!tcmdbputkeep(adb->mdb, kbuf, ksiz, vbuf, vsiz)) err = true; } else if(!strcmp(name, "putcat")){ tcmdbputcat(adb->mdb, kbuf, ksiz, vbuf, vsiz); } if(err){ tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "out")){ if(argc > 0){ rv = tclistnew2(1); const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); if(!tcmdbout(adb->mdb, kbuf, ksiz)){ tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "get")){ if(argc > 0){ rv = tclistnew2(1); const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); int vsiz; char *vbuf = tcmdbget(adb->mdb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); } else { tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "putlist")){ rv = tclistnew2(1); argc--; for(int i = 0; i < argc; i += 2){ const char *kbuf, *vbuf; int ksiz, vsiz; TCLISTVAL(kbuf, args, i, ksiz); TCLISTVAL(vbuf, args, i + 1, vsiz); tcmdbput(adb->mdb, kbuf, ksiz, vbuf, vsiz); } } else if(!strcmp(name, "outlist")){ rv = tclistnew2(1); for(int i = 0; i < argc; i++){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, i, ksiz); tcmdbout(adb->mdb, kbuf, ksiz); } } else if(!strcmp(name, "getlist")){ rv = tclistnew2(argc * 2); for(int i = 0; i < argc; i++){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, i, ksiz); int vsiz; char *vbuf = tcmdbget(adb->mdb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, kbuf, ksiz); TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); } } } else if(!strcmp(name, "getpart")){ if(argc > 0){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); int off = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0; if(off < 0) off = 0; if(off > INT_MAX / 2 - 1) off = INT_MAX - 1; int len = argc > 2 ? tcatoi(TCLISTVALPTR(args, 2)) : -1; if(len < 0 || len > INT_MAX / 2) len = INT_MAX / 2; int vsiz; char *vbuf = tcmdbget(adb->mdb, kbuf, ksiz, &vsiz); if(vbuf){ if(off < vsiz){ rv = tclistnew2(1); vsiz -= off; if(vsiz > len) vsiz = len; if(off > 0) memmove(vbuf, vbuf + off, vsiz); tclistpushmalloc(rv, vbuf, vsiz); } else { rv = NULL; TCFREE(vbuf); } } else { rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "iterinit")){ rv = tclistnew2(1); if(argc > 0){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); tcmdbiterinit2(adb->mdb, kbuf, ksiz); } else { tcmdbiterinit(adb->mdb); } } else if(!strcmp(name, "iternext")){ rv = tclistnew2(1); int ksiz; char *kbuf = tcmdbiternext(adb->mdb, &ksiz); if(kbuf){ TCLISTPUSH(rv, kbuf, ksiz); int vsiz; char *vbuf = tcmdbget(adb->mdb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); } TCFREE(kbuf); } else { tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "sync")){ rv = tclistnew2(1); if(!tcadbsync(adb)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "optimize")){ rv = tclistnew2(1); const char *params = argc > 0 ? TCLISTVALPTR(args, 0) : NULL; if(!tcadboptimize(adb, params)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "vanish")){ rv = tclistnew2(1); if(!tcadbvanish(adb)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "regex")){ if(argc > 0){ const char *regex = TCLISTVALPTR(args, 0); int options = REG_EXTENDED | REG_NOSUB; if(*regex == '*'){ options |= REG_ICASE; regex++; } regex_t rbuf; if(regcomp(&rbuf, regex, options) == 0){ rv = tclistnew(); int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0; if(max < 1) max = INT_MAX; tcmdbiterinit(adb->mdb); char *kbuf; int ksiz; while(max > 0 && (kbuf = tcmdbiternext(adb->mdb, &ksiz))){ if(regexec(&rbuf, kbuf, 0, NULL, 0) == 0){ int vsiz; char *vbuf = tcmdbget(adb->mdb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, kbuf, ksiz); TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); max--; } } TCFREE(kbuf); } regfree(&rbuf); } else { rv = NULL; } } else { rv = NULL; } } else { rv = NULL; } break; case ADBONDB: if(!strcmp(name, "put") || !strcmp(name, "putkeep") || !strcmp(name, "putcat")){ if(argc > 1){ rv = tclistnew2(1); const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); const char *vbuf; int vsiz; TCLISTVAL(vbuf, args, 1, vsiz); bool err = false; if(!strcmp(name, "put")){ tcndbput(adb->ndb, kbuf, ksiz, vbuf, vsiz); } else if(!strcmp(name, "putkeep")){ if(!tcndbputkeep(adb->ndb, kbuf, ksiz, vbuf, vsiz)) err = true; } else if(!strcmp(name, "putcat")){ tcndbputcat(adb->ndb, kbuf, ksiz, vbuf, vsiz); } if(err){ tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "out")){ if(argc > 0){ rv = tclistnew2(1); const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); if(!tcndbout(adb->ndb, kbuf, ksiz)){ tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "get")){ if(argc > 0){ rv = tclistnew2(1); const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); int vsiz; char *vbuf = tcndbget(adb->ndb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); } else { tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "putlist")){ rv = tclistnew2(1); argc--; for(int i = 0; i < argc; i += 2){ const char *kbuf, *vbuf; int ksiz, vsiz; TCLISTVAL(kbuf, args, i, ksiz); TCLISTVAL(vbuf, args, i + 1, vsiz); tcndbput(adb->ndb, kbuf, ksiz, vbuf, vsiz); } } else if(!strcmp(name, "outlist")){ rv = tclistnew2(1); for(int i = 0; i < argc; i++){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, i, ksiz); tcndbout(adb->ndb, kbuf, ksiz); } } else if(!strcmp(name, "getlist")){ rv = tclistnew2(argc * 2); for(int i = 0; i < argc; i++){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, i, ksiz); int vsiz; char *vbuf = tcndbget(adb->ndb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, kbuf, ksiz); TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); } } } else if(!strcmp(name, "getpart")){ if(argc > 0){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); int off = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0; if(off < 0) off = 0; if(off > INT_MAX / 2 - 1) off = INT_MAX - 1; int len = argc > 2 ? tcatoi(TCLISTVALPTR(args, 2)) : -1; if(len < 0 || len > INT_MAX / 2) len = INT_MAX / 2; int vsiz; char *vbuf = tcndbget(adb->ndb, kbuf, ksiz, &vsiz); if(vbuf){ if(off < vsiz){ rv = tclistnew2(1); vsiz -= off; if(vsiz > len) vsiz = len; if(off > 0) memmove(vbuf, vbuf + off, vsiz); tclistpushmalloc(rv, vbuf, vsiz); } else { rv = NULL; TCFREE(vbuf); } } else { rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "iterinit")){ rv = tclistnew2(1); if(argc > 0){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); tcndbiterinit2(adb->ndb, kbuf, ksiz); } else { tcndbiterinit(adb->ndb); } } else if(!strcmp(name, "iternext")){ rv = tclistnew2(1); int ksiz; char *kbuf = tcndbiternext(adb->ndb, &ksiz); if(kbuf){ TCLISTPUSH(rv, kbuf, ksiz); int vsiz; char *vbuf = tcndbget(adb->ndb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); } TCFREE(kbuf); } else { tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "sync")){ rv = tclistnew2(1); if(!tcadbsync(adb)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "optimize")){ rv = tclistnew2(1); const char *params = argc > 0 ? TCLISTVALPTR(args, 0) : NULL; if(!tcadboptimize(adb, params)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "vanish")){ rv = tclistnew2(1); if(!tcadbvanish(adb)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "regex")){ if(argc > 0){ const char *regex = TCLISTVALPTR(args, 0); int options = REG_EXTENDED | REG_NOSUB; if(*regex == '*'){ options |= REG_ICASE; regex++; } regex_t rbuf; if(regcomp(&rbuf, regex, options) == 0){ rv = tclistnew(); int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0; if(max < 1) max = INT_MAX; tcndbiterinit(adb->ndb); char *kbuf; int ksiz; while(max > 0 && (kbuf = tcndbiternext(adb->ndb, &ksiz))){ if(regexec(&rbuf, kbuf, 0, NULL, 0) == 0){ int vsiz; char *vbuf = tcndbget(adb->ndb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, kbuf, ksiz); TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); max--; } } TCFREE(kbuf); } regfree(&rbuf); } else { rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "range")){ rv = tclistnew(); int bksiz = 0; const char *bkbuf = NULL; if(argc > 0) TCLISTVAL(bkbuf, args, 0, bksiz); int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0; if(max < 1) max = INT_MAX; int eksiz = 0; const char *ekbuf = NULL; if(argc > 2) TCLISTVAL(ekbuf, args, 2, eksiz); if(bkbuf){ tcndbiterinit2(adb->ndb, bkbuf, bksiz); } else { tcndbiterinit(adb->ndb); } char *kbuf; int ksiz; while(max > 0 && (kbuf = tcndbiternext(adb->ndb, &ksiz)) != NULL){ if(ekbuf && tccmplexical(kbuf, ksiz, ekbuf, eksiz, NULL) >= 0){ TCFREE(kbuf); break; } int vsiz; char *vbuf = tcndbget(adb->ndb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, kbuf, ksiz); TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); max--; } TCFREE(kbuf); } } else { rv = NULL; } break; case ADBOHDB: if(!strcmp(name, "put") || !strcmp(name, "putkeep") || !strcmp(name, "putcat")){ if(argc > 1){ rv = tclistnew2(1); const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); const char *vbuf; int vsiz; TCLISTVAL(vbuf, args, 1, vsiz); bool err = false; if(!strcmp(name, "put")){ if(!tchdbput(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true; } else if(!strcmp(name, "putkeep")){ if(!tchdbputkeep(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true; } else if(!strcmp(name, "putcat")){ if(!tchdbputcat(adb->hdb, kbuf, ksiz, vbuf, vsiz)) err = true; } if(err){ tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "out")){ if(argc > 0){ rv = tclistnew2(1); const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); if(!tchdbout(adb->hdb, kbuf, ksiz)){ tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "get")){ if(argc > 0){ rv = tclistnew2(1); const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); int vsiz; char *vbuf = tchdbget(adb->hdb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); } else { tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "putlist")){ rv = tclistnew2(1); bool err = false; argc--; for(int i = 0; i < argc; i += 2){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, i, ksiz); int vsiz; const char *vbuf = tclistval(args, i + 1, &vsiz); if(!tchdbput(adb->hdb, kbuf, ksiz, vbuf, vsiz)){ err = true; break; } } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "outlist")){ rv = tclistnew2(1); bool err = false; for(int i = 0; i < argc; i++){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, i, ksiz); if(!tchdbout(adb->hdb, kbuf, ksiz) && tchdbecode(adb->hdb) != TCENOREC){ err = true; break; } } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "getlist")){ rv = tclistnew2(argc * 2); bool err = false; for(int i = 0; i < argc; i++){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, i, ksiz); int vsiz; char *vbuf = tchdbget(adb->hdb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, kbuf, ksiz); TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); } else if(tchdbecode(adb->hdb) != TCENOREC){ err = true; } } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "getpart")){ if(argc > 0){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); int off = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0; if(off < 0) off = 0; if(off > INT_MAX / 2 - 1) off = INT_MAX - 1; int len = argc > 2 ? tcatoi(TCLISTVALPTR(args, 2)) : -1; if(len < 0 || len > INT_MAX / 2) len = INT_MAX / 2; int vsiz; char *vbuf = tchdbget(adb->hdb, kbuf, ksiz, &vsiz); if(vbuf){ if(off < vsiz){ rv = tclistnew2(1); vsiz -= off; if(vsiz > len) vsiz = len; if(off > 0) memmove(vbuf, vbuf + off, vsiz); tclistpushmalloc(rv, vbuf, vsiz); } else { rv = NULL; TCFREE(vbuf); } } else { rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "iterinit")){ rv = tclistnew2(1); bool err = false; if(argc > 0){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); if(!tchdbiterinit2(adb->hdb, kbuf, ksiz)) err = true; } else { if(!tchdbiterinit(adb->hdb)) err = true; } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "iternext")){ rv = tclistnew2(1); int ksiz; char *kbuf = tchdbiternext(adb->hdb, &ksiz); if(kbuf){ TCLISTPUSH(rv, kbuf, ksiz); int vsiz; char *vbuf = tchdbget(adb->hdb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); } TCFREE(kbuf); } else { tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "sync")){ rv = tclistnew2(1); if(!tcadbsync(adb)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "optimize")){ rv = tclistnew2(1); const char *params = argc > 0 ? TCLISTVALPTR(args, 0) : NULL; if(!tcadboptimize(adb, params)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "vanish")){ rv = tclistnew2(1); if(!tcadbvanish(adb)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "error")){ rv = tclistnew2(1); int ecode = tchdbecode(adb->hdb); tclistprintf(rv, "%d: %s", ecode, tchdberrmsg(ecode)); uint8_t flags = tchdbflags(adb->hdb); if(flags & HDBFFATAL) tclistprintf(rv, "fatal"); } else if(!strcmp(name, "defrag")){ rv = tclistnew2(1); int64_t step = argc > 0 ? tcatoi(TCLISTVALPTR(args, 0)) : -1; if(!tchdbdefrag(adb->hdb, step)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "cacheclear")){ rv = tclistnew2(1); if(!tchdbcacheclear(adb->hdb)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "regex")){ if(argc > 0){ const char *regex = TCLISTVALPTR(args, 0); int options = REG_EXTENDED | REG_NOSUB; if(*regex == '*'){ options |= REG_ICASE; regex++; } regex_t rbuf; if(regcomp(&rbuf, regex, options) == 0){ rv = tclistnew(); int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0; if(max < 1) max = INT_MAX; tchdbiterinit(adb->hdb); TCXSTR *kxstr = tcxstrnew(); TCXSTR *vxstr = tcxstrnew(); while(max > 0 && tchdbiternext3(adb->hdb, kxstr, vxstr)){ const char *kbuf = TCXSTRPTR(kxstr); if(regexec(&rbuf, kbuf, 0, NULL, 0) == 0){ TCLISTPUSH(rv, kbuf, TCXSTRSIZE(kxstr)); TCLISTPUSH(rv, TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr)); max--; } } tcxstrdel(vxstr); tcxstrdel(kxstr); regfree(&rbuf); } else { rv = NULL; } } else { rv = NULL; } } else { rv = NULL; } break; case ADBOBDB: if(!strcmp(name, "put") || !strcmp(name, "putkeep") || !strcmp(name, "putcat") || !strcmp(name, "putdup") || !strcmp(name, "putdupback")){ if(argc > 1){ rv = tclistnew2(1); const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); const char *vbuf; int vsiz; TCLISTVAL(vbuf, args, 1, vsiz); bool err = false; if(!strcmp(name, "put")){ if(!tcbdbput(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true; } else if(!strcmp(name, "putkeep")){ if(!tcbdbputkeep(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true; } else if(!strcmp(name, "putcat")){ if(!tcbdbputcat(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true; } else if(!strcmp(name, "putdup")){ if(!tcbdbputdup(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true; } else if(!strcmp(name, "putdupback")){ if(!tcbdbputdupback(adb->bdb, kbuf, ksiz, vbuf, vsiz)) err = true; } if(err){ tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "out")){ if(argc > 0){ rv = tclistnew2(1); const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); if(!tcbdbout(adb->bdb, kbuf, ksiz)){ tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "get")){ if(argc > 0){ rv = tclistnew2(1); const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); TCLIST *vals = tcbdbget4(adb->bdb, kbuf, ksiz); if(vals){ tclistdel(rv); rv = vals; } else { tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "putlist")){ rv = tclistnew2(1); bool err = false; argc--; for(int i = 0; i < argc; i += 2){ const char *kbuf, *vbuf; int ksiz, vsiz; TCLISTVAL(kbuf, args, i, ksiz); TCLISTVAL(vbuf, args, i + 1, vsiz); if(!tcbdbputdup(adb->bdb, kbuf, ksiz, vbuf, vsiz)){ err = true; break; } } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "outlist")){ rv = tclistnew2(1); bool err = false; for(int i = 0; i < argc; i++){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, i, ksiz); if(!tcbdbout3(adb->bdb, kbuf, ksiz) && tcbdbecode(adb->bdb) != TCENOREC){ err = true; break; } } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "getlist")){ rv = tclistnew2(argc * 2); bool err = false; for(int i = 0; i < argc; i++){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, i, ksiz); TCLIST *vals = tcbdbget4(adb->bdb, kbuf, ksiz); if(vals){ int vnum = TCLISTNUM(vals); for(int j = 0; j < vnum; j++){ TCLISTPUSH(rv, kbuf, ksiz); const char *vbuf; int vsiz; TCLISTVAL(vbuf, vals, j, vsiz); TCLISTPUSH(rv, vbuf, vsiz); } tclistdel(vals); } else if(tcbdbecode(adb->bdb) != TCENOREC){ err = true; } } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "getpart")){ if(argc > 0){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); int off = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0; if(off < 0) off = 0; if(off > INT_MAX / 2 - 1) off = INT_MAX - 1; int len = argc > 2 ? tcatoi(TCLISTVALPTR(args, 2)) : -1; if(len < 0 || len > INT_MAX / 2) len = INT_MAX / 2; int vsiz; char *vbuf = tcbdbget(adb->bdb, kbuf, ksiz, &vsiz); if(vbuf){ if(off < vsiz){ rv = tclistnew2(1); vsiz -= off; if(vsiz > len) vsiz = len; if(off > 0) memmove(vbuf, vbuf + off, vsiz); tclistpushmalloc(rv, vbuf, vsiz); } else { rv = NULL; TCFREE(vbuf); } } else { rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "iterinit")){ rv = tclistnew2(1); bool err = false; if(argc > 0){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); if(!tcbdbcurjump(adb->cur, kbuf, ksiz)) err = true; } else { if(!tcbdbcurfirst(adb->cur)) err = true; } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "iternext")){ rv = tclistnew2(1); int ksiz; const char *kbuf = tcbdbcurkey3(adb->cur, &ksiz); if(kbuf){ TCLISTPUSH(rv, kbuf, ksiz); int vsiz; const char *vbuf = tcbdbcurval3(adb->cur, &vsiz); if(vbuf) TCLISTPUSH(rv, vbuf, vsiz); tcbdbcurnext(adb->cur); } else { tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "sync")){ rv = tclistnew2(1); if(!tcadbsync(adb)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "optimize")){ rv = tclistnew2(1); const char *params = argc > 0 ? TCLISTVALPTR(args, 0) : NULL; if(!tcadboptimize(adb, params)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "vanish")){ rv = tclistnew2(1); if(!tcadbvanish(adb)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "error")){ rv = tclistnew2(1); int ecode = tcbdbecode(adb->bdb); tclistprintf(rv, "%d: %s", ecode, tcbdberrmsg(ecode)); uint8_t flags = tcbdbflags(adb->bdb); if(flags & BDBFFATAL) tclistprintf(rv, "fatal"); } else if(!strcmp(name, "defrag")){ rv = tclistnew2(1); int64_t step = argc > 0 ? tcatoi(TCLISTVALPTR(args, 0)) : -1; if(!tcbdbdefrag(adb->bdb, step)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "cacheclear")){ rv = tclistnew2(1); if(!tcbdbcacheclear(adb->bdb)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "regex")){ if(argc > 0){ const char *regex = TCLISTVALPTR(args, 0); int options = REG_EXTENDED | REG_NOSUB; if(*regex == '*'){ options |= REG_ICASE; regex++; } regex_t rbuf; if(regcomp(&rbuf, regex, options) == 0){ rv = tclistnew(); int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0; if(max < 1) max = INT_MAX; BDBCUR *cur = tcbdbcurnew(adb->bdb); tcbdbcurfirst(cur); TCXSTR *kxstr = tcxstrnew(); TCXSTR *vxstr = tcxstrnew(); while(max > 0 && tcbdbcurrec(cur, kxstr, vxstr)){ const char *kbuf = TCXSTRPTR(kxstr); if(regexec(&rbuf, kbuf, 0, NULL, 0) == 0){ TCLISTPUSH(rv, kbuf, TCXSTRSIZE(kxstr)); TCLISTPUSH(rv, TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr)); max--; } tcbdbcurnext(cur); } tcxstrdel(vxstr); tcxstrdel(kxstr); tcbdbcurdel(cur); regfree(&rbuf); } else { rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "range")){ rv = tclistnew(); int bksiz = 0; const char *bkbuf = NULL; if(argc > 0) TCLISTVAL(bkbuf, args, 0, bksiz); int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0; if(max < 1) max = INT_MAX; int eksiz = 0; const char *ekbuf = NULL; if(argc > 2) TCLISTVAL(ekbuf, args, 2, eksiz); TCCMP cmp = tcbdbcmpfunc(adb->bdb); void *cmpop = tcbdbcmpop(adb->bdb); BDBCUR *cur = tcbdbcurnew(adb->bdb); if(bkbuf){ tcbdbcurjump(cur, bkbuf, bksiz); } else { tcbdbcurfirst(cur); } TCXSTR *kxstr = tcxstrnew(); TCXSTR *vxstr = tcxstrnew(); while(max > 0 && tcbdbcurrec(cur, kxstr, vxstr)){ const char *kbuf = TCXSTRPTR(kxstr); int ksiz = TCXSTRSIZE(kxstr); if(ekbuf && cmp(kbuf, ksiz, ekbuf, eksiz, cmpop) >= 0) break; TCLISTPUSH(rv, kbuf, ksiz); TCLISTPUSH(rv, TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr)); max--; tcbdbcurnext(cur); } tcxstrdel(vxstr); tcxstrdel(kxstr); tcbdbcurdel(cur); } else { rv = NULL; } break; case ADBOFDB: if(!strcmp(name, "put") || !strcmp(name, "putkeep") || !strcmp(name, "putcat")){ if(argc > 1){ rv = tclistnew2(1); const char *kbuf, *vbuf; int ksiz, vsiz; TCLISTVAL(kbuf, args, 0, ksiz); TCLISTVAL(vbuf, args, 1, vsiz); bool err = false; if(!strcmp(name, "put")){ if(!tcfdbput2(adb->fdb, kbuf, ksiz, vbuf, vsiz)) err = true; } else if(!strcmp(name, "putkeep")){ if(!tcfdbputkeep2(adb->fdb, kbuf, ksiz, vbuf, vsiz)) err = true; } else if(!strcmp(name, "putcat")){ if(!tcfdbputcat2(adb->fdb, kbuf, ksiz, vbuf, vsiz)) err = true; } if(err){ tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "out")){ if(argc > 0){ rv = tclistnew2(1); const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); if(!tcfdbout2(adb->fdb, kbuf, ksiz)){ tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "get")){ if(argc > 0){ rv = tclistnew2(1); const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); int vsiz; char *vbuf = tcfdbget2(adb->fdb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); } else { tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "putlist")){ rv = tclistnew2(1); bool err = false; argc--; for(int i = 0; i < argc; i += 2){ const char *kbuf, *vbuf; int ksiz, vsiz; TCLISTVAL(kbuf, args, i, ksiz); TCLISTVAL(vbuf, args, i + 1, vsiz); if(!tcfdbput2(adb->fdb, kbuf, ksiz, vbuf, vsiz)){ err = true; break; } } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "outlist")){ rv = tclistnew2(1); bool err = false; for(int i = 0; i < argc; i++){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, i, ksiz); if(!tcfdbout2(adb->fdb, kbuf, ksiz) && tcfdbecode(adb->fdb) != TCENOREC){ err = true; break; } } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "getlist")){ rv = tclistnew2(argc * 2); bool err = false; for(int i = 0; i < argc; i++){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, i, ksiz); int vsiz; char *vbuf = tcfdbget2(adb->fdb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, kbuf, ksiz); TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); } else if(tcfdbecode(adb->fdb) != TCENOREC){ err = true; } } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "getpart")){ if(argc > 0){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); int off = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0; if(off < 0) off = 0; if(off > INT_MAX / 2 - 1) off = INT_MAX - 1; int len = argc > 2 ? tcatoi(TCLISTVALPTR(args, 2)) : -1; if(len < 0 || len > INT_MAX / 2) len = INT_MAX / 2; int vsiz; char *vbuf = tcfdbget2(adb->fdb, kbuf, ksiz, &vsiz); if(vbuf){ if(off < vsiz){ rv = tclistnew2(1); vsiz -= off; if(vsiz > len) vsiz = len; if(off > 0) memmove(vbuf, vbuf + off, vsiz); tclistpushmalloc(rv, vbuf, vsiz); } else { rv = NULL; TCFREE(vbuf); } } else { rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "iterinit")){ rv = tclistnew2(1); bool err = false; if(argc > 0){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); if(!tcfdbiterinit3(adb->fdb, kbuf, ksiz)) err = true; } else { if(!tcfdbiterinit(adb->fdb)) err = true; } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "iternext")){ rv = tclistnew2(1); int ksiz; char *kbuf = tcfdbiternext2(adb->fdb, &ksiz); if(kbuf){ TCLISTPUSH(rv, kbuf, ksiz); int vsiz; char *vbuf = tcfdbget2(adb->fdb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); } TCFREE(kbuf); } else { tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "sync")){ rv = tclistnew2(1); if(!tcadbsync(adb)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "optimize")){ rv = tclistnew2(1); const char *params = argc > 0 ? TCLISTVALPTR(args, 0) : NULL; if(!tcadboptimize(adb, params)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "vanish")){ rv = tclistnew2(1); if(!tcadbvanish(adb)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "error")){ rv = tclistnew2(1); int ecode = tcfdbecode(adb->fdb); tclistprintf(rv, "%d: %s", ecode, tcfdberrmsg(ecode)); uint8_t flags = tcfdbflags(adb->fdb); if(flags & FDBFFATAL) tclistprintf(rv, "fatal"); } else if(!strcmp(name, "regex")){ if(argc > 0){ const char *regex = TCLISTVALPTR(args, 0); int options = REG_EXTENDED | REG_NOSUB; if(*regex == '*'){ options |= REG_ICASE; regex++; } regex_t rbuf; if(regcomp(&rbuf, regex, options) == 0){ rv = tclistnew(); int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0; if(max < 1) max = INT_MAX; tcfdbiterinit(adb->fdb); char *kbuf; int ksiz; while(max > 0 && (kbuf = tcfdbiternext2(adb->fdb, &ksiz))){ if(regexec(&rbuf, kbuf, 0, NULL, 0) == 0){ int vsiz; char *vbuf = tcfdbget2(adb->fdb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, kbuf, ksiz); TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); max--; } } TCFREE(kbuf); } regfree(&rbuf); } else { rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "range")){ rv = tclistnew(); int bksiz = 0; const char *bkbuf = NULL; if(argc > 0) TCLISTVAL(bkbuf, args, 0, bksiz); int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0; if(max < 1) max = INT_MAX; int eksiz = 0; const char *ekbuf = NULL; if(argc > 2) TCLISTVAL(ekbuf, args, 2, eksiz); if(bkbuf){ tcfdbiterinit3(adb->fdb, bkbuf, bksiz); } else { tcfdbiterinit(adb->fdb); } int64_t eid = ekbuf ? tcfdbkeytoid(ekbuf, eksiz) : -1; char *kbuf; int ksiz; while(max > 0 && (kbuf = tcfdbiternext2(adb->fdb, &ksiz)) != NULL){ if(eid > 0 && tcatoi(kbuf) >= eid){ TCFREE(kbuf); break; } int vsiz; char *vbuf = tcfdbget2(adb->fdb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, kbuf, ksiz); TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); max--; } TCFREE(kbuf); } } else { rv = NULL; } break; case ADBOTDB: if(!strcmp(name, "put") || !strcmp(name, "putkeep") || !strcmp(name, "putcat")){ if(argc > 0){ rv = tclistnew2(1); char *pkbuf; int pksiz; TCLISTVAL(pkbuf, args, 0, pksiz); argc--; TCMAP *cols = tcmapnew2(argc); for(int i = 1; i < argc; i += 2){ const char *kbuf, *vbuf; int ksiz, vsiz; TCLISTVAL(kbuf, args, i, ksiz); TCLISTVAL(vbuf, args, i + 1, vsiz); tcmapput(cols, kbuf, ksiz, vbuf, vsiz); } bool err = false; if(!strcmp(name, "put")){ if(!tctdbput(adb->tdb, pkbuf, pksiz, cols)) err = true; } else if(!strcmp(name, "putkeep")){ if(!tctdbputkeep(adb->tdb, pkbuf, pksiz, cols)) err = true; } else if(!strcmp(name, "putcat")){ if(!tctdbputcat(adb->tdb, pkbuf, pksiz, cols)) err = true; } tcmapdel(cols); if(err){ tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "out")){ if(argc > 0){ rv = tclistnew2(1); char *pkbuf; int pksiz; TCLISTVAL(pkbuf, args, 0, pksiz); if(!tctdbout(adb->tdb, pkbuf, pksiz)){ tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "get")){ if(argc > 0){ rv = tclistnew2(1); char *pkbuf; int pksiz; TCLISTVAL(pkbuf, args, 0, pksiz); TCMAP *cols = tctdbget(adb->tdb, pkbuf, pksiz); if(cols){ tcmapiterinit(cols); const char *kbuf; int ksiz; while((kbuf = tcmapiternext(cols, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); TCLISTPUSH(rv, kbuf, ksiz); TCLISTPUSH(rv, vbuf, vsiz); } tcmapdel(cols); } else { tclistdel(rv); rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "putlist")){ rv = tclistnew2(1); bool err = false; argc--; for(int i = 0; i < argc; i += 2){ const char *kbuf, *vbuf; int ksiz, vsiz; TCLISTVAL(kbuf, args, i, ksiz); TCLISTVAL(vbuf, args, i + 1, vsiz); if(!tctdbput2(adb->tdb, kbuf, ksiz, vbuf, vsiz)){ err = true; break; } } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "outlist")){ rv = tclistnew2(1); bool err = false; for(int i = 0; i < argc; i++){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, i, ksiz); if(!tctdbout(adb->tdb, kbuf, ksiz) && tctdbecode(adb->tdb) != TCENOREC){ err = true; break; } } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "getlist")){ rv = tclistnew2(argc * 2); bool err = false; for(int i = 0; i < argc; i++){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, i, ksiz); int vsiz; char *vbuf = tctdbget2(adb->tdb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, kbuf, ksiz); TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); } else if(tctdbecode(adb->tdb) != TCENOREC){ err = true; } } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "getpart")){ if(argc > 0){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, 0, ksiz); int off = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0; if(off < 0) off = 0; if(off > INT_MAX / 2 - 1) off = INT_MAX - 1; int len = argc > 2 ? tcatoi(TCLISTVALPTR(args, 2)) : -1; if(len < 0 || len > INT_MAX / 2) len = INT_MAX / 2; int vsiz; char *vbuf = tctdbget2(adb->tdb, kbuf, ksiz, &vsiz); if(vbuf){ if(off < vsiz){ rv = tclistnew2(1); vsiz -= off; if(vsiz > len) vsiz = len; if(off > 0) memmove(vbuf, vbuf + off, vsiz); tclistpushmalloc(rv, vbuf, vsiz); } else { rv = NULL; TCFREE(vbuf); } } else { rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "iterinit")){ rv = tclistnew2(1); bool err = false; if(argc > 0){ const char *pkbuf; int pksiz; TCLISTVAL(pkbuf, args, 0, pksiz); if(!tctdbiterinit2(adb->tdb, pkbuf, pksiz)) err = true; } else { if(!tctdbiterinit(adb->tdb)) err = true; } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "iternext")){ rv = tclistnew2(1); int pksiz; char *pkbuf = tctdbiternext(adb->tdb, &pksiz); if(pkbuf){ TCLISTPUSH(rv, pkbuf, pksiz); int csiz; char *cbuf = tctdbget2(adb->tdb, pkbuf, pksiz, &csiz); if(cbuf){ TCLISTPUSH(rv, cbuf, csiz); TCFREE(cbuf); } TCFREE(pkbuf); } else { tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "sync")){ rv = tclistnew2(1); if(!tcadbsync(adb)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "optimize")){ rv = tclistnew2(1); const char *params = argc > 0 ? TCLISTVALPTR(args, 0) : NULL; if(!tcadboptimize(adb, params)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "vanish")){ rv = tclistnew2(1); if(!tcadbvanish(adb)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "error")){ rv = tclistnew2(1); int ecode = tctdbecode(adb->tdb); tclistprintf(rv, "%d: %s", ecode, tctdberrmsg(ecode)); uint8_t flags = tctdbflags(adb->tdb); if(flags & TDBFFATAL) tclistprintf(rv, "fatal"); } else if(!strcmp(name, "defrag")){ rv = tclistnew2(1); int64_t step = argc > 0 ? tcatoi(TCLISTVALPTR(args, 0)) : -1; if(!tctdbdefrag(adb->tdb, step)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "cacheclear")){ rv = tclistnew2(1); if(!tctdbcacheclear(adb->tdb)){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "regex")){ if(argc > 0){ const char *regex = TCLISTVALPTR(args, 0); int options = REG_EXTENDED | REG_NOSUB; if(*regex == '*'){ options |= REG_ICASE; regex++; } regex_t rbuf; if(regcomp(&rbuf, regex, options) == 0){ rv = tclistnew(); int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0; if(max < 1) max = INT_MAX; tctdbiterinit(adb->tdb); char *kbuf; int ksiz; while(max > 0 && (kbuf = tctdbiternext(adb->tdb, &ksiz))){ if(regexec(&rbuf, kbuf, 0, NULL, 0) == 0){ int vsiz; char *vbuf = tctdbget2(adb->tdb, kbuf, ksiz, &vsiz); if(vbuf){ TCLISTPUSH(rv, kbuf, ksiz); TCLISTPUSH(rv, vbuf, vsiz); TCFREE(vbuf); max--; } } TCFREE(kbuf); } regfree(&rbuf); } else { rv = NULL; } } else { rv = NULL; } } else if(!strcmp(name, "setindex")){ rv = tclistnew2(1); bool err = false; argc--; for(int i = 0; i < argc; i += 2){ const char *kbuf, *vbuf; kbuf = TCLISTVALPTR(args, i); vbuf = TCLISTVALPTR(args, i + 1); int type = tctdbstrtoindextype(vbuf); if(type >= 0){ if(!tctdbsetindex(adb->tdb, kbuf, type)) err = true; } else { err = true; } } if(err){ tclistdel(rv); rv = NULL; } } else if(!strcmp(name, "search") || !strcmp(name, "metasearch")){ bool toout = false; bool tocnt = false; bool tohint = false; TDBQRY *qry = tctdbqrynew(adb->tdb); TDBQRY **qrys = NULL; int qnum = 0; int mstype = TDBMSUNION; TCLIST *cnames = NULL; for(int i = 0; i < argc; i++){ const char *arg; int asiz; TCLISTVAL(arg, args, i, asiz); TCLIST *tokens = tcstrsplit2(arg, asiz); int tnum = TCLISTNUM(tokens); if(tnum > 0){ const char *cmd = TCLISTVALPTR(tokens, 0); if((!strcmp(cmd, "addcond") || !strcmp(cmd, "cond")) && tnum > 3){ const char *name = TCLISTVALPTR(tokens, 1); const char *opstr = TCLISTVALPTR(tokens, 2); const char *expr = TCLISTVALPTR(tokens, 3); int op = tctdbqrystrtocondop(opstr); if(op >= 0) tctdbqryaddcond(qry, name, op, expr); } else if((!strcmp(cmd, "setorder") || !strcmp(cmd, "order")) && tnum > 2){ const char *name = TCLISTVALPTR(tokens, 1); const char *typestr = TCLISTVALPTR(tokens, 2); int type = tctdbqrystrtoordertype(typestr); if(type >= 0) tctdbqrysetorder(qry, name, type); } else if((!strcmp(cmd, "setlimit") || !strcmp(cmd, "limit") || !strcmp(cmd, "setmax") || !strcmp(cmd, "max") ) && tnum > 1){ const char *maxstr = TCLISTVALPTR(tokens, 1); int max = tcatoi(maxstr); int skip = 0; if(tnum > 2){ maxstr = TCLISTVALPTR(tokens, 2); skip = tcatoi(maxstr); } tctdbqrysetlimit(qry, max, skip); } else if(!strcmp(cmd, "get") || !strcmp(cmd, "columns")){ if(!cnames) cnames = tclistnew(); for(int j = 1; j < tnum; j++){ const char *token; int tsiz; TCLISTVAL(token, tokens, j, tsiz); TCLISTPUSH(cnames, token, tsiz); } } else if(!strcmp(cmd, "next")){ if(qrys){ TCREALLOC(qrys, qrys, sizeof(*qrys) * (qnum + 1)); } else { TCMALLOC(qrys, sizeof(*qrys) * 2); qrys[0] = qry; qnum = 1; } qry = tctdbqrynew(adb->tdb); qrys[qnum++] = qry; } else if(!strcmp(cmd, "mstype") && tnum > 1){ const char *typestr = TCLISTVALPTR(tokens, 1); mstype = tctdbstrtometasearcytype(typestr); if(mstype < 0) mstype = TDBMSUNION; } else if(!strcmp(cmd, "out") || !strcmp(cmd, "remove")){ toout = true; } else if(!strcmp(cmd, "count")){ tocnt = true; } else if(!strcmp(cmd, "hint")){ tohint = true; } } tclistdel(tokens); } if(toout){ if(cnames){ rv = tclistnew2(1); void *opq[2]; opq[0] = rv; opq[1] = cnames; if(!tctdbqryproc2(qry, tcadbtdbqrygetout, opq)){ tclistdel(rv); rv = NULL; } } else { if(tctdbqrysearchout2(qry)){ rv = tclistnew2(1); } else { rv = NULL; } } } else { if(qrys){ rv = tctdbmetasearch(qrys, qnum, mstype); } else { rv = tctdbqrysearch(qry); } if(cnames){ int cnnum = TCLISTNUM(cnames); int rnum = TCLISTNUM(rv); TCLIST *nrv = tclistnew2(rnum); for(int i = 0; i < rnum; i++){ const char *pkbuf; int pksiz; TCLISTVAL(pkbuf, rv, i, pksiz); TCMAP *cols = tctdbget(adb->tdb, pkbuf, pksiz); if(cols){ tcmapput(cols, "", 0, pkbuf, pksiz); tcmapmove(cols, "", 0, true); if(cnnum > 0){ TCMAP *ncols = tcmapnew2(cnnum + 1); for(int j = 0; j < cnnum; j++){ const char *cname; int cnsiz; TCLISTVAL(cname, cnames, j, cnsiz); int cvsiz; const char *cvalue = tcmapget(cols, cname, cnsiz, &cvsiz); if(cvalue) tcmapput(ncols, cname, cnsiz, cvalue, cvsiz); } tcmapdel(cols); cols = ncols; } int csiz; char *cbuf = tcstrjoin4(cols, &csiz); tclistpushmalloc(nrv, cbuf, csiz); tcmapdel(cols); } } tclistdel(rv); rv = nrv; } } if(tocnt && rv){ tclistclear(rv); char numbuf[TCNUMBUFSIZ]; int len = sprintf(numbuf, "%d", tctdbqrycount(qry)); TCLISTPUSH(rv, numbuf, len); } if(tohint && rv){ TCXSTR *hbuf = tcxstrnew(); TCXSTRCAT(hbuf, "", 1); TCXSTRCAT(hbuf, "", 1); TCXSTRCAT(hbuf, "[[HINT]]\n", 9); const char *hint = tctdbqryhint(qrys ? qrys[0] : qry); TCXSTRCAT(hbuf, hint, strlen(hint)); TCLISTPUSH(rv, TCXSTRPTR(hbuf), TCXSTRSIZE(hbuf)); tcxstrdel(hbuf); } if(cnames) tclistdel(cnames); if(qrys){ for(int i = 0; i < qnum; i++){ tctdbqrydel(qrys[i]); } TCFREE(qrys); } else { tctdbqrydel(qry); } } else if(!strcmp(name, "genuid")){ rv = tclistnew2(1); char numbuf[TCNUMBUFSIZ]; int nsiz = sprintf(numbuf, "%lld", (long long)tctdbgenuid(adb->tdb)); TCLISTPUSH(rv, numbuf, nsiz); } else { rv = NULL; } break; case ADBOSKEL: skel = adb->skel; if(skel->misc){ rv = skel->misc(skel->opq, name, args); } else { rv = NULL; } break; default: rv = NULL; break; } return rv; } /************************************************************************************************* * features for experts *************************************************************************************************/ /* Set an extra database sleleton to an abstract database object. */ bool tcadbsetskel(TCADB *adb, ADBSKEL *skel){ assert(skel); if(adb->omode != ADBOVOID) return false; if(adb->skel) TCFREE(adb->skel); adb->skel = tcmemdup(skel, sizeof(*skel)); return true; } /* Set the multiple database skeleton to an abstract database object. */ bool tcadbsetskelmulti(TCADB *adb, int num){ assert(adb && num >= 0); if(adb->omode != ADBOVOID) return false; if(num < 1) return false; if(num > CHAR_MAX) num = CHAR_MAX; ADBMUL *mul = tcadbmulnew(num); ADBSKEL skel; memset(&skel, 0, sizeof(skel)); skel.opq = mul; skel.del = (void (*)(void *))tcadbmuldel; skel.open = (bool (*)(void *, const char *))tcadbmulopen; skel.close = (bool (*)(void *))tcadbmulclose; skel.put = (bool (*)(void *, const void *, int, const void *, int))tcadbmulput; skel.putkeep = (bool (*)(void *, const void *, int, const void *, int))tcadbmulputkeep; skel.putcat = (bool (*)(void *, const void *, int, const void *, int))tcadbmulputcat; skel.out = (bool (*)(void *, const void *, int))tcadbmulout; skel.get = (void *(*)(void *, const void *, int, int *))tcadbmulget; skel.vsiz = (int (*)(void *, const void *, int))tcadbmulvsiz; skel.iterinit = (bool (*)(void *))tcadbmuliterinit; skel.iternext = (void *(*)(void *, int *))tcadbmuliternext; skel.fwmkeys = (TCLIST *(*)(void *, const void *, int, int))tcadbmulfwmkeys; skel.addint = (int (*)(void *, const void *, int, int))tcadbmuladdint; skel.adddouble = (double (*)(void *, const void *, int, double))tcadbmuladddouble; skel.sync = (bool (*)(void *))tcadbmulsync; skel.optimize = (bool (*)(void *, const char *))tcadbmuloptimize; skel.vanish = (bool (*)(void *))tcadbmulvanish; skel.copy = (bool (*)(void *, const char *))tcadbmulcopy; skel.tranbegin = (bool (*)(void *))tcadbmultranbegin; skel.trancommit = (bool (*)(void *))tcadbmultrancommit; skel.tranabort = (bool (*)(void *))tcadbmultranabort; skel.path = (const char *(*)(void *))tcadbmulpath; skel.rnum = (uint64_t (*)(void *))tcadbmulrnum; skel.size = (uint64_t (*)(void *))tcadbmulsize; skel.misc = (TCLIST *(*)(void *, const char *, const TCLIST *))tcadbmulmisc; skel.putproc = (bool (*)(void *, const void *, int, const void *, int, TCPDPROC, void *))tcadbmulputproc; skel.foreach = (bool (*)(void *, TCITER, void *))tcadbmulforeach; if(!tcadbsetskel(adb, &skel)){ tcadbmuldel(mul); return false; } return true; } /* Get the open mode of an abstract database object. */ int tcadbomode(TCADB *adb){ assert(adb); return adb->omode; } /* Get the concrete database object of an abstract database object. */ void *tcadbreveal(TCADB *adb){ assert(adb); void *rv; switch(adb->omode){ case ADBOMDB: rv = adb->mdb; break; case ADBONDB: rv = adb->ndb; break; case ADBOHDB: rv = adb->hdb; break; case ADBOBDB: rv = adb->bdb; break; case ADBOFDB: rv = adb->fdb; break; case ADBOTDB: rv = adb->tdb; break; case ADBOSKEL: rv = adb->skel; break; default: rv = NULL; break; } return rv; } /* Store a record into an abstract database object with a duplication handler. */ bool tcadbputproc(TCADB *adb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op){ assert(adb && kbuf && ksiz >= 0 && proc); bool err = false; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: if(tcmdbputproc(adb->mdb, kbuf, ksiz, vbuf, vsiz, proc, op)){ if(adb->capnum > 0 || adb->capsiz > 0){ adb->capcnt++; if((adb->capcnt & 0xff) == 0){ if(adb->capnum > 0 && tcmdbrnum(adb->mdb) > adb->capnum + 0x100) tcmdbcutfront(adb->mdb, 0x100); if(adb->capsiz > 0 && tcmdbmsiz(adb->mdb) > adb->capsiz) tcmdbcutfront(adb->mdb, 0x200); } } } else { err = true; } break; case ADBONDB: if(tcndbputproc(adb->ndb, kbuf, ksiz, vbuf, vsiz, proc, op)){ if(adb->capnum > 0 || adb->capsiz > 0){ adb->capcnt++; if((adb->capcnt & 0xff) == 0){ if(adb->capnum > 0 && tcndbrnum(adb->ndb) > adb->capnum + 0x100) tcndbcutfringe(adb->ndb, 0x100); if(adb->capsiz > 0 && tcndbmsiz(adb->ndb) > adb->capsiz) tcndbcutfringe(adb->ndb, 0x200); } } } else { err = true; } break; case ADBOHDB: if(!tchdbputproc(adb->hdb, kbuf, ksiz, vbuf, vsiz, proc, op)) err = true; break; case ADBOBDB: if(!tcbdbputproc(adb->bdb, kbuf, ksiz, vbuf, vsiz, proc, op)) err = true; break; case ADBOFDB: if(!tcfdbputproc(adb->fdb, tcfdbkeytoid(kbuf, ksiz), vbuf, vsiz, proc, op)) err = true; break; case ADBOTDB: if(!tctdbputproc(adb->tdb, kbuf, ksiz, vbuf, vsiz, proc, op)) err = true; break; case ADBOSKEL: skel = adb->skel; if(skel->putproc){ if(!skel->putproc(skel->opq, kbuf, ksiz, vbuf, vsiz, proc, op)) err = true; } else { err = true; } break; default: err = true; break; } return !err; } /* Process each record atomically of an abstract database object. */ bool tcadbforeach(TCADB *adb, TCITER iter, void *op){ assert(adb && iter); bool err = false; ADBSKEL *skel; switch(adb->omode){ case ADBOMDB: tcmdbforeach(adb->mdb, iter, op); break; case ADBONDB: tcndbforeach(adb->ndb, iter, op); break; case ADBOHDB: if(!tchdbforeach(adb->hdb, iter, op)) err = true; break; case ADBOBDB: if(!tcbdbforeach(adb->bdb, iter, op)) err = true; break; case ADBOFDB: if(!tcfdbforeach(adb->fdb, iter, op)) err = true; break; case ADBOTDB: if(!tctdbforeach(adb->tdb, iter, op)) err = true; break; case ADBOSKEL: skel = adb->skel; if(skel->foreach){ if(!skel->foreach(skel->opq, iter, op)) err = true; } else { err = true; } break; default: err = true; break; } return !err; } /* Map records of an abstract database object into another B+ tree database. */ bool tcadbmapbdb(TCADB *adb, TCLIST *keys, TCBDB *bdb, ADBMAPPROC proc, void *op, int64_t csiz){ assert(adb && bdb && proc); if(csiz < 0) csiz = 256LL << 20; TCLIST *recs = tclistnew2(tclmin(csiz / 64 + 256, INT_MAX / 4)); ADBMAPBDB map; map.adb = adb; map.bdb = bdb; map.recs = recs; map.proc = proc; map.op = op; map.rsiz = 0; map.csiz = csiz; bool err = false; if(keys){ int knum = TCLISTNUM(keys); for(int i = 0; i < knum && !err; i++){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, keys, i, ksiz); int vsiz; char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz); if(vbuf){ if(!tcadbmapbdbiter(kbuf, ksiz, vbuf, vsiz, &map)) err = true; TCFREE(vbuf); if(map.rsiz > map.csiz && !tcadbmapbdbdump(&map)) err = true; } if(map.rsiz > 0 && !tcadbmapbdbdump(&map)) err = true; } } else { if(!tcadbforeach(adb, tcadbmapbdbiter, &map)) err = true; } if(map.rsiz > 0 && !tcadbmapbdbdump(&map)) err = true; tclistdel(recs); return !err; } /* Emit records generated by the mapping function into the result map. */ bool tcadbmapbdbemit(void *map, const char *kbuf, int ksiz, const char *vbuf, int vsiz){ assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); ADBMAPBDB *mymap = map; int rsiz = sizeof(ksiz) + ksiz + vsiz; char stack[TCNUMBUFSIZ*8]; char *rbuf; if(rsiz <= sizeof(stack)){ rbuf = stack; } else { TCMALLOC(rbuf, rsiz); } bool err = false; char *wp = rbuf; memcpy(wp, &ksiz, sizeof(ksiz)); wp += sizeof(ksiz); memcpy(wp, kbuf, ksiz); wp += ksiz; memcpy(wp, vbuf, vsiz); TCLISTPUSH(mymap->recs, rbuf, rsiz); mymap->rsiz += rsiz + sizeof(TCLISTDATUM); if(rbuf != stack) TCFREE(rbuf); if(mymap->rsiz > mymap->csiz && !tcadbmapbdbdump(map)) err = true; return !err; } /************************************************************************************************* * private features *************************************************************************************************/ /* Create a multiple database object. `num' specifies the number of inner databases. The return value is the new multiple database object. */ static ADBMUL *tcadbmulnew(int num){ assert(num > 0); ADBMUL *mul; TCMALLOC(mul, sizeof(*mul)); mul->adbs = NULL; mul->num = num; mul->iter = -1; mul->path = NULL; return mul; } /* Delete a multiple database object. `mul' specifies the multiple database object. */ static void tcadbmuldel(ADBMUL *mul){ assert(mul); if(mul->adbs) tcadbmulclose(mul); TCFREE(mul); } /* Open a multiple database. `mul' specifies the multiple database object. If successful, the return value is true, else, it is false. */ static bool tcadbmulopen(ADBMUL *mul, const char *name){ assert(mul && name); if(mul->adbs) return false; mul->iter = -1; TCLIST *elems = tcstrsplit(name, "#"); char *path = tclistshift2(elems); if(!path){ tclistdel(elems); return false; } const char *ext = strrchr(path, MYEXTCHR); if(!ext) ext = ""; const char *params = strchr(name, '#'); if(!params) params = ""; bool owmode = true; bool ocmode = true; bool otmode = false; int ln = TCLISTNUM(elems); for(int i = 0; i < ln; i++){ const char *elem = TCLISTVALPTR(elems, i); char *pv = strchr(elem, '='); if(!pv) continue; *(pv++) = '\0'; if(!tcstricmp(elem, "mode")){ owmode = strchr(pv, 'w') || strchr(pv, 'W'); ocmode = strchr(pv, 'c') || strchr(pv, 'C'); otmode = strchr(pv, 't') || strchr(pv, 'T'); } } tclistdel(elems); bool err = false; char *gpat = tcsprintf("%s%c%s*%s", path, MYPATHCHR, ADBMULPREFIX, ext); TCLIST *cpaths = tcglobpat(gpat); tclistsort(cpaths); int cnum = TCLISTNUM(cpaths); if(owmode){ if(otmode){ for(int i = 0; i < cnum; i++){ const char *cpath = TCLISTVALPTR(cpaths, i); if(unlink(cpath) != 0) err = true; } tclistclear(cpaths); cnum = 0; } if(ocmode && cnum < 1){ if(mkdir(path, ADBDIRMODE) != 0 && errno != EEXIST){ err = true; } else { for(int i = 0; i < mul->num; i++){ tclistprintf(cpaths, "%s%c%s%03d%s", path, MYPATHCHR, ADBMULPREFIX, i + 1, ext); } cnum = TCLISTNUM(cpaths); } } } if(!err && cnum > 0){ TCADB **adbs; TCMALLOC(adbs, sizeof(*adbs) * cnum); for(int i = 0; i < cnum; i++){ TCADB *adb = tcadbnew(); const char *cpath = TCLISTVALPTR(cpaths, i); char *cname = tcsprintf("%s%s", cpath, params); if(!tcadbopen(adb, cname)) err = true; TCFREE(cname); adbs[i] = adb; } if(err){ for(int i = cnum - 1; i >= 0; i--){ tcadbdel(adbs[i]); } TCFREE(adbs); } else { mul->adbs = adbs; mul->num = cnum; mul->path = path; path = NULL; } } tclistdel(cpaths); TCFREE(gpat); TCFREE(path); return !err; } /* Close a multiple database object. `mul' specifies the multiple database object. If successful, the return value is true, else, it is false. */ static bool tcadbmulclose(ADBMUL *mul){ assert(mul); if(!mul->adbs) return false; TCADB **adbs = mul->adbs; int num = mul->num; bool err = false; for(int i = num - 1; i >= 0; i--){ TCADB *adb = adbs[i]; if(!tcadbclose(adb)) err = true; tcadbdel(adb); } TCFREE(mul->path); TCFREE(adbs); mul->adbs = NULL; mul->path = NULL; return !err; } /* Store a record into a multiple database object. `mul' specifies the multiple 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. */ static bool tcadbmulput(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(mul && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!mul->adbs) return false; int idx = tcadbmulidx(mul, kbuf, ksiz); TCADB *adb = mul->adbs[idx]; return tcadbput(adb, kbuf, ksiz, vbuf, vsiz); } /* Store a new record into a multiple database object. `mul' specifies the multiple 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. */ static bool tcadbmulputkeep(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(mul && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!mul->adbs) return false; int idx = tcadbmulidx(mul, kbuf, ksiz); TCADB *adb = mul->adbs[idx]; return tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz); } /* Concatenate a value at the end of the existing record in a multiple database object. `mul' specifies the multiple 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. */ static bool tcadbmulputcat(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(mul && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!mul->adbs) return false; int idx = tcadbmulidx(mul, kbuf, ksiz); TCADB *adb = mul->adbs[idx]; return tcadbputcat(adb, kbuf, ksiz, vbuf, vsiz); } /* Remove a record of a multiple database object. `mul' specifies the multiple 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. */ static bool tcadbmulout(ADBMUL *mul, const void *kbuf, int ksiz){ assert(mul && kbuf && ksiz >= 0); if(!mul->adbs) return false; int idx = tcadbmulidx(mul, kbuf, ksiz); TCADB *adb = mul->adbs[idx]; return tcadbout(adb, kbuf, ksiz); } /* Retrieve a record in a multiple database object. `mul' specifies the multiple 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. */ static void *tcadbmulget(ADBMUL *mul, const void *kbuf, int ksiz, int *sp){ assert(mul && kbuf && ksiz >= 0 && sp); if(!mul->adbs) return false; int idx = tcadbmulidx(mul, kbuf, ksiz); TCADB *adb = mul->adbs[idx]; return tcadbget(adb, kbuf, ksiz, sp); } /* Get the size of the value of a record in a multiple database object. `mul' specifies the multiple 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. */ static int tcadbmulvsiz(ADBMUL *mul, const void *kbuf, int ksiz){ assert(mul && kbuf && ksiz >= 0); if(!mul->adbs) return false; int idx = tcadbmulidx(mul, kbuf, ksiz); TCADB *adb = mul->adbs[idx]; return tcadbvsiz(adb, kbuf, ksiz); } /* Initialize the iterator of a multiple database object. `mul' specifies the multiple database object. If successful, the return value is true, else, it is false. */ static bool tcadbmuliterinit(ADBMUL *mul){ assert(mul); if(!mul->adbs) return false; mul->iter = -1; TCADB **adbs = mul->adbs; int num = mul->num; bool err = false; for(int i = 0; i < num; i++){ if(!tcadbiterinit(adbs[i])) err = true; } if(err) return false; mul->iter = 0; return true; } /* Get the next key of the iterator of a multiple database object. `mul' specifies the multiple 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'. */ static void *tcadbmuliternext(ADBMUL *mul, int *sp){ assert(mul && sp); if(!mul->adbs || mul->iter < 0) return false; while(mul->iter < mul->num){ TCADB *adb = mul->adbs[mul->iter]; char *rv = tcadbiternext(adb, sp); if(rv) return rv; mul->iter++; } mul->iter = -1; return NULL; } /* Get forward matching keys in a multiple database object. `mul' specifies the multiple 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. */ static TCLIST *tcadbmulfwmkeys(ADBMUL *mul, const void *pbuf, int psiz, int max){ assert(mul && pbuf && psiz >= 0); if(!mul->adbs) return tclistnew2(1); if(max < 0) max = INT_MAX; TCADB **adbs = mul->adbs; int num = mul->num; TCLIST *rv = tclistnew(); for(int i = 0; i < num && TCLISTNUM(rv) < max; i++){ TCLIST *res = tcadbfwmkeys(adbs[i], pbuf, psiz, max); int rnum = TCLISTNUM(res); for(int j = 0; j < rnum && TCLISTNUM(rv) < max; j++){ const char *vbuf; int vsiz; TCLISTVAL(vbuf, res, j, vsiz); TCLISTPUSH(rv, vbuf, vsiz); } tclistdel(res); } return rv; } /* Add an integer to a record in a multiple database object. `mul' specifies the multiple 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'. */ static int tcadbmuladdint(ADBMUL *mul, const void *kbuf, int ksiz, int num){ assert(mul && kbuf && ksiz >= 0); if(!mul->adbs) return INT_MIN; int idx = tcadbmulidx(mul, kbuf, ksiz); TCADB *adb = mul->adbs[idx]; return tcadbaddint(adb, kbuf, ksiz, num); } /* Add a real number to a record in a multiple database object. `mul' specifies the multiple 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. */ static double tcadbmuladddouble(ADBMUL *mul, const void *kbuf, int ksiz, double num){ assert(mul && kbuf && ksiz >= 0); if(!mul->adbs) return nan(""); int idx = tcadbmulidx(mul, kbuf, ksiz); TCADB *adb = mul->adbs[idx]; return tcadbadddouble(adb, kbuf, ksiz, num); } /* Synchronize updated contents of a multiple database object with the file and the device. `mul' specifies the multiple database object. If successful, the return value is true, else, it is false. */ static bool tcadbmulsync(ADBMUL *mul){ assert(mul); if(!mul->adbs) return false; TCADB **adbs = mul->adbs; int num = mul->num; bool err = false; for(int i = 0; i < num; i++){ if(!tcadbsync(adbs[i])) err = true; } return !err; } /* Optimize the storage of a multiple database object. `mul' specifies the multiple database object. `params' specifies the string of the tuning parameters, which works as with the tuning of parameters the function `tcadbmulopen'. If successful, the return value is true, else, it is false. */ static bool tcadbmuloptimize(ADBMUL *mul, const char *params){ assert(mul); if(!mul->adbs) return false; mul->iter = -1; TCADB **adbs = mul->adbs; int num = mul->num; bool err = false; for(int i = 0; i < num; i++){ if(!tcadboptimize(adbs[i], params)) err = true; } return !err; } /* Remove all records of a multiple database object. `mul' specifies the multiple database object. If successful, the return value is true, else, it is false. */ static bool tcadbmulvanish(ADBMUL *mul){ assert(mul); if(!mul->adbs) return false; mul->iter = -1; TCADB **adbs = mul->adbs; int num = mul->num; bool err = false; for(int i = 0; i < num; i++){ if(!tcadbvanish(adbs[i])) err = true; } return !err; } /* Copy the database file of a multiple database object. `mul' specifies the multiple database object. `path' specifies the path of the destination file. If successful, the return value is true, else, it is false. False is returned if the executed command returns non-zero code. */ static bool tcadbmulcopy(ADBMUL *mul, const char *path){ assert(mul && path); TCADB **adbs = mul->adbs; int num = mul->num; bool err = false; if(*path == '@'){ for(int i = 0; i < num; i++){ if(!tcadbcopy(adbs[i], path)) err = true; } } else { if(mkdir(path, ADBDIRMODE) == -1 && errno != EEXIST) return false; for(int i = 0; i < num; i++){ TCADB *adb = adbs[i]; const char *cpath = tcadbpath(adb); if(cpath){ const char *cname = strrchr(cpath, MYPATHCHR); cname = cname ? cname + 1 : cpath; const char *ext = strrchr(cname, MYEXTCHR); if(!ext) ext = ""; char *npath = tcsprintf("%s%c%s%03d%s", path, MYPATHCHR, ADBMULPREFIX, i + 1, ext); if(!tcadbcopy(adb, npath)) err = true; TCFREE(npath); } else { err = true; } } } return !err; } /* Begin the transaction of a multiple database object. `mul' specifies the multiple database object. If successful, the return value is true, else, it is false. */ static bool tcadbmultranbegin(ADBMUL *mul){ assert(mul); if(!mul->adbs) return false; TCADB **adbs = mul->adbs; int num = mul->num; bool err = false; for(int i = 0; i < num; i++){ if(!tcadbtranbegin(adbs[i])){ while(--i >= 0){ tcadbtranabort(adbs[i]); } err = true; break; } } return !err; } /* Commit the transaction of a multiple database object. `mul' specifies the multiple database object. If successful, the return value is true, else, it is false. */ static bool tcadbmultrancommit(ADBMUL *mul){ assert(mul); if(!mul->adbs) return false; TCADB **adbs = mul->adbs; int num = mul->num; bool err = false; for(int i = num - 1; i >= 0; i--){ if(!tcadbtrancommit(adbs[i])) err = true; } return !err; } /* Abort the transaction of a multiple database object. `mul' specifies the multiple database object. If successful, the return value is true, else, it is false. */ static bool tcadbmultranabort(ADBMUL *mul){ assert(mul); if(!mul->adbs) return false; TCADB **adbs = mul->adbs; int num = mul->num; bool err = false; for(int i = num - 1; i >= 0; i--){ if(!tcadbtranabort(adbs[i])) err = true; } return !err; } /* Get the file path of a multiple database object. `mul' specifies the multiple database object. The return value is the path of the database file or `NULL' if the object does not connect to any database. */ static const char *tcadbmulpath(ADBMUL *mul){ assert(mul); if(!mul->adbs) return NULL; return mul->path; } /* Get the number of records of a multiple database object. `mul' specifies the multiple database object. The return value is the number of records or 0 if the object does not connect to any database instance. */ static uint64_t tcadbmulrnum(ADBMUL *mul){ assert(mul); if(!mul->adbs) return 0; TCADB **adbs = mul->adbs; int num = mul->num; uint64_t rnum = 0; for(int i = 0; i < num; i++){ rnum += tcadbrnum(adbs[i]); } return rnum; } /* Get the size of the database of a multiple database object. `mul' specifies the multiple database object. The return value is the size of the database or 0 if the object does not connect to any database instance. */ static uint64_t tcadbmulsize(ADBMUL *mul){ assert(mul); if(!mul->adbs) return 0; TCADB **adbs = mul->adbs; int num = mul->num; uint64_t size = 0; for(int i = 0; i < num; i++){ size += tcadbsize(adbs[i]); } return size; } /* Call a versatile function for miscellaneous operations of a multiple database object. `mul' specifies the multiple database object. `name' specifies the name of the function. `args' specifies a list object containing arguments. If successful, the return value is a list object of the result. */ static TCLIST *tcadbmulmisc(ADBMUL *mul, const char *name, const TCLIST *args){ assert(mul && name); if(!mul->adbs) return NULL; TCADB **adbs = mul->adbs; int num = mul->num; TCLIST *rv = tclistnew(); if(*name == '@'){ name++; int anum = TCLISTNUM(args) - 1; TCLIST *targs = tclistnew2(2); for(int i = 0; i < anum; i++){ const char *kbuf; int ksiz; TCLISTVAL(kbuf, args, i, ksiz); tclistclear(targs); TCLISTPUSH(targs, kbuf, ksiz); int idx = tcadbmulidx(mul, kbuf, ksiz); TCADB *adb = mul->adbs[idx]; TCLIST *res = tcadbmisc(adb, name, targs); if(res){ int rnum = TCLISTNUM(res); for(int j = 0; j < rnum; j++){ const char *vbuf; int vsiz; TCLISTVAL(vbuf, res, j, vsiz); TCLISTPUSH(rv, vbuf, vsiz); } tclistdel(res); } } tclistdel(targs); } else if(*name == '%'){ name++; int anum = TCLISTNUM(args) - 1; TCLIST *targs = tclistnew2(2); for(int i = 0; i < anum; i += 2){ const char *kbuf, *vbuf; int ksiz, vsiz; TCLISTVAL(kbuf, args, i, ksiz); TCLISTVAL(vbuf, args, i + 1, vsiz); tclistclear(targs); TCLISTPUSH(targs, kbuf, ksiz); TCLISTPUSH(targs, vbuf, vsiz); int idx = tcadbmulidx(mul, kbuf, ksiz); TCADB *adb = mul->adbs[idx]; TCLIST *res = tcadbmisc(adb, name, targs); if(res){ int rnum = TCLISTNUM(res); for(int j = 0; j < rnum; j++){ TCLISTVAL(vbuf, res, j, vsiz); TCLISTPUSH(rv, vbuf, vsiz); } tclistdel(res); } } tclistdel(targs); } else { for(int i = 0; i < num; i++){ TCLIST *res = tcadbmisc(adbs[i], name, args); if(res){ int rnum = TCLISTNUM(res); for(int j = 0; j < rnum; j++){ const char *vbuf; int vsiz; TCLISTVAL(vbuf, res, j, vsiz); TCLISTPUSH(rv, vbuf, vsiz); } tclistdel(res); } else { tclistdel(rv); rv = NULL; break; } } } return rv; } /* Store a record into a multiple database object with a duplication handler. `mul' specifies the multiple 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. `proc' specifies the pointer to the callback function to process duplication. `op' specifies an arbitrary pointer to be given as a parameter of the callback function. If successful, the return value is true, else, it is false. */ static bool tcadbmulputproc(ADBMUL *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op){ assert(mul && kbuf && ksiz >= 0 && proc); if(!mul->adbs) return false; int idx = tcadbmulidx(mul, kbuf, ksiz); TCADB *adb = mul->adbs[idx]; return tcadbputproc(adb, kbuf, ksiz, vbuf, vsiz, proc, op); } /* Process each record atomically of a multiple database object. `mul' specifies the multiple database object. `iter' specifies the pointer to the iterator function called for each record. `op' specifies an arbitrary pointer to be given as a parameter of the iterator function. If successful, the return value is true, else, it is false. */ static bool tcadbmulforeach(ADBMUL *mul, TCITER iter, void *op){ assert(mul && iter); if(!mul->adbs) return false; TCADB **adbs = mul->adbs; int num = mul->num; bool err = false; for(int i = 0; i < num; i++){ if(!tcadbforeach(adbs[i], iter, op)){ err = true; break; } } return !err; } /* Get the database index of a multiple database object. `mul' specifies the multiple database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. The return value is the bucket index. */ static int tcadbmulidx(ADBMUL *mul, const void *kbuf, int ksiz){ assert(mul && kbuf && ksiz >= 0); uint32_t hash = 20090810; const char *rp = (char *)kbuf + ksiz; while(ksiz--){ hash = (hash * 29) ^ *(uint8_t *)--rp; } return hash % mul->num; } /* Call the mapping function for every record of a multiple 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. `op' specifies the pointer to the optional opaque object. The return value is true to continue iteration or false to stop iteration. */ static bool tcadbmapbdbiter(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op){ assert(kbuf && ksiz >= 0 && vbuf && vsiz >= 0 && op); ADBMAPBDB *map = op; bool err = false; if(!map->proc(map, kbuf, ksiz, vbuf, vsiz, map->op)) err = true; return !err; } /* Dump all cached records into the B+ tree database. `map' specifies the mapper object for the B+ tree database. The return value is true if successful, else, it is false. */ static bool tcadbmapbdbdump(ADBMAPBDB *map){ assert(map); TCBDB *bdb = map->bdb; TCLIST *recs = map->recs; int rnum = TCLISTNUM(recs); TCCMP cmp = tcbdbcmpfunc(bdb); if(cmp == tccmplexical){ tclistsortex(recs, tcadbmapreccmplexical); } else if(cmp == tccmpdecimal){ tclistsortex(recs, tcadbmapreccmpdecimal); } else if(cmp == tccmpint32){ tclistsortex(recs, tcadbmapreccmpint32); } else if(cmp == tccmpint64){ tclistsortex(recs, tcadbmapreccmpint64); } bool err = false; for(int i = 0; i < rnum; i++){ const char *rbuf; int rsiz; TCLISTVAL(rbuf, recs, i, rsiz); int ksiz; memcpy(&ksiz, rbuf, sizeof(ksiz)); const char *kbuf = rbuf + sizeof(ksiz); if(!tcbdbputdup(bdb, kbuf, ksiz, kbuf + ksiz, rsiz - sizeof(ksiz) - ksiz)){ err = true; break; } } tclistclear(recs); map->rsiz = 0; return !err; } /* Compare two list elements by lexical order for mapping. `a' specifies the pointer to one element. `b' specifies the pointer to the other element. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tcadbmapreccmplexical(const TCLISTDATUM *a, const TCLISTDATUM *b){ assert(a && b); unsigned char *ao = (unsigned char *)((TCLISTDATUM *)a)->ptr; unsigned char *bo = (unsigned char *)((TCLISTDATUM *)b)->ptr; int size = (((TCLISTDATUM *)a)->size < ((TCLISTDATUM *)b)->size) ? ((TCLISTDATUM *)a)->size : ((TCLISTDATUM *)b)->size; for(int i = sizeof(int); i < size; i++){ if(ao[i] > bo[i]) return 1; if(ao[i] < bo[i]) return -1; } return ((TCLISTDATUM *)a)->size - ((TCLISTDATUM *)b)->size; } /* Compare two keys as decimal strings of real numbers for mapping. `a' specifies the pointer to one element. `b' specifies the pointer to the other element. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tcadbmapreccmpdecimal(const TCLISTDATUM *a, const TCLISTDATUM *b){ assert(a && b); return tccmpdecimal(((TCLISTDATUM *)a)->ptr + sizeof(int), a->size - sizeof(int), ((TCLISTDATUM *)b)->ptr + sizeof(int), b->size - sizeof(int), NULL); } /* Compare two list elements as 32-bit integers in the native byte order for mapping. `a' specifies the pointer to one element. `b' specifies the pointer to the other element. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tcadbmapreccmpint32(const TCLISTDATUM *a, const TCLISTDATUM *b){ assert(a && b); return tccmpint32(((TCLISTDATUM *)a)->ptr + sizeof(int), a->size - sizeof(int), ((TCLISTDATUM *)b)->ptr + sizeof(int), b->size - sizeof(int), NULL); } /* Compare two list elements as 64-bit integers in the native byte order for mapping. `a' specifies the pointer to one element. `b' specifies the pointer to the other element. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tcadbmapreccmpint64(const TCLISTDATUM *a, const TCLISTDATUM *b){ assert(a && b); return tccmpint64(((TCLISTDATUM *)a)->ptr + sizeof(int), a->size - sizeof(int), ((TCLISTDATUM *)b)->ptr + sizeof(int), b->size - sizeof(int), NULL); } /* Retrieve and remove each record corresponding to a query object. `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. `op' specifies the pointer to the optional opaque object. The return value is flags of the post treatment by bitwise-or. If successful, the return value is true, else, it is false. */ static int tcadbtdbqrygetout(const void *pkbuf, int pksiz, TCMAP *cols, void *op){ TCLIST *rv = ((void **)op)[0]; TCLIST *cnames = ((void **)op)[1]; int cnnum = TCLISTNUM(cnames); tcmapput(cols, "", 0, pkbuf, pksiz); tcmapmove(cols, "", 0, true); if(cnnum > 0){ TCMAP *ncols = tcmapnew2(cnnum + 1); for(int j = 0; j < cnnum; j++){ const char *cname; int cnsiz; TCLISTVAL(cname, cnames, j, cnsiz); int cvsiz; const char *cvalue = tcmapget(cols, cname, cnsiz, &cvsiz); if(cvalue) tcmapput(ncols, cname, cnsiz, cvalue, cvsiz); } int csiz; char *cbuf = tcstrjoin4(ncols, &csiz); tclistpushmalloc(rv, cbuf, csiz); tcmapdel(ncols); } else { int csiz; char *cbuf = tcstrjoin4(cols, &csiz); tclistpushmalloc(rv, cbuf, csiz); } return TDBQPOUT; } // END OF FILE tokyocabinet-1.4.48/tcamgr.c0000644000175000017500000006172112013574446014743 0ustar mikiomikio/************************************************************************************************* * The command line utility of the abstract database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include #include "myconf.h" /* global variables */ const char *g_progname; // program name /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void printerr(TCADB *adb); static int sepstrtochr(const char *str); static char *strtozsv(const char *str, int sep, int *sp); static int printdata(const char *ptr, int size, bool px, int sep); static void setskeltran(ADBSKEL *skel); static bool mapbdbproc(void *map, const char *kbuf, int ksiz, const char *vbuf, int vsiz, void *op); static int runcreate(int argc, char **argv); static int runinform(int argc, char **argv); static int runput(int argc, char **argv); static int runout(int argc, char **argv); static int runget(int argc, char **argv); static int runlist(int argc, char **argv); static int runoptimize(int argc, char **argv); static int runmisc(int argc, char **argv); static int runmap(int argc, char **argv); static int runversion(int argc, char **argv); static int proccreate(const char *name); static int procinform(const char *name); static int procput(const char *name, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode); static int procout(const char *name, const char *kbuf, int ksiz); static int procget(const char *name, const char *kbuf, int ksiz, int sep, bool px, bool pz); static int proclist(const char *name, int sep, int max, bool pv, bool px, const char *fmstr); static int procoptimize(const char *name, const char *params); static int procmisc(const char *name, const char *func, const TCLIST *args, int sep, bool px); static int procmap(const char *name, const char *dest, const char *fmstr); static int procversion(void); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; if(argc < 2) usage(); int rv = 0; if(!strcmp(argv[1], "create")){ rv = runcreate(argc, argv); } else if(!strcmp(argv[1], "inform")){ rv = runinform(argc, argv); } else if(!strcmp(argv[1], "put")){ rv = runput(argc, argv); } else if(!strcmp(argv[1], "out")){ rv = runout(argc, argv); } else if(!strcmp(argv[1], "get")){ rv = runget(argc, argv); } else if(!strcmp(argv[1], "list")){ rv = runlist(argc, argv); } else if(!strcmp(argv[1], "optimize")){ rv = runoptimize(argc, argv); } else if(!strcmp(argv[1], "misc")){ rv = runmisc(argc, argv); } else if(!strcmp(argv[1], "map")){ rv = runmap(argc, argv); } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){ rv = runversion(argc, argv); } else { usage(); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: the command line utility of the abstract database API\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s create name\n", g_progname); fprintf(stderr, " %s inform name\n", g_progname); fprintf(stderr, " %s put [-sx] [-sep chr] [-dk|-dc|-dai|-dad] name key value\n", g_progname); fprintf(stderr, " %s out [-sx] [-sep chr] name key\n", g_progname); fprintf(stderr, " %s get [-sx] [-sep chr] [-px] [-pz] name key\n", g_progname); fprintf(stderr, " %s list [-sep chr] [-m num] [-pv] [-px] [-fm str] name\n", g_progname); fprintf(stderr, " %s optimize name [params]\n", g_progname); fprintf(stderr, " %s misc [-sx] [-sep chr] [-px] name func [arg...]\n", g_progname); fprintf(stderr, " %s map [-fm str] name dest\n", g_progname); fprintf(stderr, " %s version\n", g_progname); fprintf(stderr, "\n"); exit(1); } /* get the character of separation string */ static int sepstrtochr(const char *str){ if(!strcmp(str, "\\t")) return '\t'; if(!strcmp(str, "\\r")) return '\r'; if(!strcmp(str, "\\n")) return '\n'; return *(unsigned char *)str; } /* encode a string as a zero separaterd string */ static char *strtozsv(const char *str, int sep, int *sp){ int size = strlen(str); char *buf = tcmemdup(str, size); for(int i = 0; i < size; i++){ if(buf[i] == sep) buf[i] = '\0'; } *sp = size; return buf; } /* print error information */ static void printerr(TCADB *adb){ const char *path = tcadbpath(adb); fprintf(stderr, "%s: %s: error\n", g_progname, path ? path : "-"); } /* print record data */ static int printdata(const char *ptr, int size, bool px, int sep){ int len = 0; while(size-- > 0){ if(px){ if(len > 0) putchar(' '); len += printf("%02X", *(unsigned char *)ptr); } else if(sep > 0){ if(*ptr == '\0'){ putchar(sep); } else { putchar(*ptr); } len++; } else { putchar(*ptr); len++; } ptr++; } return len; } /* set the transparent skeleton database */ static void setskeltran(ADBSKEL *skel){ memset(skel, 0, sizeof(*skel)); skel->opq = tcadbnew(); skel->del = (void (*)(void *))tcadbdel; skel->open = (bool (*)(void *, const char *))tcadbopen; skel->close = (bool (*)(void *))tcadbclose; skel->put = (bool (*)(void *, const void *, int, const void *, int))tcadbput; skel->putkeep = (bool (*)(void *, const void *, int, const void *, int))tcadbputkeep; skel->putcat = (bool (*)(void *, const void *, int, const void *, int))tcadbputcat; skel->out = (bool (*)(void *, const void *, int))tcadbout; skel->get = (void *(*)(void *, const void *, int, int *))tcadbget; skel->vsiz = (int (*)(void *, const void *, int))tcadbvsiz; skel->iterinit = (bool (*)(void *))tcadbiterinit; skel->iternext = (void *(*)(void *, int *))tcadbiternext; skel->fwmkeys = (TCLIST *(*)(void *, const void *, int, int))tcadbfwmkeys; skel->addint = (int (*)(void *, const void *, int, int))tcadbaddint; skel->adddouble = (double (*)(void *, const void *, int, double))tcadbadddouble; skel->sync = (bool (*)(void *))tcadbsync; skel->optimize = (bool (*)(void *, const char *))tcadboptimize; skel->vanish = (bool (*)(void *))tcadbvanish; skel->copy = (bool (*)(void *, const char *))tcadbcopy; skel->tranbegin = (bool (*)(void *))tcadbtranbegin; skel->trancommit = (bool (*)(void *))tcadbtrancommit; skel->tranabort = (bool (*)(void *))tcadbtranabort; skel->path = (const char *(*)(void *))tcadbpath; skel->rnum = (uint64_t (*)(void *))tcadbrnum; skel->size = (uint64_t (*)(void *))tcadbsize; skel->misc = (TCLIST *(*)(void *, const char *, const TCLIST *))tcadbmisc; skel->putproc = (bool (*)(void *, const void *, int, const void *, int, TCPDPROC, void *))tcadbputproc; skel->foreach = (bool (*)(void *, TCITER, void *))tcadbforeach; } /* mapping function */ static bool mapbdbproc(void *map, const char *kbuf, int ksiz, const char *vbuf, int vsiz, void *op){ bool err = false; if(!tcadbmapbdbemit(map, kbuf, ksiz, vbuf, vsiz)) err = true; return !err; } /* parse arguments of create command */ static int runcreate(int argc, char **argv){ char *name = NULL; for(int i = 2; i < argc; i++){ if(!name && argv[i][0] == '-'){ usage(); } else if(!name){ name = argv[i]; } else { usage(); } } if(!name) usage(); int rv = proccreate(name); return rv; } /* parse arguments of inform command */ static int runinform(int argc, char **argv){ char *name = NULL; for(int i = 2; i < argc; i++){ if(!name && argv[i][0] == '-'){ usage(); } else if(!name){ name = argv[i]; } else { usage(); } } if(!name) usage(); name = tcsprintf("%s#mode=r", name); int rv = procinform(name); tcfree(name); return rv; } /* parse arguments of put command */ static int runput(int argc, char **argv){ char *name = NULL; char *key = NULL; char *value = NULL; int dmode = 0; bool sx = false; int sep = -1; for(int i = 2; i < argc; i++){ if(!name && argv[i][0] == '-'){ if(!strcmp(argv[i], "-dk")){ dmode = -1; } else if(!strcmp(argv[i], "-dc")){ dmode = 1; } else if(!strcmp(argv[i], "-dai")){ dmode = 10; } else if(!strcmp(argv[i], "-dad")){ dmode = 11; } else if(!strcmp(argv[i], "-sx")){ sx = true; } else if(!strcmp(argv[i], "-sep")){ if(++i >= argc) usage(); sep = sepstrtochr(argv[i]); } else { usage(); } } else if(!name){ name = argv[i]; } else if(!key){ key = argv[i]; } else if(!value){ value = argv[i]; } else { usage(); } } if(!name || !key || !value) usage(); char *kbuf, *vbuf; int ksiz, vsiz; if(sx){ kbuf = tchexdecode(key, &ksiz); vbuf = tchexdecode(value, &vsiz); } else if(sep > 0){ kbuf = strtozsv(key, sep, &ksiz); vbuf = strtozsv(value, sep, &vsiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); vsiz = strlen(value); vbuf = tcmemdup(value, vsiz); } int rv = procput(name, kbuf, ksiz, vbuf, vsiz, dmode); tcfree(vbuf); tcfree(kbuf); return rv; } /* parse arguments of out command */ static int runout(int argc, char **argv){ char *name = NULL; char *key = NULL; bool sx = false; int sep = -1; for(int i = 2; i < argc; i++){ if(!name && argv[i][0] == '-'){ if(!strcmp(argv[i], "-sx")){ sx = true; } else if(!strcmp(argv[i], "-sep")){ if(++i >= argc) usage(); sep = sepstrtochr(argv[i]); } else { usage(); } } else if(!name){ name = argv[i]; } else if(!key){ key = argv[i]; } else { usage(); } } if(!name || !key) usage(); int ksiz; char *kbuf; if(sx){ kbuf = tchexdecode(key, &ksiz); } else if(sep > 0){ kbuf = strtozsv(key, sep, &ksiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); } int rv = procout(name, kbuf, ksiz); tcfree(kbuf); return rv; } /* parse arguments of get command */ static int runget(int argc, char **argv){ char *name = NULL; char *key = NULL; bool sx = false; int sep = -1; bool px = false; bool pz = false; for(int i = 2; i < argc; i++){ if(!name && argv[i][0] == '-'){ if(!strcmp(argv[i], "-sx")){ sx = true; } else if(!strcmp(argv[i], "-sep")){ if(++i >= argc) usage(); sep = sepstrtochr(argv[i]); } else if(!strcmp(argv[i], "-px")){ px = true; } else if(!strcmp(argv[i], "-pz")){ pz = true; } else { usage(); } } else if(!name){ name = argv[i]; } else if(!key){ key = argv[i]; } else { usage(); } } if(!name || !key) usage(); int ksiz; char *kbuf; if(sx){ kbuf = tchexdecode(key, &ksiz); } else if(sep > 0){ kbuf = strtozsv(key, sep, &ksiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); } name = tcsprintf("%s#mode=r", name); int rv = procget(name, kbuf, ksiz, sep, px, pz); tcfree(name); tcfree(kbuf); return rv; } /* parse arguments of list command */ static int runlist(int argc, char **argv){ char *name = NULL; int sep = -1; int max = -1; bool pv = false; bool px = false; char *fmstr = NULL; for(int i = 2; i < argc; i++){ if(!name && argv[i][0] == '-'){ if(!strcmp(argv[i], "-sep")){ if(++i >= argc) usage(); sep = sepstrtochr(argv[i]); } else if(!strcmp(argv[i], "-m")){ if(++i >= argc) usage(); max = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-pv")){ pv = true; } else if(!strcmp(argv[i], "-px")){ px = true; } else if(!strcmp(argv[i], "-fm")){ if(++i >= argc) usage(); fmstr = argv[i]; } else { usage(); } } else if(!name){ name = argv[i]; } else { usage(); } } if(!name) usage(); name = tcsprintf("%s#mode=r", name); int rv = proclist(name, sep, max, pv, px, fmstr); tcfree(name); return rv; } /* parse arguments of optimize command */ static int runoptimize(int argc, char **argv){ char *name = NULL; char *params = NULL; for(int i = 2; i < argc; i++){ if(!name && argv[i][0] == '-'){ usage(); } else if(!name){ name = argv[i]; } else if(!params){ params = argv[i]; } else { usage(); } } if(!name) usage(); int rv = procoptimize(name, params); return rv; } /* parse arguments of misc command */ static int runmisc(int argc, char **argv){ char *name = NULL; char *func = NULL; TCLIST *args = tcmpoollistnew(tcmpoolglobal()); bool sx = false; int sep = -1; bool px = false; for(int i = 2; i < argc; i++){ if(!name && argv[i][0] == '-'){ if(!strcmp(argv[i], "-sx")){ sx = true; } else if(!strcmp(argv[i], "-sep")){ if(++i >= argc) usage(); sep = sepstrtochr(argv[i]); } else if(!strcmp(argv[i], "-px")){ px = true; } else { usage(); } } else if(!name){ name = argv[i]; } else if(!func){ func = argv[i]; } else { if(sx){ int size; char *buf = tchexdecode(argv[i], &size); tclistpush(args, buf, size); tcfree(buf); } else if(sep > 0){ int size; char *buf = strtozsv(argv[i], sep, &size); tclistpush(args, buf, size); tcfree(buf); } else { tclistpush2(args, argv[i]); } } } if(!name || !func) usage(); int rv = procmisc(name, func, args, sep, px); return rv; } /* parse arguments of map command */ static int runmap(int argc, char **argv){ char *name = NULL; char *dest = NULL; char *fmstr = NULL; for(int i = 2; i < argc; i++){ if(!name && argv[i][0] == '-'){ if(!strcmp(argv[i], "-fm")){ if(++i >= argc) usage(); fmstr = argv[i]; } else { usage(); } } else if(!name){ name = argv[i]; } else if(!dest){ dest = argv[i]; } else { usage(); } } if(!name || !dest) usage(); name = tcsprintf("%s#mode=r", name); int rv = procmap(name, dest, fmstr); tcfree(name); return rv; } /* parse arguments of version command */ static int runversion(int argc, char **argv){ int rv = procversion(); return rv; } /* perform create command */ static int proccreate(const char *name){ TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ printerr(adb); skel.del(skel.opq); tcadbdel(adb); return 1; } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, 8)){ printerr(adb); tcadbdel(adb); return 1; } name++; } if(!tcadbopen(adb, name)){ printerr(adb); tcadbdel(adb); return 1; } bool err = false; if(!tcadbclose(adb)){ printerr(adb); err = true; } tcadbdel(adb); return err ? 1 : 0; } /* perform inform command */ static int procinform(const char *name){ TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ printerr(adb); skel.del(skel.opq); tcadbdel(adb); return 1; } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, 8)){ printerr(adb); tcadbdel(adb); return 1; } name++; } if(!tcadbopen(adb, name)){ printerr(adb); tcadbdel(adb); return 1; } bool err = false; const char *path = tcadbpath(adb); if(!path) path = "(unknown)"; printf("path: %s\n", path); const char *type = "(unknown)"; switch(tcadbomode(adb)){ case ADBOVOID: type = "not opened"; break; case ADBOMDB: type = "on-memory hash database"; break; case ADBONDB: type = "on-memory tree database"; break; case ADBOHDB: type = "hash database"; break; case ADBOBDB: type = "B+ tree database"; break; case ADBOFDB: type = "fixed-length database"; break; case ADBOTDB: type = "table database"; break; case ADBOSKEL: type = "skeleton database"; break; } printf("database type: %s\n", type); printf("record number: %llu\n", (unsigned long long)tcadbrnum(adb)); printf("size: %llu\n", (unsigned long long)tcadbsize(adb)); if(!tcadbclose(adb)){ printerr(adb); err = true; } tcadbdel(adb); return err ? 1 : 0; } /* perform put command */ static int procput(const char *name, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode){ TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ printerr(adb); skel.del(skel.opq); tcadbdel(adb); return 1; } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, 8)){ printerr(adb); tcadbdel(adb); return 1; } name++; } if(!tcadbopen(adb, name)){ printerr(adb); tcadbdel(adb); return 1; } bool err = false; int inum; double dnum; switch(dmode){ case -1: if(!tcadbputkeep(adb, kbuf, ksiz, vbuf, vsiz)){ printerr(adb); err = true; } break; case 1: if(!tcadbputcat(adb, kbuf, ksiz, vbuf, vsiz)){ printerr(adb); err = true; } break; case 10: inum = tcadbaddint(adb, kbuf, ksiz, tcatoi(vbuf)); if(inum == INT_MIN){ printerr(adb); err = true; } else { printf("%d\n", inum); } break; case 11: dnum = tcadbadddouble(adb, kbuf, ksiz, tcatof(vbuf)); if(isnan(dnum)){ printerr(adb); err = true; } else { printf("%.6f\n", dnum); } break; default: if(!tcadbput(adb, kbuf, ksiz, vbuf, vsiz)){ printerr(adb); err = true; } break; } if(!tcadbclose(adb)){ if(!err) printerr(adb); err = true; } tcadbdel(adb); return err ? 1 : 0; } /* perform out command */ static int procout(const char *name, const char *kbuf, int ksiz){ TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ printerr(adb); skel.del(skel.opq); tcadbdel(adb); return 1; } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, 8)){ printerr(adb); tcadbdel(adb); return 1; } name++; } if(!tcadbopen(adb, name)){ printerr(adb); tcadbdel(adb); return 1; } bool err = false; if(!tcadbout(adb, kbuf, ksiz)){ printerr(adb); err = true; } if(!tcadbclose(adb)){ if(!err) printerr(adb); err = true; } tcadbdel(adb); return err ? 1 : 0; } /* perform get command */ static int procget(const char *name, const char *kbuf, int ksiz, int sep, bool px, bool pz){ TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ printerr(adb); skel.del(skel.opq); tcadbdel(adb); return 1; } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, 8)){ printerr(adb); tcadbdel(adb); return 1; } name++; } if(!tcadbopen(adb, name)){ printerr(adb); tcadbdel(adb); return 1; } bool err = false; int vsiz; char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz); if(vbuf){ printdata(vbuf, vsiz, px, sep); if(!pz) putchar('\n'); tcfree(vbuf); } else { printerr(adb); err = true; } if(!tcadbclose(adb)){ if(!err) printerr(adb); err = true; } tcadbdel(adb); return err ? 1 : 0; } /* perform list command */ static int proclist(const char *name, int sep, int max, bool pv, bool px, const char *fmstr){ TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ printerr(adb); skel.del(skel.opq); tcadbdel(adb); return 1; } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, 8)){ printerr(adb); tcadbdel(adb); return 1; } name++; } if(!tcadbopen(adb, name)){ printerr(adb); tcadbdel(adb); return 1; } bool err = false; if(fmstr){ TCLIST *keys = tcadbfwmkeys2(adb, fmstr, max); for(int i = 0; i < tclistnum(keys); i++){ int ksiz; const char *kbuf = tclistval(keys, i, &ksiz); printdata(kbuf, ksiz, px, sep); if(pv){ int vsiz; char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz); if(vbuf){ putchar('\t'); printdata(vbuf, vsiz, px, sep); tcfree(vbuf); } } putchar('\n'); } tclistdel(keys); } else { if(!tcadbiterinit(adb)){ printerr(adb); err = true; } int ksiz; char *kbuf; int cnt = 0; while((kbuf = tcadbiternext(adb, &ksiz)) != NULL){ printdata(kbuf, ksiz, px, sep); if(pv){ int vsiz; char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz); if(vbuf){ putchar('\t'); printdata(vbuf, vsiz, px, sep); tcfree(vbuf); } } putchar('\n'); tcfree(kbuf); if(max >= 0 && ++cnt >= max) break; } } if(!tcadbclose(adb)){ if(!err) printerr(adb); err = true; } tcadbdel(adb); return err ? 1 : 0; } /* perform optimize command */ static int procoptimize(const char *name, const char *params){ TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ printerr(adb); skel.del(skel.opq); tcadbdel(adb); return 1; } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, 8)){ printerr(adb); tcadbdel(adb); return 1; } name++; } if(!tcadbopen(adb, name)){ printerr(adb); tcadbdel(adb); return 1; } bool err = false; if(!tcadboptimize(adb, params)){ printerr(adb); err = true; } if(!tcadbclose(adb)){ if(!err) printerr(adb); err = true; } tcadbdel(adb); return err ? 1 : 0; } /* perform misc command */ static int procmisc(const char *name, const char *func, const TCLIST *args, int sep, bool px){ TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ printerr(adb); skel.del(skel.opq); tcadbdel(adb); return 1; } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, 8)){ printerr(adb); tcadbdel(adb); return 1; } name++; } if(!tcadbopen(adb, name)){ printerr(adb); tcadbdel(adb); return 1; } bool err = false; TCLIST *res = tcadbmisc(adb, func, args); if(res){ for(int i = 0; i < tclistnum(res); i++){ int rsiz; const char *rbuf = tclistval(res, i, &rsiz); printdata(rbuf, rsiz, px, sep); printf("\n"); } tclistdel(res); } else { printerr(adb); err = true; } if(!tcadbclose(adb)){ if(!err) printerr(adb); err = true; } tcadbdel(adb); return err ? 1 : 0; } /* perform map command */ static int procmap(const char *name, const char *dest, const char *fmstr){ TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ printerr(adb); skel.del(skel.opq); tcadbdel(adb); return 1; } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, 8)){ printerr(adb); tcadbdel(adb); return 1; } name++; } if(!tcadbopen(adb, name)){ printerr(adb); tcadbdel(adb); return 1; } bool err = false; TCBDB *bdb = tcbdbnew(); if(!tcbdbopen(bdb, dest, BDBOWRITER | BDBOCREAT | BDBOTRUNC)){ printerr(adb); tcbdbdel(bdb); tcadbdel(adb); return 1; } if(fmstr){ TCLIST *keys = tcadbfwmkeys2(adb, fmstr, -1); if(!tcadbmapbdb(adb, keys, bdb, mapbdbproc, NULL, -1)){ printerr(adb); err = true; } tclistdel(keys); } else { if(!tcadbmapbdb(adb, NULL, bdb, mapbdbproc, NULL, -1)){ printerr(adb); err = true; } } if(!tcbdbclose(bdb)){ printerr(adb); err = true; } tcbdbdel(bdb); if(!tcadbclose(adb)){ printerr(adb); err = true; } tcadbdel(adb); return err ? 1 : 0; } /* perform version command */ static int procversion(void){ printf("Tokyo Cabinet version %s (%d:%s) for %s\n", tcversion, _TC_LIBVER, _TC_FORMATVER, TCSYSNAME); printf("Copyright (C) 2006-2012 FAL Labs\n"); return 0; } // END OF FILE tokyocabinet-1.4.48/lab/0000755000175000017500000000000011420767305014050 5ustar mikiomikiotokyocabinet-1.4.48/lab/calccomp0000755000175000017500000000677511043116724015570 0ustar mikiomikio#! /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 tokyocabinet-1.4.48/lab/printenv.cgi0000755000175000017500000000057311212230567016403 0ustar mikiomikio#! /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 tokyocabinet-1.4.48/lab/wgettsv0000755000175000017500000001643411303140142015471 0ustar mikiomikio#! /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 tokyocabinet-1.4.48/lab/htmltotsv0000755000175000017500000000522611226602035016037 0ustar mikiomikio#! /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 tokyocabinet-1.4.48/lab/stopwatch0000755000175000017500000000275611156705565016032 0ustar mikiomikio#! /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 tokyocabinet-1.4.48/lab/diffcheck0000755000175000017500000000143611130701517015677 0ustar mikiomikio#! /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 tokyocabinet-1.4.48/lab/magic0000644000175000017500000000161411135630016015044 0ustar mikiomikio# 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 tokyocabinet-1.4.48/lab/stepcount0000755000175000017500000000066211130675525016026 0ustar mikiomikio#! /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 tokyocabinet-1.4.48/lab/widthcheck0000755000175000017500000000204011303142466016102 0ustar mikiomikio#! /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 tokyocabinet-1.4.48/lab/tabcheck0000755000175000017500000000135211130701440015525 0ustar mikiomikio#! /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 tokyocabinet-1.4.48/lab/datechange0000755000175000017500000000240711420767256016071 0ustar mikiomikio#! /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 tokyocabinet-1.4.48/tcawmgr.c0000644000175000017500000004121312013574446015124 0ustar mikiomikio/************************************************************************************************* * The CGI utility of the abstract database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" #define MINIBNUM 31 // bucket number of map for trivial use #define OUTBUFSIZ (256*1024) // size of the output buffer #define UPLOADMAX (256*1024*1024) // maximum size of the upload data #define DEFSHOWNUM 30 // default number of show result #define FWMMAX 10000 // maximum number of forward matching #define VALWIDTH 80 // maximum width of shown value #define PAGETITLE "Abstract Database Manager" // page title #define DBNAME "casket" // name of the database #define XP(...) tcxstrprintf(obuf, __VA_ARGS__) // syntex sugar of output setting typedef struct { // type of structure of CGI parameters int action; // kind of the action const char *kbuf; // key buffer int ksiz; // key size const char *vbuf; // value buffer int vsiz; // value size int num; // number per page int page; // number of the page } PARAMS; enum { // enumeration for error codes ACTLIST, // list records ACTLISTVAL, // list records with values ACTPUT, // put a record ACTOUT, // remove a record ACTGET // get a record }; /* global variables */ const char *g_scriptname; // program name /* function prototypes */ int main(int argc, char **argv); static void readparameters(TCMAP *params); static void dolist(PARAMS *params, TCADB *db); static void doget(PARAMS *params, TCADB *db); static void doerror(int code, const char *msg); static void sethtmlheader(PARAMS *params, TCXSTR *obuf, TCADB *db); static void sethtmlfooter(PARAMS *params, TCXSTR *obuf, TCADB *db); static void sethtmlcomform(PARAMS *params, TCXSTR *obuf, TCADB *db); static void sethtmlrecval(const char *kbuf, int ksiz, TCXSTR *obuf, TCADB *db); /* main routine */ int main(int argc, char **argv){ g_scriptname = getenv("SCRIPT_NAME"); if(!g_scriptname) g_scriptname = argv[0]; const char *rp = strrchr(g_scriptname, '/'); if(rp) g_scriptname = rp + 1; TCMAP *pmap = tcmapnew2(MINIBNUM); readparameters(pmap); PARAMS params; params.action = ACTLIST; int size; const char *buf = tcmapget(pmap, "action", 6, &size); if(buf) params.action = tcatoix(buf); if(params.action < ACTLIST) params.action = ACTLIST; buf = tcmapget(pmap, "key", 3, &size); if(buf){ params.kbuf = buf; params.ksiz = size; } else { params.kbuf = ""; params.ksiz = 0; } buf = tcmapget(pmap, "value", 5, &size); if(buf){ params.vbuf = buf; params.vsiz = size; } else { params.vbuf = ""; params.vsiz = 0; } if(params.ksiz < 1){ buf = tcmapget(pmap, "value_filename", 14, &size); if(buf){ params.kbuf = buf; params.ksiz = size; } } params.num = 0; buf = tcmapget(pmap, "num", 3, &size); if(buf) params.num = tcatoix(buf); if(params.num < 1) params.num = DEFSHOWNUM; params.page = 1; buf = tcmapget(pmap, "page", 4, &size); if(buf) params.page = tcatoix(buf); if(params.page < 1) params.page = 1; bool wmode; switch(params.action){ case ACTPUT: case ACTOUT: wmode = true; break; default: wmode = false; break; } TCADB *db = tcadbnew(); char path[strlen(DBNAME)+16]; sprintf(path, "%s.tch#mode=%s", DBNAME, wmode ? "w" : "r"); if(!tcadbopen(db, path)){ sprintf(path, "%s.tcb#mode=%s", DBNAME, wmode ? "w" : "r"); if(!tcadbopen(db, path)){ sprintf(path, "%s.tcf#mode=%s", DBNAME, wmode ? "w" : "r"); tcadbopen(db, path); } } if(tcadbsize(db) > 0){ if(wmode) tcadbtranbegin(db); switch(params.action){ case ACTLIST: case ACTLISTVAL: case ACTPUT: case ACTOUT: dolist(¶ms, db); break; case ACTGET: doget(¶ms, db); break; default: doerror(400, "no such action"); break; } if(wmode) tcadbtrancommit(db); } else { doerror(500, "the database file could not be opened"); } tcadbdel(db); tcmapdel(pmap); return 0; } /* read CGI parameters */ static void readparameters(TCMAP *params){ int maxlen = UPLOADMAX; char *buf = NULL; int len = 0; const char *rp; if((rp = getenv("REQUEST_METHOD")) != NULL && !strcmp(rp, "POST") && (rp = getenv("CONTENT_LENGTH")) != NULL && (len = tcatoix(rp)) > 0){ if(len > maxlen) len = maxlen; buf = tccalloc(len + 1, 1); if(fread(buf, 1, len, stdin) != len){ tcfree(buf); buf = NULL; } } else if((rp = getenv("QUERY_STRING")) != NULL){ buf = tcstrdup(rp); len = strlen(buf); } if(buf && len > 0) tcwwwformdecode2(buf, len, getenv("CONTENT_TYPE"), params); tcfree(buf); } /* perform the list action */ static void dolist(PARAMS *params, TCADB *db){ printf("Content-Type: text/html\r\n"); printf("\r\n"); TCXSTR *obuf = tcxstrnew3(OUTBUFSIZ); sethtmlheader(params, obuf, db); if(params->action == ACTPUT){ XP("
\n"); if(params->ksiz < 1){ XP("

Error: the key should be specified.

\n"); } else if(tcadbput(db, params->kbuf, params->ksiz, params->vbuf, params->vsiz)){ XP("

Stored successfully!

\n"); } else { XP("

Error: unknown error.

\n"); } params->kbuf = ""; params->ksiz = 0; params->action = ACTLIST; } else if(params->action == ACTOUT){ XP("
\n"); if(params->ksiz < 1){ XP("

Error: the key should be specified.

\n"); } else if(tcadbout(db, params->kbuf, params->ksiz)){ XP("

Removed successfully!

\n"); } else if(tcadbvsiz(db, params->kbuf, params->ksiz) >= 0){ XP("

Error: unknown error.

\n"); } else { XP("

Error: no such record.

\n"); } params->kbuf = ""; params->ksiz = 0; params->action = ACTLIST; } sethtmlcomform(params, obuf, db); bool isnext = false; XP("
\n"); XP("
\n"); int num = params->num; bool sv = params->action == ACTLISTVAL; if(params->ksiz > 0){ TCLIST *keys = tcadbfwmkeys(db, params->kbuf, params->ksiz, FWMMAX); int knum = tclistnum(keys); int skip = params->num * (params->page - 1); int end = skip + params->num; for(int i = skip; i < knum && i < end; i++){ int ksiz; const char *kbuf = tclistval(keys, i, &ksiz); XP("
\n"); XP("%@", g_scriptname, ACTGET, kbuf, kbuf); if(sv) sethtmlrecval(kbuf, ksiz, obuf, db); XP("
\n"); } tclistdel(keys); isnext = knum > params->num * params->page; } else { tcadbiterinit(db); int ksiz; char *kbuf; int skip = params->num * (params->page - 1); for(int i = 0; i < skip && (kbuf = tcadbiternext(db, &ksiz)) != NULL; i++){ tcfree(kbuf); } for(int i = 0; i < num && (kbuf = tcadbiternext(db, &ksiz)) != NULL; i++){ XP("
\n"); XP("%@", g_scriptname, ACTGET, kbuf, kbuf); if(sv) sethtmlrecval(kbuf, ksiz, obuf, db); XP("
\n"); tcfree(kbuf); } isnext = tcadbrnum(db) > params->num * params->page; } XP("
\n"); XP("
\n"); XP("
\n", g_scriptname); XP("
\n"); if(params->page > 1){ XP("[PREV]\n", g_scriptname, params->action, params->kbuf, params->num, params->page - 1); } else { XP("[PREV]\n"); } if(isnext){ XP("[NEXT]\n", g_scriptname, params->action, params->kbuf, params->num, params->page + 1); } else { XP("[NEXT]\n"); } if(params->action == ACTLIST){ XP("[VALUE]\n", g_scriptname, ACTLISTVAL, params->kbuf, params->num, params->page); } else { XP("[NOVAL]\n", g_scriptname, ACTLIST, params->kbuf, params->num, params->page); } XP("\n"); XP("\n"); XP("
\n"); XP("
\n"); sethtmlfooter(params, obuf, db); fwrite(tcxstrptr(obuf), 1, tcxstrsize(obuf), stdout); tcxstrdel(obuf); } /* perform the get action */ static void doget(PARAMS *params, TCADB *db){ static char *types[] = { ".gz", "application/x-gzip", ".bz2", "application/x-bzip2", ".tar", "application/x-tar", ".zip", "application/zip", ".lzh", "application/octet-stream", ".pdf", "application/pdf", ".ps", "application/postscript", ".xml", "application/xml", ".html", "application/html", ".htm", "application/html", ".doc", "application/msword", ".xls", "application/vnd.ms-excel", ".ppt", "application/ms-powerpoint", ".swf", "application/x-shockwave-flash", ".png", "image/png", ".jpg", "image/jpeg", ".jpeg", "image/jpeg", ".gif", "image/gif", ".bmp", "image/bmp", ".tif", "image/tiff", ".tiff", "image/tiff", ".svg", "image/xml+svg", ".au", "audio/basic", ".snd", "audio/basic", ".mid", "audio/midi", ".midi", "audio/midi", ".mp3", "audio/mpeg", ".mp2", "audio/mpeg", ".wav", "audio/x-wav", ".tch", "application/x-tokyocabinet-hash", ".tcb", "application/x-tokyocabinet-btree", NULL }; int vsiz; char *vbuf = tcadbget(db, params->kbuf, params->ksiz, &vsiz); if(vbuf){ const char *type = "text/plain"; for(int i = 0; types[i]; i++){ if(tcstribwm(params->kbuf, types[i])){ type = types[i+1]; break; } } printf("Content-Type: %s\r\n", type); if(!strchr(params->kbuf, '\n') && !strchr(params->kbuf, '\r')){ if(!strchr(params->kbuf, ' ') && !strchr(params->kbuf, ';')){ printf("Content-Disposition: attachment; filename=%s\r\n", params->kbuf); } else { printf("Content-Disposition: attachment; filename=\"%s\"\r\n", params->kbuf); } } printf("\r\n"); fwrite(vbuf, 1, vsiz, stdout); tcfree(vbuf); } else { doerror(404, "no such record"); } } /* perform the error action */ static void doerror(int code, const char *msg){ printf("Status: %d %s\r\n", code, msg); printf("Content-Type: text/plain\r\n"); printf("\r\n"); printf("%d: %s\n", code, msg); } /* set the header of HTML */ static void sethtmlheader(PARAMS *params, TCXSTR *obuf, TCADB *db){ XP("\n"); XP("\n"); XP("\n"); XP("\n"); XP("\n"); XP("\n"); XP("\n"); XP("\n"); XP("\n"); XP("%@\n", PAGETITLE); XP("\n"); XP("\n"); XP("\n"); XP("\n"); XP("

%@

\n", g_scriptname, PAGETITLE); } /* set the footer of HTML */ static void sethtmlfooter(PARAMS *params, TCXSTR *obuf, TCADB *db){ XP("
\n"); XP("
record number: %lld
\n", (long long)tcadbrnum(db)); XP("
size: %lld
\n", (long long)tcadbsize(db)); XP("\n"); XP("\n"); XP("\n"); } /* set the common form of HTML */ static void sethtmlcomform(PARAMS *params, TCXSTR *obuf, TCADB *db){ XP("
\n"); XP("
\n", g_scriptname); XP("
\n"); XP("\n"); XP("\n"); XP("\n"); XP("\n", ACTPUT); XP("
\n"); XP("
\n"); XP("
\n"); XP("
\n", g_scriptname); XP("
\n"); XP("\n"); XP("\n"); XP("\n"); XP("\n", ACTPUT); XP("
\n"); XP("
\n"); XP("
\n"); XP("
\n", g_scriptname); XP("
\n"); XP("\n"); XP("\n"); XP("\n", ACTOUT); XP("
\n"); XP("
\n"); XP("
\n"); XP("
\n", g_scriptname); XP("
\n"); XP("\n", params->kbuf); XP("\n"); XP("\n", ACTGET); XP("
\n"); XP("
\n"); XP("
\n"); XP("
\n", g_scriptname); XP("
\n"); XP("\n", params->kbuf); XP("\n"); XP("\n", ACTLIST); XP("
\n"); XP("
\n"); } /* set the value of a record */ static void sethtmlrecval(const char *kbuf, int ksiz, TCXSTR *obuf, TCADB *db){ int vsiz; char *vbuf = tcadbget(db, kbuf, ksiz, &vsiz); if(!vbuf) return; XP(": "); bool hex = false; int width = VALWIDTH; for(int j = 0; j < vsiz; j++){ int c = ((unsigned char *)vbuf)[j]; if(c >= 0x20 && c <= 0x7e){ if(hex) tcxstrcat(obuf, " ", 1); switch(c){ case '<': tcxstrcat(obuf, "<", 4); break; case '>': tcxstrcat(obuf, ">", 4); break; case '&': tcxstrcat(obuf, "&", 5); break; default: tcxstrcat(obuf, vbuf + j, 1); break; } width--; hex = false; } else { XP(" %02X", c); width -= 2; hex = true; } if(width < 1){ XP(" ..."); break; } } XP(""); tcfree(vbuf); } // END OF FILE tokyocabinet-1.4.48/tcbdb.h0000644000175000017500000015733612013574446014561 0ustar mikiomikio/************************************************************************************************* * The B+ tree database API of Tokyo Cabinet * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #ifndef _TCBDB_H /* duplication check */ #define _TCBDB_H #if defined(__cplusplus) #define __TCBDB_CLINKAGEBEGIN extern "C" { #define __TCBDB_CLINKAGEEND } #else #define __TCBDB_CLINKAGEBEGIN #define __TCBDB_CLINKAGEEND #endif __TCBDB_CLINKAGEBEGIN #include #include /************************************************************************************************* * API *************************************************************************************************/ typedef struct { /* type of structure for a B+ tree database */ void *mmtx; /* mutex for method */ void *cmtx; /* mutex for cache */ TCHDB *hdb; /* internal database object */ char *opaque; /* opaque buffer */ bool open; /* whether the internal database is opened */ bool wmode; /* whether to be writable */ uint32_t lmemb; /* number of members in each leaf */ uint32_t nmemb; /* number of members in each node */ uint8_t opts; /* options */ uint64_t root; /* ID number of the root page */ uint64_t first; /* ID number of the first leaf */ uint64_t last; /* ID number of the last leaf */ uint64_t lnum; /* number of leaves */ uint64_t nnum; /* number of nodes */ uint64_t rnum; /* number of records */ TCMAP *leafc; /* cache for leaves */ TCMAP *nodec; /* cache for nodes */ TCCMP cmp; /* pointer to the comparison function */ void *cmpop; /* opaque object for the comparison function */ uint32_t lcnum; /* maximum number of cached leaves */ uint32_t ncnum; /* maximum number of cached nodes */ uint32_t lsmax; /* maximum size of each leaf */ uint32_t lschk; /* counter for leaf size checking */ uint64_t capnum; /* capacity number of records */ uint64_t *hist; /* history array of visited nodes */ int hnum; /* number of element of the history array */ volatile uint64_t hleaf; /* ID number of the leaf referred by the history */ volatile uint64_t lleaf; /* ID number of the last visited leaf */ bool tran; /* whether in the transaction */ char *rbopaque; /* opaque for rollback */ volatile uint64_t clock; /* logical clock */ volatile int64_t cnt_saveleaf; /* tesing counter for leaf save times */ volatile int64_t cnt_loadleaf; /* tesing counter for leaf load times */ volatile int64_t cnt_killleaf; /* tesing counter for leaf kill times */ volatile int64_t cnt_adjleafc; /* tesing counter for node cache adjust times */ volatile int64_t cnt_savenode; /* tesing counter for node save times */ volatile int64_t cnt_loadnode; /* tesing counter for node load times */ volatile int64_t cnt_adjnodec; /* tesing counter for node cache adjust times */ } TCBDB; enum { /* enumeration for additional flags */ BDBFOPEN = HDBFOPEN, /* whether opened */ BDBFFATAL = HDBFFATAL /* whether with fatal error */ }; enum { /* enumeration for tuning options */ BDBTLARGE = 1 << 0, /* use 64-bit bucket array */ BDBTDEFLATE = 1 << 1, /* compress each page with Deflate */ BDBTBZIP = 1 << 2, /* compress each record with BZIP2 */ BDBTTCBS = 1 << 3, /* compress each page with TCBS */ BDBTEXCODEC = 1 << 4 /* compress each record with outer functions */ }; enum { /* enumeration for open modes */ BDBOREADER = 1 << 0, /* open as a reader */ BDBOWRITER = 1 << 1, /* open as a writer */ BDBOCREAT = 1 << 2, /* writer creating */ BDBOTRUNC = 1 << 3, /* writer truncating */ BDBONOLCK = 1 << 4, /* open without locking */ BDBOLCKNB = 1 << 5, /* lock without blocking */ BDBOTSYNC = 1 << 6 /* synchronize every transaction */ }; typedef struct { /* type of structure for a B+ tree cursor */ TCBDB *bdb; /* database object */ uint64_t clock; /* logical clock */ uint64_t id; /* ID number of the leaf */ int32_t kidx; /* number of the key */ int32_t vidx; /* number of the value */ } BDBCUR; enum { /* enumeration for cursor put mode */ BDBCPCURRENT, /* current */ BDBCPBEFORE, /* before */ BDBCPAFTER /* after */ }; /* Get the message string corresponding to an error code. `ecode' specifies the error code. The return value is the message string of the error code. */ const char *tcbdberrmsg(int ecode); /* Create a B+ tree database object. The return value is the new B+ tree database object. */ TCBDB *tcbdbnew(void); /* Delete a B+ tree database object. `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. */ void tcbdbdel(TCBDB *bdb); /* Get the last happened error code of a B+ tree database object. `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. */ int tcbdbecode(TCBDB *bdb); /* Set mutual exclusion control of a B+ tree database object for threading. `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. */ bool tcbdbsetmutex(TCBDB *bdb); /* Set the custom comparison function of a B+ tree database object. `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 `tctccmplexical' (dafault), `tctccmpdecimal', `tctccmpint32', and `tctccmpint64' 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. */ bool tcbdbsetcmpfunc(TCBDB *bdb, TCCMP cmp, void *cmpop); /* Set the tuning parameters of a B+ tree database object. `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. */ bool tcbdbtune(TCBDB *bdb, int32_t lmemb, int32_t nmemb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts); /* Set the caching parameters of a B+ tree database object. `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. */ bool tcbdbsetcache(TCBDB *bdb, int32_t lcnum, int32_t ncnum); /* Set the size of the extra mapped memory of a B+ tree database object. `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. */ bool tcbdbsetxmsiz(TCBDB *bdb, int64_t xmsiz); /* Set the unit step number of auto defragmentation of a B+ tree database object. `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. */ bool tcbdbsetdfunit(TCBDB *bdb, int32_t dfunit); /* Open a database file and connect a B+ tree database object. `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. */ bool tcbdbopen(TCBDB *bdb, const char *path, int omode); /* Close a B+ tree database object. `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. */ bool tcbdbclose(TCBDB *bdb); /* Store a record into a B+ tree database object. `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. */ bool tcbdbput(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a string record into a B+ tree database object. `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. */ bool tcbdbput2(TCBDB *bdb, const char *kstr, const char *vstr); /* Store a new record into a B+ tree database object. `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. */ bool tcbdbputkeep(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a new string record into a B+ tree database object. `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. */ bool tcbdbputkeep2(TCBDB *bdb, const char *kstr, const char *vstr); /* Concatenate a value at the end of the existing record in a B+ tree database object. `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. */ bool tcbdbputcat(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Concatenate a string value at the end of the existing record in a B+ tree database object. `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. */ bool tcbdbputcat2(TCBDB *bdb, const char *kstr, const char *vstr); /* Store a record into a B+ tree database object with allowing duplication of keys. `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. */ bool tcbdbputdup(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a string record into a B+ tree database object with allowing duplication of keys. `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. */ bool tcbdbputdup2(TCBDB *bdb, const char *kstr, const char *vstr); /* Store records into a B+ tree database object with allowing duplication of keys. `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. */ bool tcbdbputdup3(TCBDB *bdb, const void *kbuf, int ksiz, const TCLIST *vals); /* Remove a record of a B+ tree database object. `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. */ bool tcbdbout(TCBDB *bdb, const void *kbuf, int ksiz); /* Remove a string record of a B+ tree database object. `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. */ bool tcbdbout2(TCBDB *bdb, const char *kstr); /* Remove records of a B+ tree database object. `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. */ bool tcbdbout3(TCBDB *bdb, const void *kbuf, int ksiz); /* Retrieve a record in a B+ tree database object. `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. */ void *tcbdbget(TCBDB *bdb, const void *kbuf, int ksiz, int *sp); /* Retrieve a string record in a B+ tree database object. `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. */ char *tcbdbget2(TCBDB *bdb, const char *kstr); /* Retrieve a record in a B+ tree database object as a volatile buffer. `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. */ const void *tcbdbget3(TCBDB *bdb, const void *kbuf, int ksiz, int *sp); /* Retrieve records in a B+ tree database object. `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. */ TCLIST *tcbdbget4(TCBDB *bdb, const void *kbuf, int ksiz); /* Get the number of records corresponding a key in a B+ tree database object. `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. */ int tcbdbvnum(TCBDB *bdb, const void *kbuf, int ksiz); /* Get the number of records corresponding a string key in a B+ tree database object. `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. */ int tcbdbvnum2(TCBDB *bdb, const char *kstr); /* Get the size of the value of a record in a B+ tree database object. `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. */ int tcbdbvsiz(TCBDB *bdb, const void *kbuf, int ksiz); /* Get the size of the value of a string record in a B+ tree database object. `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. */ int tcbdbvsiz2(TCBDB *bdb, const char *kstr); /* Get keys of ranged records in a B+ tree database object. `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. */ TCLIST *tcbdbrange(TCBDB *bdb, const void *bkbuf, int bksiz, bool binc, const void *ekbuf, int eksiz, bool einc, int max); /* Get string keys of ranged records in a B+ tree database object. `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. */ TCLIST *tcbdbrange2(TCBDB *bdb, const char *bkstr, bool binc, const char *ekstr, bool einc, int max); /* Get forward matching keys in a B+ tree database object. `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. */ TCLIST *tcbdbfwmkeys(TCBDB *bdb, const void *pbuf, int psiz, int max); /* Get forward matching string keys in a B+ tree database object. `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. */ TCLIST *tcbdbfwmkeys2(TCBDB *bdb, const char *pstr, int max); /* Add an integer to a record in a B+ tree database object. `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. */ int tcbdbaddint(TCBDB *bdb, const void *kbuf, int ksiz, int num); /* Add a real number to a record in a B+ tree database object. `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. */ double tcbdbadddouble(TCBDB *bdb, const void *kbuf, int ksiz, double num); /* Synchronize updated contents of a B+ tree database object with the file and the device. `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. */ bool tcbdbsync(TCBDB *bdb); /* Optimize the file of a B+ tree database object. `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. */ bool tcbdboptimize(TCBDB *bdb, int32_t lmemb, int32_t nmemb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts); /* Remove all records of a B+ tree database object. `bdb' specifies the B+ tree database object connected as a writer. If successful, the return value is true, else, it is false. */ bool tcbdbvanish(TCBDB *bdb); /* Copy the database file of a B+ tree database object. `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. */ bool tcbdbcopy(TCBDB *bdb, const char *path); /* Begin the transaction of a B+ tree database object. `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. */ bool tcbdbtranbegin(TCBDB *bdb); /* Commit the transaction of a B+ tree database object. `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. */ bool tcbdbtrancommit(TCBDB *bdb); /* Abort the transaction of a B+ tree database object. `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. */ bool tcbdbtranabort(TCBDB *bdb); /* Get the file path of a B+ tree database object. `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. */ const char *tcbdbpath(TCBDB *bdb); /* Get the number of records of a B+ tree database object. `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. */ uint64_t tcbdbrnum(TCBDB *bdb); /* Get the size of the database file of a B+ tree database object. `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. */ uint64_t tcbdbfsiz(TCBDB *bdb); /* Create a cursor object. `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. */ BDBCUR *tcbdbcurnew(TCBDB *bdb); /* Delete a cursor object. `cur' specifies the cursor object. */ void tcbdbcurdel(BDBCUR *cur); /* Move a cursor object to the first record. `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. */ bool tcbdbcurfirst(BDBCUR *cur); /* Move a cursor object to the last record. `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. */ bool tcbdbcurlast(BDBCUR *cur); /* Move a cursor object to the front of records corresponding a key. `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. */ bool tcbdbcurjump(BDBCUR *cur, const void *kbuf, int ksiz); /* Move a cursor object to the front of records corresponding a key string. `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. */ bool tcbdbcurjump2(BDBCUR *cur, const char *kstr); /* Move a cursor object to the previous record. `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. */ bool tcbdbcurprev(BDBCUR *cur); /* Move a cursor object to the next record. `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. */ bool tcbdbcurnext(BDBCUR *cur); /* Insert a record around a cursor object. `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. */ bool tcbdbcurput(BDBCUR *cur, const void *vbuf, int vsiz, int cpmode); /* Insert a string record around a cursor object. `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. */ bool tcbdbcurput2(BDBCUR *cur, const char *vstr, int cpmode); /* Remove the record where a cursor object is. `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. */ bool tcbdbcurout(BDBCUR *cur); /* Get the key of the record where the cursor object is. `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. */ void *tcbdbcurkey(BDBCUR *cur, int *sp); /* Get the key string of the record where the cursor object is. `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. */ char *tcbdbcurkey2(BDBCUR *cur); /* Get the key of the record where the cursor object is, as a volatile buffer. `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. */ const void *tcbdbcurkey3(BDBCUR *cur, int *sp); /* Get the value of the record where the cursor object is. `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. */ void *tcbdbcurval(BDBCUR *cur, int *sp); /* Get the value string of the record where the cursor object is. `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. */ char *tcbdbcurval2(BDBCUR *cur); /* Get the value of the record where the cursor object is, as a volatile buffer. `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. */ const void *tcbdbcurval3(BDBCUR *cur, int *sp); /* Get the key and the value of the record where the cursor object is. `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. */ bool tcbdbcurrec(BDBCUR *cur, TCXSTR *kxstr, TCXSTR *vxstr); /************************************************************************************************* * features for experts *************************************************************************************************/ /* Set the error code of a B+ tree database object. `bdb' specifies the B+ tree database object. `ecode' specifies the error code. `file' specifies the file name of the code. `line' specifies the line number of the code. `func' specifies the function name of the code. */ void tcbdbsetecode(TCBDB *bdb, int ecode, const char *filename, int line, const char *func); /* Set the file descriptor for debugging output. `bdb' specifies the B+ tree database object. `fd' specifies the file descriptor for debugging output. */ void tcbdbsetdbgfd(TCBDB *bdb, int fd); /* Get the file descriptor for debugging output. `bdb' specifies the B+ tree database object. The return value is the file descriptor for debugging output. */ int tcbdbdbgfd(TCBDB *bdb); /* Check whether mutual exclusion control is set to a B+ tree database object. `bdb' specifies the B+ tree database object. If mutual exclusion control is set, it is true, else it is false. */ bool tcbdbhasmutex(TCBDB *bdb); /* Synchronize updating contents on memory of a B+ tree database object. `bdb' specifies the B+ tree database object connected as a writer. `phys' specifies whether to synchronize physically. If successful, the return value is true, else, it is false. */ bool tcbdbmemsync(TCBDB *bdb, bool phys); /* Get the comparison function of a B+ tree database object. `bdb' specifies the B+ tree database object. The return value is the pointer to the comparison function. */ TCCMP tcbdbcmpfunc(TCBDB *bdb); /* Get the opaque object for the comparison function of a B+ tree database object. `bdb' specifies the B+ tree database object. The return value is the opaque object for the comparison function. */ void *tcbdbcmpop(TCBDB *bdb); /* Get the maximum number of cached leaf nodes of a B+ tree database object. `bdb' specifies the B+ tree database object. The return value is the maximum number of cached leaf nodes. */ uint32_t tcbdblmemb(TCBDB *bdb); /* Get the maximum number of cached non-leaf nodes of a B+ tree database object. `bdb' specifies the B+ tree database object. The return value is the maximum number of cached non-leaf nodes. */ uint32_t tcbdbnmemb(TCBDB *bdb); /* Get the number of the leaf nodes of B+ tree database object. `bdb' specifies the B+ tree database object. If successful, the return value is the number of the leaf nodes or 0 if the object does not connect to any database file. */ uint64_t tcbdblnum(TCBDB *bdb); /* Get the number of the non-leaf nodes of B+ tree database object. `bdb' specifies the B+ tree database object. If successful, the return value is the number of the non-leaf nodes or 0 if the object does not connect to any database file. */ uint64_t tcbdbnnum(TCBDB *bdb); /* Get the number of elements of the bucket array of a B+ tree database object. `bdb' specifies the B+ tree database object. The return value is the number of elements of the bucket array or 0 if the object does not connect to any database file. */ uint64_t tcbdbbnum(TCBDB *bdb); /* Get the record alignment of a B+ tree database object. `bdb' specifies the B+ tree database object. The return value is the record alignment or 0 if the object does not connect to any database file. */ uint32_t tcbdbalign(TCBDB *bdb); /* Get the maximum number of the free block pool of a B+ tree database object. `bdb' specifies the B+ tree database object. The return value is the maximum number of the free block pool or 0 if the object does not connect to any database file. */ uint32_t tcbdbfbpmax(TCBDB *bdb); /* Get the inode number of the database file of a B+ tree database object. `bdb' specifies the B+ tree database object. The return value is the inode number of the database file or 0 if the object does not connect to any database file. */ uint64_t tcbdbinode(TCBDB *bdb); /* Get the modification time of the database file of a B+ tree database object. `bdb' specifies the B+ tree database object. The return value is the inode number of the database file or 0 if the object does not connect to any database file. */ time_t tcbdbmtime(TCBDB *bdb); /* Get the additional flags of a B+ tree database object. `bdb' specifies the B+ tree database object. The return value is the additional flags. */ uint8_t tcbdbflags(TCBDB *bdb); /* Get the options of a B+ tree database object. `bdb' specifies the B+ tree database object. The return value is the options. */ uint8_t tcbdbopts(TCBDB *bdb); /* Get the pointer to the opaque field of a B+ tree database object. `bdb' specifies the B+ tree database object. The return value is the pointer to the opaque field whose size is 128 bytes. */ char *tcbdbopaque(TCBDB *bdb); /* Get the number of used elements of the bucket array of a B+ tree database object. `bdb' specifies the B+ tree database object. The return value is the number of used elements of the bucket array or 0 if the object does not connect to any database file. */ uint64_t tcbdbbnumused(TCBDB *bdb); /* Set the maximum size of each leaf node. `bdb' specifies the B+ tree database object which is not opened. `lsmax' specifies the maximum size of each leaf node. If it is not more than 0, the default value is specified. The default value is 16386. If successful, the return value is true, else, it is false. Note that the tuning parameters of the database should be set before the database is opened. */ bool tcbdbsetlsmax(TCBDB *bdb, uint32_t lsmax); /* Set the capacity number of records. `bdb' specifies the B+ tree database object which is not opened. `capnum' specifies the capacity number of records. If it is not more than 0, the capacity is unlimited. If successful, the return value is true, else, it is false. When the number of records exceeds the capacity, forehand records are removed implicitly. Note that the tuning parameters of the database should be set before the database is opened. */ bool tcbdbsetcapnum(TCBDB *bdb, uint64_t capnum); /* Set the custom codec functions of a B+ tree database object. `bdb' specifies the B+ tree database object. `enc' specifies the pointer to the custom encoding function. It receives four parameters. The first parameter is the pointer to the region. The second parameter is the size of the region. The third parameter is the pointer to the variable into which the size of the region of the return value is assigned. The fourth parameter is the pointer to the optional opaque object. It returns the pointer to the result object allocated with `malloc' call if successful, else, it returns `NULL'. `encop' specifies an arbitrary pointer to be given as a parameter of the encoding function. If it is not needed, `NULL' can be specified. `dec' specifies the pointer to the custom decoding function. `decop' specifies an arbitrary pointer to be given as a parameter of the decoding function. If it is not needed, `NULL' can be specified. If successful, the return value is true, else, it is false. Note that the custom codec functions should be set before the database is opened and should be set every time the database is being opened. */ bool tcbdbsetcodecfunc(TCBDB *bdb, TCCODEC enc, void *encop, TCCODEC dec, void *decop); /* Get the unit step number of auto defragmentation of a B+ tree database object. `bdb' specifies the B+ tree database object. The return value is the unit step number of auto defragmentation. */ uint32_t tcbdbdfunit(TCBDB *bdb); /* Perform dynamic defragmentation of a B+ tree database object. `bdb' specifies the B+ tree database object connected as a writer. `step' specifie the number of steps. If it is not more than 0, the whole file is defragmented gradually without keeping a continuous lock. If successful, the return value is true, else, it is false. */ bool tcbdbdefrag(TCBDB *bdb, int64_t step); /* Clear the cache of a B+ tree database object. `bdb' specifies the B+ tree database object. If successful, the return value is true, else, it is false. */ bool tcbdbcacheclear(TCBDB *bdb); /* Store a new record into a B+ tree database object with backward duplication. `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. */ bool tcbdbputdupback(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a new string record into a B+ tree database object with backward duplication. `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. */ bool tcbdbputdupback2(TCBDB *bdb, const char *kstr, const char *vstr); /* Store a record into a B+ tree database object with a duplication handler. `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. `NULL' means that record addition is ommited if there is no corresponding record. `vsiz' specifies the size of the region of the value. `proc' specifies the pointer to the callback function to process duplication. It receives four parameters. The first parameter is the pointer to the region of the value. The second parameter is the size of the region of the value. The third parameter is the pointer to the variable into which the size of the region of the return value is assigned. The fourth parameter is the pointer to the optional opaque object. It returns the pointer to the result object allocated with `malloc'. It is released by the caller. If it is `NULL', the record is not modified. If it is `(void *)-1', the record is removed. `op' specifies an arbitrary pointer to be given as a parameter of the callback function. If it is not needed, `NULL' can be specified. If successful, the return value is true, else, it is false. Note that the callback function can not perform any database operation because the function is called in the critical section guarded by the same locks of database operations. */ bool tcbdbputproc(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op); /* Move a cursor object to the rear of records corresponding a key. `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 last record corresponding the key or the previous substitute if completely matching record does not exist. */ bool tcbdbcurjumpback(BDBCUR *cur, const void *kbuf, int ksiz); /* Move a cursor object to the rear of records corresponding a key string. `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 last record corresponding the key or the previous substitute if completely matching record does not exist. */ bool tcbdbcurjumpback2(BDBCUR *cur, const char *kstr); /* Process each record atomically of a B+ tree database object. `bdb' specifies the B+ tree database object. `iter' specifies the pointer to the iterator function called for each record. It receives five parameters. The first parameter is the pointer to the region of the key. The second parameter is the size of the region of the key. The third parameter is the pointer to the region of the value. The fourth parameter is the size of the region of the value. The fifth parameter is the pointer to the optional opaque object. It returns true to continue iteration or false to stop 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. Note that the callback function can not perform any database operation because the function is called in the critical section guarded by the same locks of database operations. */ bool tcbdbforeach(TCBDB *bdb, TCITER iter, void *op); __TCBDB_CLINKAGEEND #endif /* duplication check */ /* END OF FILE */ tokyocabinet-1.4.48/tcfmgr.c0000644000175000017500000005026312013574446014747 0ustar mikiomikio/************************************************************************************************* * The command line utility of the fixed-length database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" /* global variables */ const char *g_progname; // program name int g_dbgfd; // debugging output /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void printerr(TCFDB *fdb); static int printdata(const char *ptr, int size, bool px); static char *mygetline(FILE *ifp); static int runcreate(int argc, char **argv); static int runinform(int argc, char **argv); static int runput(int argc, char **argv); static int runout(int argc, char **argv); static int runget(int argc, char **argv); static int runlist(int argc, char **argv); static int runoptimize(int argc, char **argv); static int runimporttsv(int argc, char **argv); static int runversion(int argc, char **argv); static int proccreate(const char *path, int width, int64_t limsiz); static int procinform(const char *path, int omode); static int procput(const char *path, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int omode, int dmode); static int procout(const char *path, const char *kbuf, int ksiz, int omode); static int procget(const char *path, const char *kbuf, int ksiz, int omode, bool px, bool pz); static int proclist(const char *path, int omode, int max, bool pv, bool px, const char *rlstr, const char *rustr, const char *ristr); static int procoptimize(const char *path, int width, int64_t limsiz, int omode); static int procimporttsv(const char *path, const char *file, int omode, bool sc); static int procversion(void); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; g_dbgfd = -1; const char *ebuf = getenv("TCDBGFD"); if(ebuf) g_dbgfd = tcatoix(ebuf); if(argc < 2) usage(); int rv = 0; if(!strcmp(argv[1], "create")){ rv = runcreate(argc, argv); } else if(!strcmp(argv[1], "inform")){ rv = runinform(argc, argv); } else if(!strcmp(argv[1], "put")){ rv = runput(argc, argv); } else if(!strcmp(argv[1], "out")){ rv = runout(argc, argv); } else if(!strcmp(argv[1], "get")){ rv = runget(argc, argv); } else if(!strcmp(argv[1], "list")){ rv = runlist(argc, argv); } else if(!strcmp(argv[1], "optimize")){ rv = runoptimize(argc, argv); } else if(!strcmp(argv[1], "importtsv")){ rv = runimporttsv(argc, argv); } else if(!strcmp(argv[1], "version") || !strcmp(argv[1], "--version")){ rv = runversion(argc, argv); } else { usage(); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: the command line utility of the fixed-length database API\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s create path [width [limsiz]]\n", g_progname); fprintf(stderr, " %s inform [-nl|-nb] path\n", g_progname); fprintf(stderr, " %s put [-nl|-nb] [-sx] [-dk|-dc|-dai|-dad] path key value\n", g_progname); fprintf(stderr, " %s out [-nl|-nb] [-sx] path key\n", g_progname); fprintf(stderr, " %s get [-nl|-nb] [-sx] [-px] [-pz] path key\n", g_progname); fprintf(stderr, " %s list [-nl|-nb] [-m num] [-pv] [-px] [-rb lkey ukey] [-ri str] path\n", g_progname); fprintf(stderr, " %s optimize [-nl|-nb] path [width [limsiz]]\n", g_progname); fprintf(stderr, " %s importtsv [-nl|-nb] [-sc] path [file]\n", g_progname); fprintf(stderr, " %s version\n", g_progname); fprintf(stderr, "\n"); exit(1); } /* print error information */ static void printerr(TCFDB *fdb){ const char *path = tcfdbpath(fdb); int ecode = tcfdbecode(fdb); fprintf(stderr, "%s: %s: %d: %s\n", g_progname, path ? path : "-", ecode, tcfdberrmsg(ecode)); } /* print record data */ static int printdata(const char *ptr, int size, bool px){ int len = 0; while(size-- > 0){ if(px){ if(len > 0) putchar(' '); len += printf("%02X", *(unsigned char *)ptr); } else { putchar(*ptr); len++; } ptr++; } return len; } /* read a line from a file descriptor */ static char *mygetline(FILE *ifp){ int len = 0; int blen = 1024; char *buf = tcmalloc(blen + 1); bool end = true; int c; while((c = fgetc(ifp)) != EOF){ end = false; if(c == '\0') continue; if(blen <= len){ blen *= 2; buf = tcrealloc(buf, blen + 1); } if(c == '\n' || c == '\r') c = '\0'; buf[len++] = c; if(c == '\0') break; } if(end){ tcfree(buf); return NULL; } buf[len] = '\0'; return buf; } /* parse arguments of create command */ static int runcreate(int argc, char **argv){ char *path = NULL; char *wstr = NULL; char *lstr = NULL; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ usage(); } else if(!path){ path = argv[i]; } else if(!wstr){ wstr = argv[i]; } else if(!lstr){ lstr = argv[i]; } else { usage(); } } if(!path) usage(); int width = wstr ? tcatoix(wstr) : -1; int64_t limsiz = lstr ? tcatoix(lstr) : -1; int rv = proccreate(path, width, limsiz); return rv; } /* parse arguments of inform command */ static int runinform(int argc, char **argv){ char *path = NULL; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procinform(path, omode); return rv; } /* parse arguments of put command */ static int runput(int argc, char **argv){ char *path = NULL; char *key = NULL; char *value = NULL; int omode = 0; int dmode = 0; bool sx = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else if(!strcmp(argv[i], "-dk")){ dmode = -1; } else if(!strcmp(argv[i], "-dc")){ dmode = 1; } else if(!strcmp(argv[i], "-dai")){ dmode = 10; } else if(!strcmp(argv[i], "-dad")){ dmode = 11; } else if(!strcmp(argv[i], "-sx")){ sx = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!key){ key = argv[i]; } else if(!value){ value = argv[i]; } else { usage(); } } if(!path || !key || !value) usage(); char *kbuf, *vbuf; int ksiz, vsiz; if(sx){ kbuf = tchexdecode(key, &ksiz); vbuf = tchexdecode(value, &vsiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); vsiz = strlen(value); vbuf = tcmemdup(value, vsiz); } int rv = procput(path, kbuf, ksiz, vbuf, vsiz, omode, dmode); tcfree(vbuf); tcfree(kbuf); return rv; } /* parse arguments of out command */ static int runout(int argc, char **argv){ char *path = NULL; char *key = NULL; int omode = 0; bool sx = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else if(!strcmp(argv[i], "-sx")){ sx = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!key){ key = argv[i]; } else { usage(); } } if(!path || !key) usage(); int ksiz; char *kbuf; if(sx){ kbuf = tchexdecode(key, &ksiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); } int rv = procout(path, kbuf, ksiz, omode); tcfree(kbuf); return rv; } /* parse arguments of get command */ static int runget(int argc, char **argv){ char *path = NULL; char *key = NULL; int omode = 0; bool sx = false; bool px = false; bool pz = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else if(!strcmp(argv[i], "-sx")){ sx = true; } else if(!strcmp(argv[i], "-px")){ px = true; } else if(!strcmp(argv[i], "-pz")){ pz = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!key){ key = argv[i]; } else { usage(); } } if(!path || !key) usage(); int ksiz; char *kbuf; if(sx){ kbuf = tchexdecode(key, &ksiz); } else { ksiz = strlen(key); kbuf = tcmemdup(key, ksiz); } int rv = procget(path, kbuf, ksiz, omode, px, pz); tcfree(kbuf); return rv; } /* parse arguments of list command */ static int runlist(int argc, char **argv){ char *path = NULL; int omode = 0; int max = -1; bool pv = false; bool px = false; char *rlstr = NULL; char *rustr = NULL; char *ristr = NULL; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else if(!strcmp(argv[i], "-m")){ if(++i >= argc) usage(); max = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-pv")){ pv = true; } else if(!strcmp(argv[i], "-px")){ px = true; } else if(!strcmp(argv[i], "-rb")){ if(++i >= argc) usage(); rlstr = argv[i]; if(++i >= argc) usage(); rustr = argv[i]; } else if(!strcmp(argv[i], "-ri")){ if(++i >= argc) usage(); ristr = argv[i]; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = proclist(path, omode, max, pv, px, rlstr, rustr, ristr); return rv; } /* parse arguments of optimize command */ static int runoptimize(int argc, char **argv){ char *path = NULL; char *wstr = NULL; char *lstr = NULL; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!wstr){ wstr = argv[i]; } else if(!lstr){ lstr = argv[i]; } else { usage(); } } if(!path) usage(); int width = wstr ? tcatoix(wstr) : -1; int64_t limsiz = lstr ? tcatoix(lstr) : -1; int rv = procoptimize(path, width, limsiz, omode); return rv; } /* parse arguments of importtsv command */ static int runimporttsv(int argc, char **argv){ char *path = NULL; char *file = NULL; int omode = 0; bool sc = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else if(!strcmp(argv[i], "-sc")){ sc = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!file){ file = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procimporttsv(path, file, omode, sc); return rv; } /* parse arguments of version command */ static int runversion(int argc, char **argv){ int rv = procversion(); return rv; } /* perform create command */ static int proccreate(const char *path, int width, int64_t limsiz){ TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(!tcfdbtune(fdb, width, limsiz)){ printerr(fdb); tcfdbdel(fdb); return 1; } if(!tcfdbopen(fdb, path, FDBOWRITER | FDBOCREAT | FDBOTRUNC)){ printerr(fdb); tcfdbdel(fdb); return 1; } bool err = false; if(!tcfdbclose(fdb)){ printerr(fdb); err = true; } tcfdbdel(fdb); return err ? 1 : 0; } /* perform inform command */ static int procinform(const char *path, int omode){ TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(!tcfdbopen(fdb, path, FDBOREADER | omode)){ printerr(fdb); tcfdbdel(fdb); return 1; } bool err = false; const char *npath = tcfdbpath(fdb); if(!npath) npath = "(unknown)"; printf("path: %s\n", npath); const char *type = "(unknown)"; switch(tcfdbtype(fdb)){ case TCDBTHASH: type = "hash"; break; case TCDBTBTREE: type = "btree"; break; case TCDBTFIXED: type = "fixed"; break; case TCDBTTABLE: type = "table"; break; } printf("database type: %s\n", type); uint8_t flags = tcfdbflags(fdb); printf("additional flags:"); if(flags & FDBFOPEN) printf(" open"); if(flags & FDBFFATAL) printf(" fatal"); printf("\n"); printf("minimum ID number: %llu\n", (unsigned long long)tcfdbmin(fdb)); printf("maximum ID number: %llu\n", (unsigned long long)tcfdbmax(fdb)); printf("width of the value: %u\n", (unsigned int)tcfdbwidth(fdb)); printf("limit file size: %llu\n", (unsigned long long)tcfdblimsiz(fdb)); printf("limit ID number: %llu\n", (unsigned long long)tcfdblimid(fdb)); printf("inode number: %lld\n", (long long)tcfdbinode(fdb)); char date[48]; tcdatestrwww(tcfdbmtime(fdb), INT_MAX, date); printf("modified time: %s\n", date); printf("record number: %llu\n", (unsigned long long)tcfdbrnum(fdb)); printf("file size: %llu\n", (unsigned long long)tcfdbfsiz(fdb)); if(!tcfdbclose(fdb)){ if(!err) printerr(fdb); err = true; } tcfdbdel(fdb); return err ? 1 : 0; } /* perform put command */ static int procput(const char *path, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int omode, int dmode){ TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(!tcfdbopen(fdb, path, FDBOWRITER | omode)){ printerr(fdb); tcfdbdel(fdb); return 1; } bool err = false; int inum; double dnum; switch(dmode){ case -1: if(!tcfdbputkeep2(fdb, kbuf, ksiz, vbuf, vsiz)){ printerr(fdb); err = true; } break; case 1: if(!tcfdbputcat2(fdb, kbuf, ksiz, vbuf, vsiz)){ printerr(fdb); err = true; } break; case 10: inum = tcfdbaddint(fdb, tcfdbkeytoid(kbuf, ksiz), tcatoi(vbuf)); if(inum == INT_MIN){ printerr(fdb); err = true; } else { printf("%d\n", inum); } break; case 11: dnum = tcfdbadddouble(fdb, tcfdbkeytoid(kbuf, ksiz), tcatof(vbuf)); if(isnan(dnum)){ printerr(fdb); err = true; } else { printf("%.6f\n", dnum); } break; default: if(!tcfdbput2(fdb, kbuf, ksiz, vbuf, vsiz)){ printerr(fdb); err = true; } break; } if(!tcfdbclose(fdb)){ if(!err) printerr(fdb); err = true; } tcfdbdel(fdb); return err ? 1 : 0; } /* perform out command */ static int procout(const char *path, const char *kbuf, int ksiz, int omode){ TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(!tcfdbopen(fdb, path, FDBOWRITER | omode)){ printerr(fdb); tcfdbdel(fdb); return 1; } bool err = false; if(!tcfdbout2(fdb, kbuf, ksiz)){ printerr(fdb); err = true; } if(!tcfdbclose(fdb)){ if(!err) printerr(fdb); err = true; } tcfdbdel(fdb); return err ? 1 : 0; } /* perform get command */ static int procget(const char *path, const char *kbuf, int ksiz, int omode, bool px, bool pz){ TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(!tcfdbopen(fdb, path, FDBOREADER | omode)){ printerr(fdb); tcfdbdel(fdb); return 1; } bool err = false; int vsiz; char *vbuf = tcfdbget2(fdb, kbuf, ksiz, &vsiz); if(vbuf){ printdata(vbuf, vsiz, px); if(!pz) putchar('\n'); tcfree(vbuf); } else { printerr(fdb); err = true; } if(!tcfdbclose(fdb)){ if(!err) printerr(fdb); err = true; } tcfdbdel(fdb); return err ? 1 : 0; } /* perform list command */ static int proclist(const char *path, int omode, int max, bool pv, bool px, const char *rlstr, const char *rustr, const char *ristr){ TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(!tcfdbopen(fdb, path, FDBOREADER | omode)){ printerr(fdb); tcfdbdel(fdb); return 1; } bool err = false; if(rlstr || ristr){ TCLIST *keys = ristr ? tcfdbrange5(fdb, ristr, max) : tcfdbrange3(fdb, rlstr, rustr, max); for(int i = 0; i < tclistnum(keys); i++){ int ksiz; const char *kbuf = tclistval(keys, i, &ksiz); printf("%s", kbuf); if(pv){ int vsiz; char *vbuf = tcfdbget2(fdb, kbuf, ksiz, &vsiz); if(vbuf){ putchar('\t'); printdata(vbuf, vsiz, px); tcfree(vbuf); } } putchar('\n'); } tclistdel(keys); } else { if(!tcfdbiterinit(fdb)){ printerr(fdb); err = true; } int cnt = 0; uint64_t id; while((id = tcfdbiternext(fdb)) > 0){ printf("%llu", (unsigned long long)id); if(pv){ int vsiz; char *vbuf = tcfdbget(fdb, id, &vsiz); if(vbuf){ putchar('\t'); printdata(vbuf, vsiz, px); tcfree(vbuf); } } putchar('\n'); if(max >= 0 && ++cnt >= max) break; } } if(!tcfdbclose(fdb)){ if(!err) printerr(fdb); err = true; } tcfdbdel(fdb); return err ? 1 : 0; } /* perform optimize command */ static int procoptimize(const char *path, int width, int64_t limsiz, int omode){ TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(!tcfdbopen(fdb, path, FDBOWRITER | omode)){ printerr(fdb); tcfdbdel(fdb); return 1; } bool err = false; if(!tcfdboptimize(fdb, width, limsiz)){ printerr(fdb); err = true; } if(!tcfdbclose(fdb)){ if(!err) printerr(fdb); err = true; } tcfdbdel(fdb); return err ? 1 : 0; } /* perform importtsv command */ static int procimporttsv(const char *path, const char *file, int omode, bool sc){ FILE *ifp = file ? fopen(file, "rb") : stdin; if(!ifp){ fprintf(stderr, "%s: could not open\n", file ? file : "(stdin)"); return 1; } TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(!tcfdbopen(fdb, path, FDBOWRITER | FDBOCREAT | omode)){ printerr(fdb); tcfdbdel(fdb); if(ifp != stdin) fclose(ifp); return 1; } bool err = false; char *line; int cnt = 0; while(!err && (line = mygetline(ifp)) != NULL){ char *pv = strchr(line, '\t'); if(!pv){ tcfree(line); continue; } *pv = '\0'; if(sc) tcstrutfnorm(line, TCUNSPACE | TCUNLOWER | TCUNNOACC | TCUNWIDTH); if(!tcfdbput3(fdb, line, pv + 1)){ printerr(fdb); err = true; } tcfree(line); if(cnt > 0 && cnt % 100 == 0){ putchar('.'); fflush(stdout); if(cnt % 5000 == 0) printf(" (%08d)\n", cnt); } cnt++; } printf(" (%08d)\n", cnt); if(!tcfdbclose(fdb)){ if(!err) printerr(fdb); err = true; } tcfdbdel(fdb); if(ifp != stdin) fclose(ifp); return err ? 1 : 0; } /* perform version command */ static int procversion(void){ printf("Tokyo Cabinet version %s (%d:%s) for %s\n", tcversion, _TC_LIBVER, _TC_FORMATVER, TCSYSNAME); printf("Copyright (C) 2006-2012 FAL Labs\n"); return 0; } // END OF FILE tokyocabinet-1.4.48/tcutil.h0000644000175000017500000056365112013574446015010 0ustar mikiomikio/************************************************************************************************* * The utility API of Tokyo Cabinet * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #ifndef _TCUTIL_H /* duplication check */ #define _TCUTIL_H #if defined(__cplusplus) #define __TCUTIL_CLINKAGEBEGIN extern "C" { #define __TCUTIL_CLINKAGEEND } #else #define __TCUTIL_CLINKAGEBEGIN #define __TCUTIL_CLINKAGEEND #endif __TCUTIL_CLINKAGEBEGIN #include #if ! defined(__cplusplus) #include #endif #include #include #include #include /************************************************************************************************* * basic utilities *************************************************************************************************/ /* String containing the version information. */ extern const char *tcversion; /* Pointer to the call back function for handling a fatal error. 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. */ extern void (*tcfatalfunc)(const char *); /* Allocate a region on memory. `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. */ void *tcmalloc(size_t size); /* Allocate a nullified region on memory. `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. */ void *tccalloc(size_t nmemb, size_t size); /* Re-allocate a region on memory. `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. */ void *tcrealloc(void *ptr, size_t size); /* Duplicate a region on memory. `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. */ void *tcmemdup(const void *ptr, size_t size); /* Duplicate a string on memory. `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. */ char *tcstrdup(const void *str); /* Free a region on memory. `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. */ void tcfree(void *ptr); /************************************************************************************************* * basic utilities (for experts) *************************************************************************************************/ /* type of the pointer to a comparison function. `aptr' specifies the pointer to the region of one key. `asiz' specifies the size of the region of one key. `bptr' specifies the pointer to the region of the other key. `bsiz' specifies the size of the region of the other key. `op' specifies the pointer to the optional opaque object. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ typedef int (*TCCMP)(const char *aptr, int asiz, const char *bptr, int bsiz, void *op); /* type of the pointer to a encoding or decoding function. `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. `op' specifies the pointer to the optional opaque object. If successful, the return value is the pointer to the result object allocated with `malloc' call, else, it is `NULL'. */ typedef void *(*TCCODEC)(const void *ptr, int size, int *sp, void *op); /* type of the pointer to a callback function to process record duplication. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. `sp' specifies the pointer to the variable into which the size of the region of the return value is assigned. `op' specifies the pointer to the optional opaque object. The return value is the pointer to the result object allocated with `malloc'. It is released by the caller. If it is `NULL', the record is not modified. */ typedef void *(*TCPDPROC)(const void *vbuf, int vsiz, int *sp, void *op); /* type of the pointer to a iterator function. `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. `op' specifies the pointer to the optional opaque object. The return value is true to continue iteration or false to stop iteration. */ typedef bool (*TCITER)(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op); /************************************************************************************************* * extensible string *************************************************************************************************/ typedef struct { /* type of structure for an extensible string object */ char *ptr; /* pointer to the region */ int size; /* size of the region */ int asize; /* size of the allocated region */ } TCXSTR; /* Create an extensible string object. The return value is the new extensible string object. */ TCXSTR *tcxstrnew(void); /* Create an extensible string object from a character string. `str' specifies the string of the initial content. The return value is the new extensible string object containing the specified string. */ TCXSTR *tcxstrnew2(const char *str); /* Create an extensible string object with the initial allocation size. `asiz' specifies the initial allocation size. The return value is the new extensible string object. */ TCXSTR *tcxstrnew3(int asiz); /* Copy an extensible string object. `xstr' specifies the extensible string object. The return value is the new extensible string object equivalent to the specified object. */ TCXSTR *tcxstrdup(const TCXSTR *xstr); /* Delete an extensible string object. `xstr' specifies the extensible string object. Note that the deleted object and its derivatives can not be used anymore. */ void tcxstrdel(TCXSTR *xstr); /* Concatenate a region to the end of an extensible string object. `xstr' specifies the extensible string object. `ptr' specifies the pointer to the region to be appended. `size' specifies the size of the region. */ void tcxstrcat(TCXSTR *xstr, const void *ptr, int size); /* Concatenate a character string to the end of an extensible string object. `xstr' specifies the extensible string object. `str' specifies the string to be appended. */ void tcxstrcat2(TCXSTR *xstr, const char *str); /* Get the pointer of the region of an extensible string object. `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. */ const void *tcxstrptr(const TCXSTR *xstr); /* Get the size of the region of an extensible string object. `xstr' specifies the extensible string object. The return value is the size of the region of the object. */ int tcxstrsize(const TCXSTR *xstr); /* Clear an extensible string object. `xstr' specifies the extensible string object. The internal buffer of the object is cleared and the size is set zero. */ void tcxstrclear(TCXSTR *xstr); /* Perform formatted output into an extensible string object. `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. */ void tcxstrprintf(TCXSTR *xstr, const char *format, ...); /* Allocate a formatted string on memory. `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. */ char *tcsprintf(const char *format, ...); /************************************************************************************************* * extensible string (for experts) *************************************************************************************************/ /* Convert an extensible string object into a usual allocated region. `xstr' specifies the extensible string object. The return value is the pointer to the allocated 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. 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. Because the region of the original object is deleted, it should not be deleted again. */ void *tcxstrtomalloc(TCXSTR *xstr); /* Create an extensible string object from an allocated region. `ptr' specifies the pointer to the region allocated with `malloc' call. `size' specifies the size of the region. The return value is the new extensible string object wrapping the specified region. Note that the specified region is released when the object is deleted. */ TCXSTR *tcxstrfrommalloc(void *ptr, int size); /************************************************************************************************* * array list *************************************************************************************************/ typedef struct { /* type of structure for an element of a list */ char *ptr; /* pointer to the region */ int size; /* size of the effective region */ } TCLISTDATUM; typedef struct { /* type of structure for an array list */ TCLISTDATUM *array; /* array of data */ int anum; /* number of the elements of the array */ int start; /* start index of used elements */ int num; /* number of used elements */ } TCLIST; /* Create a list object. The return value is the new list object. */ TCLIST *tclistnew(void); /* Create a list object with expecting the number of elements. `anum' specifies the number of elements expected to be stored in the list. The return value is the new list object. */ TCLIST *tclistnew2(int anum); /* Create a list object with initial string elements. `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. */ TCLIST *tclistnew3(const char *str, ...); /* Copy a list object. `list' specifies the list object. The return value is the new list object equivalent to the specified object. */ TCLIST *tclistdup(const TCLIST *list); /* Delete a list object. `list' specifies the list object. Note that the deleted object and its derivatives can not be used anymore. */ void tclistdel(TCLIST *list); /* Get the number of elements of a list object. `list' specifies the list object. The return value is the number of elements of the list. */ int tclistnum(const TCLIST *list); /* Get the pointer to the region of an element of a list object. `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'. */ const void *tclistval(const TCLIST *list, int index, int *sp); /* Get the string of an element of a list object. `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'. */ const char *tclistval2(const TCLIST *list, int index); /* Add an element at the end of a list object. `list' specifies the list object. `ptr' specifies the pointer to the region of the new element. `size' specifies the size of the region. */ void tclistpush(TCLIST *list, const void *ptr, int size); /* Add a string element at the end of a list object. `list' specifies the list object. `str' specifies the string of the new element. */ void tclistpush2(TCLIST *list, const char *str); /* Remove an element of the end of a list object. `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'. */ void *tclistpop(TCLIST *list, int *sp); /* Remove a string element of the end of a list object. `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'. */ char *tclistpop2(TCLIST *list); /* Add an element at the top of a list object. `list' specifies the list object. `ptr' specifies the pointer to the region of the new element. `size' specifies the size of the region. */ void tclistunshift(TCLIST *list, const void *ptr, int size); /* Add a string element at the top of a list object. `list' specifies the list object. `str' specifies the string of the new element. */ void tclistunshift2(TCLIST *list, const char *str); /* Remove an element of the top of a list object. `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'. */ void *tclistshift(TCLIST *list, int *sp); /* Remove a string element of the top of a list object. `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'. */ char *tclistshift2(TCLIST *list); /* Add an element at the specified location of a list object. `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. */ void tclistinsert(TCLIST *list, int index, const void *ptr, int size); /* Add a string element at the specified location of a list object. `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. */ void tclistinsert2(TCLIST *list, int index, const char *str); /* Remove an element at the specified location of a list object. `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'. */ void *tclistremove(TCLIST *list, int index, int *sp); /* Remove a string element at the specified location of a list object. `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'. */ char *tclistremove2(TCLIST *list, int index); /* Overwrite an element at the specified location of a list object. `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. */ void tclistover(TCLIST *list, int index, const void *ptr, int size); /* Overwrite a string element at the specified location of a list object. `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. */ void tclistover2(TCLIST *list, int index, const char *str); /* Sort elements of a list object in lexical order. `list' specifies the list object. */ void tclistsort(TCLIST *list); /* Search a list object for an element using liner search. `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. */ int tclistlsearch(const TCLIST *list, const void *ptr, int size); /* Search a list object for an element using binary search. `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. */ int tclistbsearch(const TCLIST *list, const void *ptr, int size); /* Clear a list object. `list' specifies the list object. All elements are removed. */ void tclistclear(TCLIST *list); /* Serialize a list object into a byte array. `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. */ void *tclistdump(const TCLIST *list, int *sp); /* Create a list object from a serialized byte array. `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. */ TCLIST *tclistload(const void *ptr, int size); /************************************************************************************************* * array list (for experts) *************************************************************************************************/ /* Add an allocated element at the end of a list object. `list' specifies the list object. `ptr' specifies the pointer to the region allocated with `malloc' call. `size' specifies the size of the region. Note that the specified region is released when the object is deleted. */ void tclistpushmalloc(TCLIST *list, void *ptr, int size); /* Sort elements of a list object in case-insensitive lexical order. `list' specifies the list object. */ void tclistsortci(TCLIST *list); /* Sort elements of a list object by an arbitrary comparison function. `list' specifies the list object. `cmp' specifies the pointer to the comparison function. The structure TCLISTDATUM has the member "ptr" which is the pointer to the region of the element, and the member "size" which is the size of the region. */ void tclistsortex(TCLIST *list, int (*cmp)(const TCLISTDATUM *, const TCLISTDATUM *)); /* Invert elements of a list object. `list' specifies the list object. */ void tclistinvert(TCLIST *list); /* Perform formatted output into a list object. `list' specifies the list 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. */ void tclistprintf(TCLIST *list, const char *format, ...); /************************************************************************************************* * hash map *************************************************************************************************/ typedef struct _TCMAPREC { /* type of structure for an element of a map */ int32_t ksiz; /* size of the region of the key */ int32_t vsiz; /* size of the region of the value */ struct _TCMAPREC *left; /* pointer to the left child */ struct _TCMAPREC *right; /* pointer to the right child */ struct _TCMAPREC *prev; /* pointer to the previous element */ struct _TCMAPREC *next; /* pointer to the next element */ } TCMAPREC; typedef struct { /* type of structure for a map */ TCMAPREC **buckets; /* bucket array */ TCMAPREC *first; /* pointer to the first element */ TCMAPREC *last; /* pointer to the last element */ TCMAPREC *cur; /* pointer to the current element */ uint32_t bnum; /* number of buckets */ uint64_t rnum; /* number of records */ uint64_t msiz; /* total size of records */ } TCMAP; /* Create a map object. The return value is the new map object. */ TCMAP *tcmapnew(void); /* Create a map object with specifying the number of the buckets. `bnum' specifies the number of the buckets. The return value is the new map object. */ TCMAP *tcmapnew2(uint32_t bnum); /* Create a map object with initial string elements. `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. */ TCMAP *tcmapnew3(const char *str, ...); /* Copy a map object. `map' specifies the map object. The return value is the new map object equivalent to the specified object. */ TCMAP *tcmapdup(const TCMAP *map); /* Delete a map object. `map' specifies the map object. Note that the deleted object and its derivatives can not be used anymore. */ void tcmapdel(TCMAP *map); /* Store a record into a map object. `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. */ void tcmapput(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a string record into a map object. `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. */ void tcmapput2(TCMAP *map, const char *kstr, const char *vstr); /* Store a new record into a map object. `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. */ bool tcmapputkeep(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a new string record into a map object. `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. */ bool tcmapputkeep2(TCMAP *map, const char *kstr, const char *vstr); /* Concatenate a value at the end of the value of the existing record in a map object. `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. */ void tcmapputcat(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Concatenate a string value at the end of the value of the existing record in a map object. `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. */ void tcmapputcat2(TCMAP *map, const char *kstr, const char *vstr); /* Remove a record of a map object. `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. */ bool tcmapout(TCMAP *map, const void *kbuf, int ksiz); /* Remove a string record of a map object. `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. */ bool tcmapout2(TCMAP *map, const char *kstr); /* Retrieve a record in a map object. `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. */ const void *tcmapget(const TCMAP *map, const void *kbuf, int ksiz, int *sp); /* Retrieve a string record in a map object. `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. */ const char *tcmapget2(const TCMAP *map, const char *kstr); /* Move a record to the edge of a map object. `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. */ bool tcmapmove(TCMAP *map, const void *kbuf, int ksiz, bool head); /* Move a string record to the edge of a map object. `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. */ bool tcmapmove2(TCMAP *map, const char *kstr, bool head); /* Initialize the iterator of a map object. `map' specifies the map object. The iterator is used in order to access the key of every record stored in the map object. */ void tcmapiterinit(TCMAP *map); /* Get the next key of the iterator of a map object. `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. */ const void *tcmapiternext(TCMAP *map, int *sp); /* Get the next key string of the iterator of a map object. `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. */ const char *tcmapiternext2(TCMAP *map); /* Get the number of records stored in a map object. `map' specifies the map object. The return value is the number of the records stored in the map object. */ uint64_t tcmaprnum(const TCMAP *map); /* Get the total size of memory used in a map object. `map' specifies the map object. The return value is the total size of memory used in a map object. */ uint64_t tcmapmsiz(const TCMAP *map); /* Create a list object containing all keys in a map object. `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. */ TCLIST *tcmapkeys(const TCMAP *map); /* Create a list object containing all values in a map object. `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. */ TCLIST *tcmapvals(const TCMAP *map); /* Add an integer to a record in a map object. `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. */ int tcmapaddint(TCMAP *map, const void *kbuf, int ksiz, int num); /* Add a real number to a record in a map object. `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. */ double tcmapadddouble(TCMAP *map, const void *kbuf, int ksiz, double num); /* Clear a map object. `map' specifies the map object. All records are removed. */ void tcmapclear(TCMAP *map); /* Remove front records of a map object. `map' specifies the map object. `num' specifies the number of records to be removed. */ void tcmapcutfront(TCMAP *map, int num); /* Serialize a map object into a byte array. `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. */ void *tcmapdump(const TCMAP *map, int *sp); /* Create a map object from a serialized byte array. `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. */ TCMAP *tcmapload(const void *ptr, int size); /************************************************************************************************* * hash map (for experts) *************************************************************************************************/ /* Store a record and make it semivolatile in a map object. `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 record is moved to the tail. */ void tcmapput3(TCMAP *map, const void *kbuf, int ksiz, const char *vbuf, int vsiz); /* Store a record of the value of two regions into a map object. `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. `fvbuf' specifies the pointer to the former region of the value. `fvsiz' specifies the size of the former region of the value. `lvbuf' specifies the pointer to the latter region of the value. `lvsiz' specifies the size of the latter region of the value. If a record with the same key exists in the map, it is overwritten. */ void tcmapput4(TCMAP *map, const void *kbuf, int ksiz, const void *fvbuf, int fvsiz, const void *lvbuf, int lvsiz); /* Concatenate a value at the existing record and make it semivolatile in a map object. `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. */ void tcmapputcat3(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a record into a map object with a duplication handler. `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. `NULL' means that record addition is ommited if there is no corresponding record. `vsiz' specifies the size of the region of the value. `proc' specifies the pointer to the callback function to process duplication. It receives four parameters. The first parameter is the pointer to the region of the value. The second parameter is the size of the region of the value. The third parameter is the pointer to the variable into which the size of the region of the return value is assigned. The fourth parameter is the pointer to the optional opaque object. It returns the pointer to the result object allocated with `malloc'. It is released by the caller. If it is `NULL', the record is not modified. If it is `(void *)-1', the record is removed. `op' specifies an arbitrary pointer to be given as a parameter of the callback function. If it is not needed, `NULL' can be specified. If successful, the return value is true, else, it is false. */ bool tcmapputproc(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op); /* Retrieve a semivolatile record in a map object. `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 internal region of the returned record is moved to the tail so that the record will survive for a time under LRU cache algorithm removing records from the head. */ const void *tcmapget3(TCMAP *map, const void *kbuf, int ksiz, int *sp); /* Retrieve a string record in a map object with specifying the default value string. `map' specifies the map object. `kstr' specifies the string of the key. `dstr' specifies the string of the default value. The return value is the string of the value of the corresponding record or the default value string. */ const char *tcmapget4(TCMAP *map, const char *kstr, const char *dstr); /* Initialize the iterator of a map object at the record corresponding a key. `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 there is no record corresponding the condition, the iterator is not modified. */ void tcmapiterinit2(TCMAP *map, const void *kbuf, int ksiz); /* Initialize the iterator of a map object at the record corresponding a key string. `map' specifies the map object. `kstr' specifies the string of the key. If there is no record corresponding the condition, the iterator is not modified. */ void tcmapiterinit3(TCMAP *map, const char *kstr); /* Get the value bound to the key fetched from the iterator of a map object. `kbuf' specifies the pointer to the region of the iteration key. `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 of the corresponding record. 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. */ const void *tcmapiterval(const void *kbuf, int *sp); /* Get the value string bound to the key fetched from the iterator of a map object. `kstr' specifies the string of the iteration key. The return value is the pointer to the region of the value of the corresponding record. */ const char *tcmapiterval2(const char *kstr); /* Create an array of strings of all keys in a map object. `map' specifies the map object. `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 all string keys in the map object. 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. Note that elements of the array point to the inner objects, whose life duration is synchronous with the map object. */ const char **tcmapkeys2(const TCMAP *map, int *np); /* Create an array of strings of all values in a map object. `map' specifies the map object. `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 all string values in the map object. 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. Note that elements of the array point to the inner objects, whose life duration is synchronous with the map object. */ const char **tcmapvals2(const TCMAP *map, int *np); /* Extract a map record from a serialized byte array. `ptr' specifies the pointer to the region of serialized byte array. `size' specifies the size of the region. `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. */ void *tcmaploadone(const void *ptr, int size, const void *kbuf, int ksiz, int *sp); /* Perform formatted output into a map object. `map' specifies the map object. `kstr' specifies the string of the key. `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. */ void tcmapprintf(TCMAP *map, const char *kstr, const char *format, ...); /************************************************************************************************* * ordered tree *************************************************************************************************/ typedef struct _TCTREEREC { /* type of structure for an element of a tree */ int32_t ksiz; /* size of the region of the key */ int32_t vsiz; /* size of the region of the value */ struct _TCTREEREC *left; /* pointer to the left child */ struct _TCTREEREC *right; /* pointer to the right child */ } TCTREEREC; typedef struct { /* type of structure for a tree */ TCTREEREC *root; /* pointer to the root element */ TCTREEREC *cur; /* pointer to the current element */ uint64_t rnum; /* number of records */ uint64_t msiz; /* total size of records */ TCCMP cmp; /* pointer to the comparison function */ void *cmpop; /* opaque object for the comparison function */ } TCTREE; /* Create a tree object. The return value is the new tree object. */ TCTREE *tctreenew(void); /* Create a tree object with specifying the custom comparison function. `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. */ TCTREE *tctreenew2(TCCMP cmp, void *cmpop); /* Copy a tree object. `tree' specifies the tree object. The return value is the new tree object equivalent to the specified object. */ TCTREE *tctreedup(const TCTREE *tree); /* Delete a tree object. `tree' specifies the tree object. Note that the deleted object and its derivatives can not be used anymore. */ void tctreedel(TCTREE *tree); /* Store a record into a tree object. `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. */ void tctreeput(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a string record into a tree object. `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. */ void tctreeput2(TCTREE *tree, const char *kstr, const char *vstr); /* Store a new record into a tree object. `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. */ bool tctreeputkeep(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a new string record into a tree object. `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. */ bool tctreeputkeep2(TCTREE *tree, const char *kstr, const char *vstr); /* Concatenate a value at the end of the value of the existing record in a tree object. `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. */ void tctreeputcat(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Concatenate a string value at the end of the value of the existing record in a tree object. `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. */ void tctreeputcat2(TCTREE *tree, const char *kstr, const char *vstr); /* Store a record into a tree object with a duplication handler. `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. `NULL' means that record addition is ommited if there is no corresponding record. `vsiz' specifies the size of the region of the value. `proc' specifies the pointer to the callback function to process duplication. It receives four parameters. The first parameter is the pointer to the region of the value. The second parameter is the size of the region of the value. The third parameter is the pointer to the variable into which the size of the region of the return value is assigned. The fourth parameter is the pointer to the optional opaque object. It returns the pointer to the result object allocated with `malloc'. It is released by the caller. If it is `NULL', the record is not modified. If it is `(void *)-1', the record is removed. `op' specifies an arbitrary pointer to be given as a parameter of the callback function. If it is not needed, `NULL' can be specified. If successful, the return value is true, else, it is false. */ bool tctreeputproc(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op); /* Remove a record of a tree object. `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. */ bool tctreeout(TCTREE *tree, const void *kbuf, int ksiz); /* Remove a string record of a tree object. `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. */ bool tctreeout2(TCTREE *tree, const char *kstr); /* Retrieve a record in a tree object. `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. */ const void *tctreeget(TCTREE *tree, const void *kbuf, int ksiz, int *sp); /* Retrieve a string record in a tree object. `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. */ const char *tctreeget2(TCTREE *tree, const char *kstr); /* Initialize the iterator of a tree object. `tree' specifies the tree object. The iterator is used in order to access the key of every record stored in the tree object. */ void tctreeiterinit(TCTREE *tree); /* Get the next key of the iterator of a tree object. `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. */ const void *tctreeiternext(TCTREE *tree, int *sp); /* Get the next key string of the iterator of a tree object. `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. */ const char *tctreeiternext2(TCTREE *tree); /* Get the number of records stored in a tree object. `tree' specifies the tree object. The return value is the number of the records stored in the tree object. */ uint64_t tctreernum(const TCTREE *tree); /* Get the total size of memory used in a tree object. `tree' specifies the tree object. The return value is the total size of memory used in a tree object. */ uint64_t tctreemsiz(const TCTREE *tree); /* Create a list object containing all keys in a tree object. `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. */ TCLIST *tctreekeys(const TCTREE *tree); /* Create a list object containing all values in a tree object. `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. */ TCLIST *tctreevals(const TCTREE *tree); /* Add an integer to a record in a tree object. `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. */ int tctreeaddint(TCTREE *tree, const void *kbuf, int ksiz, int num); /* Add a real number to a record in a tree object. `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. */ double tctreeadddouble(TCTREE *tree, const void *kbuf, int ksiz, double num); /* Clear a tree object. `tree' specifies the tree object. All records are removed. */ void tctreeclear(TCTREE *tree); /* Remove fringe records of a tree object. `tree' specifies the tree object. `num' specifies the number of records to be removed. */ void tctreecutfringe(TCTREE *tree, int num); /* Serialize a tree object into a byte array. `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. */ void *tctreedump(const TCTREE *tree, int *sp); /* Create a tree object from a serialized byte array. `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. */ TCTREE *tctreeload(const void *ptr, int size, TCCMP cmp, void *cmpop); /************************************************************************************************* * ordered tree (for experts) *************************************************************************************************/ /* Store a record into a tree object without balancing nodes. `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 structure of the tree is not modifed by this function. */ void tctreeput3(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a new record into a tree object without balancing nodes. `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 structure of the tree is not modifed by this function. */ bool tctreeputkeep3(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Concatenate a value at the existing record in a tree object without balancing nodes. `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 structure of the tree is not modifed by this function. */ void tctreeputcat3(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Retrieve a record in a tree object without balancing nodes. `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 structure of the tree is not modifed by this function. */ const void *tctreeget3(const TCTREE *tree, const void *kbuf, int ksiz, int *sp); /* Retrieve a string record in a tree object with specifying the default value string. `tree' specifies the tree object. `kstr' specifies the string of the key. `dstr' specifies the string of the default value. The return value is the string of the value of the corresponding record or the default value string. */ const char *tctreeget4(TCTREE *tree, const char *kstr, const char *dstr); /* Initialize the iterator of a tree object in front of records corresponding a key. `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. The iterator is set to the first record corresponding the key or the next substitute if completely matching record does not exist. */ void tctreeiterinit2(TCTREE *tree, const void *kbuf, int ksiz); /* Initialize the iterator of a tree object in front of records corresponding a key string. `tree' specifies the tree object. `kstr' specifies the string of the key. The iterator is set to the first record corresponding the key or the next substitute if completely matching record does not exist. */ void tctreeiterinit3(TCTREE *tree, const char *kstr); /* Get the value bound to the key fetched from the iterator of a tree object. `kbuf' specifies the pointer to the region of the iteration key. `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 of the corresponding record. 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. */ const void *tctreeiterval(const void *kbuf, int *sp); /* Get the value string bound to the key fetched from the iterator of a tree object. `kstr' specifies the string of the iteration key. The return value is the pointer to the region of the value of the corresponding record. */ const char *tctreeiterval2(const char *kstr); /* Create an array of strings of all keys in a tree object. `tree' specifies the tree object. `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 all string keys in the tree object. 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. Note that elements of the array point to the inner objects, whose life duration is synchronous with the tree object. */ const char **tctreekeys2(const TCTREE *tree, int *np); /* Create an array of strings of all values in a tree object. `tree' specifies the tree object. `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 all string values in the tree object. 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. Note that elements of the array point to the inner objects, whose life duration is synchronous with the tree object. */ const char **tctreevals2(const TCTREE *tree, int *np); /* Extract a tree record from a serialized byte array. `ptr' specifies the pointer to the region of serialized byte array. `size' specifies the size of the region. `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. */ void *tctreeloadone(const void *ptr, int size, const void *kbuf, int ksiz, int *sp); /* Perform formatted output into a tree object. `map' specifies the tree object. `kstr' specifies the string of the key. `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. */ void tctreeprintf(TCTREE *tree, const char *kstr, const char *format, ...); /************************************************************************************************* * on-memory hash database *************************************************************************************************/ typedef struct { /* type of structure for a on-memory hash database */ void **mmtxs; /* mutexes for method */ void *imtx; /* mutex for iterator */ TCMAP **maps; /* internal map objects */ int iter; /* index of maps for the iterator */ } TCMDB; /* Create an on-memory hash database object. The return value is the new on-memory hash database object. The object can be shared by plural threads because of the internal mutex. */ TCMDB *tcmdbnew(void); /* Create an on-memory hash database object with specifying the number of the buckets. `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. */ TCMDB *tcmdbnew2(uint32_t bnum); /* Delete an on-memory hash database object. `mdb' specifies the on-memory hash database object. */ void tcmdbdel(TCMDB *mdb); /* Store a record into an on-memory hash database object. `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. */ void tcmdbput(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a string record into an on-memory hash database object. `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. */ void tcmdbput2(TCMDB *mdb, const char *kstr, const char *vstr); /* Store a new record into an on-memory hash database object. `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. */ bool tcmdbputkeep(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a new string record into an on-memory hash database object. `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. */ bool tcmdbputkeep2(TCMDB *mdb, const char *kstr, const char *vstr); /* Concatenate a value at the end of the existing record in an on-memory hash database. `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. */ void tcmdbputcat(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Concatenate a string at the end of the existing record in an on-memory hash database. `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. */ void tcmdbputcat2(TCMDB *mdb, const char *kstr, const char *vstr); /* Remove a record of an on-memory hash database object. `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. */ bool tcmdbout(TCMDB *mdb, const void *kbuf, int ksiz); /* Remove a string record of an on-memory hash database object. `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. */ bool tcmdbout2(TCMDB *mdb, const char *kstr); /* Retrieve a record in an on-memory hash database object. `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. */ void *tcmdbget(TCMDB *mdb, const void *kbuf, int ksiz, int *sp); /* Retrieve a string record in an on-memory hash database object. `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. */ char *tcmdbget2(TCMDB *mdb, const char *kstr); /* Get the size of the value of a record in an on-memory hash database object. `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. */ int tcmdbvsiz(TCMDB *mdb, const void *kbuf, int ksiz); /* Get the size of the value of a string record in an on-memory hash database object. `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. */ int tcmdbvsiz2(TCMDB *mdb, const char *kstr); /* Initialize the iterator of an on-memory hash database object. `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 database. */ void tcmdbiterinit(TCMDB *mdb); /* Get the next key of the iterator of an on-memory hash database object. `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. */ void *tcmdbiternext(TCMDB *mdb, int *sp); /* Get the next key string of the iterator of an on-memory hash database object. `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. */ char *tcmdbiternext2(TCMDB *mdb); /* Get forward matching keys in an on-memory hash database object. `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. */ TCLIST *tcmdbfwmkeys(TCMDB *mdb, const void *pbuf, int psiz, int max); /* Get forward matching string keys in an on-memory hash database object. `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. */ TCLIST *tcmdbfwmkeys2(TCMDB *mdb, const char *pstr, int max); /* Get the number of records stored in an on-memory hash database object. `mdb' specifies the on-memory hash database object. The return value is the number of the records stored in the database. */ uint64_t tcmdbrnum(TCMDB *mdb); /* Get the total size of memory used in an on-memory hash database object. `mdb' specifies the on-memory hash database object. The return value is the total size of memory used in the database. */ uint64_t tcmdbmsiz(TCMDB *mdb); /* Add an integer to a record in an on-memory hash database object. `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. */ int tcmdbaddint(TCMDB *mdb, const void *kbuf, int ksiz, int num); /* Add a real number to a record in an on-memory hash database object. `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. */ double tcmdbadddouble(TCMDB *mdb, const void *kbuf, int ksiz, double num); /* Clear an on-memory hash database object. `mdb' specifies the on-memory hash database object. All records are removed. */ void tcmdbvanish(TCMDB *mdb); /* Remove front records of an on-memory hash database object. `mdb' specifies the on-memory hash database object. `num' specifies the number of records to be removed. */ void tcmdbcutfront(TCMDB *mdb, int num); /************************************************************************************************* * on-memory hash database (for experts) *************************************************************************************************/ /* Store a record and make it semivolatile in an on-memory hash database object. `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 map, it is overwritten. The record is moved to the tail. */ void tcmdbput3(TCMDB *mdb, const void *kbuf, int ksiz, const char *vbuf, int vsiz); /* Store a record of the value of two regions into an on-memory hash database object. `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. `fvbuf' specifies the pointer to the former region of the value. `fvsiz' specifies the size of the former region of the value. `lvbuf' specifies the pointer to the latter region of the value. `lvsiz' specifies the size of the latter region of the value. If a record with the same key exists in the database, it is overwritten. */ void tcmdbput4(TCMDB *mdb, const void *kbuf, int ksiz, const void *fvbuf, int fvsiz, const void *lvbuf, int lvsiz); /* Concatenate a value and make it semivolatile in on-memory hash database object. `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. */ void tcmdbputcat3(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a record into a on-memory hash database object with a duplication handler. `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. `NULL' means that record addition is ommited if there is no corresponding record. `vsiz' specifies the size of the region of the value. `proc' specifies the pointer to the callback function to process duplication. It receives four parameters. The first parameter is the pointer to the region of the value. The second parameter is the size of the region of the value. The third parameter is the pointer to the variable into which the size of the region of the return value is assigned. The fourth parameter is the pointer to the optional opaque object. It returns the pointer to the result object allocated with `malloc'. It is released by the caller. If it is `NULL', the record is not modified. If it is `(void *)-1', the record is removed. `op' specifies an arbitrary pointer to be given as a parameter of the callback function. If it is not needed, `NULL' can be specified. If successful, the return value is true, else, it is false. */ bool tcmdbputproc(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op); /* Retrieve a record and move it astern in an on-memory hash database object. `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 internal region of the returned record is moved to the tail so that the record will survive for a time under LRU cache algorithm removing records from the head. */ void *tcmdbget3(TCMDB *mdb, const void *kbuf, int ksiz, int *sp); /* Initialize the iterator of an on-memory map database object in front of a key. `mdb' specifies the on-memory map database object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. If there is no record corresponding the condition, the iterator is not modified. */ void tcmdbiterinit2(TCMDB *mdb, const void *kbuf, int ksiz); /* Initialize the iterator of an on-memory map database object in front of a key string. `mdb' specifies the on-memory map database object. `kstr' specifies the string of the key. If there is no record corresponding the condition, the iterator is not modified. */ void tcmdbiterinit3(TCMDB *mdb, const char *kstr); /* Process each record atomically of an on-memory hash database object. `iter' specifies the pointer to the iterator function called for each record. It receives five parameters. The first parameter is the pointer to the region of the key. The second parameter is the size of the region of the key. The third parameter is the pointer to the region of the value. The fourth parameter is the size of the region of the value. The fifth parameter is the pointer to the optional opaque object. It returns true to continue iteration or false to stop 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. */ void tcmdbforeach(TCMDB *mdb, TCITER iter, void *op); /************************************************************************************************* * on-memory tree database *************************************************************************************************/ typedef struct { /* type of structure for a on-memory tree database */ void *mmtx; /* mutex for method */ TCTREE *tree; /* internal tree object */ } TCNDB; /* Create an on-memory tree database object. The return value is the new on-memory tree database object. The object can be shared by plural threads because of the internal mutex. */ TCNDB *tcndbnew(void); /* Create an on-memory tree database object with specifying the custom comparison function. `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. */ TCNDB *tcndbnew2(TCCMP cmp, void *cmpop); /* Delete an on-memory tree database object. `ndb' specifies the on-memory tree database object. */ void tcndbdel(TCNDB *ndb); /* Store a record into an on-memory tree database object. `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. */ void tcndbput(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a string record into an on-memory tree database object. `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. */ void tcndbput2(TCNDB *ndb, const char *kstr, const char *vstr); /* Store a new record into an on-memory tree database object. `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. */ bool tcndbputkeep(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a new string record into an on-memory tree database object. `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. */ bool tcndbputkeep2(TCNDB *ndb, const char *kstr, const char *vstr); /* Concatenate a value at the end of the existing record in an on-memory tree database. `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. */ void tcndbputcat(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Concatenate a string at the end of the existing record in an on-memory tree database. `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. */ void tcndbputcat2(TCNDB *ndb, const char *kstr, const char *vstr); /* Remove a record of an on-memory tree database object. `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. */ bool tcndbout(TCNDB *ndb, const void *kbuf, int ksiz); /* Remove a string record of an on-memory tree database object. `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. */ bool tcndbout2(TCNDB *ndb, const char *kstr); /* Retrieve a record in an on-memory tree database object. `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. */ void *tcndbget(TCNDB *ndb, const void *kbuf, int ksiz, int *sp); /* Retrieve a string record in an on-memory tree database object. `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. */ char *tcndbget2(TCNDB *ndb, const char *kstr); /* Get the size of the value of a record in an on-memory tree database object. `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. */ int tcndbvsiz(TCNDB *ndb, const void *kbuf, int ksiz); /* Get the size of the value of a string record in an on-memory tree database object. `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. */ int tcndbvsiz2(TCNDB *ndb, const char *kstr); /* Initialize the iterator of an on-memory tree database object. `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. */ void tcndbiterinit(TCNDB *ndb); /* Get the next key of the iterator of an on-memory tree database object. `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. */ void *tcndbiternext(TCNDB *ndb, int *sp); /* Get the next key string of the iterator of an on-memory tree database object. `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. */ char *tcndbiternext2(TCNDB *ndb); /* Get forward matching keys in an on-memory tree database object. `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. */ TCLIST *tcndbfwmkeys(TCNDB *ndb, const void *pbuf, int psiz, int max); /* Get forward matching string keys in an on-memory tree database object. `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. */ TCLIST *tcndbfwmkeys2(TCNDB *ndb, const char *pstr, int max); /* Get the number of records stored in an on-memory tree database object. `ndb' specifies the on-memory tree database object. The return value is the number of the records stored in the database. */ uint64_t tcndbrnum(TCNDB *ndb); /* Get the total size of memory used in an on-memory tree database object. `ndb' specifies the on-memory tree database object. The return value is the total size of memory used in the database. */ uint64_t tcndbmsiz(TCNDB *ndb); /* Add an integer to a record in an on-memory tree database object. `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. */ int tcndbaddint(TCNDB *ndb, const void *kbuf, int ksiz, int num); /* Add a real number to a record in an on-memory tree database object. `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. */ double tcndbadddouble(TCNDB *ndb, const void *kbuf, int ksiz, double num); /* Clear an on-memory tree database object. `ndb' specifies the on-memory tree database object. All records are removed. */ void tcndbvanish(TCNDB *ndb); /* Remove fringe records of an on-memory tree database object. `ndb' specifies the on-memory tree database object. `num' specifies the number of records to be removed. */ void tcndbcutfringe(TCNDB *ndb, int num); /************************************************************************************************* * ordered tree (for experts) *************************************************************************************************/ /* Store a record into a on-memory tree database without balancing nodes. `ndb' specifies the on-memory tree database. `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 structure of the tree is not modifed by this function. */ void tcndbput3(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a new record into a on-memory tree database object without balancing nodes. `ndb' specifies the on-memory tree database. `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 structure of the tree is not modifed by this function. */ bool tcndbputkeep3(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Concatenate a value in a on-memory tree database without balancing nodes. `ndb' specifies the on-memory tree database. `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 structure of the tree is not modifed by this function. */ void tcndbputcat3(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a record into a on-memory tree database object with a duplication handler. `ndb' specifies the on-memory tree database. `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. `NULL' means that record addition is ommited if there is no corresponding record. `vsiz' specifies the size of the region of the value. `proc' specifies the pointer to the callback function to process duplication. It receives four parameters. The first parameter is the pointer to the region of the value. The second parameter is the size of the region of the value. The third parameter is the pointer to the variable into which the size of the region of the return value is assigned. The fourth parameter is the pointer to the optional opaque object. It returns the pointer to the result object allocated with `malloc'. It is released by the caller. If it is `NULL', the record is not modified. If it is `(void *)-1', the record is removed. `op' specifies an arbitrary pointer to be given as a parameter of the callback function. If it is not needed, `NULL' can be specified. If successful, the return value is true, else, it is false. */ bool tcndbputproc(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op); /* Retrieve a record in an on-memory tree database object without balancing nodes. `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 structure of the tree is not modifed by this function. */ void *tcndbget3(TCNDB *ndb, const void *kbuf, int ksiz, int *sp); /* Initialize the iterator of an on-memory tree database object in front of a key. `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. The iterator is set to the first record corresponding the key or the next substitute if completely matching record does not exist. */ void tcndbiterinit2(TCNDB *ndb, const void *kbuf, int ksiz); /* Initialize the iterator of an on-memory tree database object in front of a key string. `ndb' specifies the on-memory tree database object. `kstr' specifies the string of the key. The iterator is set to the first record corresponding the key or the next substitute if completely matching record does not exist. */ void tcndbiterinit3(TCNDB *ndb, const char *kstr); /* Process each record atomically of an on-memory tree database object. `iter' specifies the pointer to the iterator function called for each record. It receives five parameters. The first parameter is the pointer to the region of the key. The second parameter is the size of the region of the key. The third parameter is the pointer to the region of the value. The fourth parameter is the size of the region of the value. The fifth parameter is the pointer to the optional opaque object. It returns true to continue iteration or false to stop 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. */ void tcndbforeach(TCNDB *ndb, TCITER iter, void *op); /************************************************************************************************* * memory pool *************************************************************************************************/ typedef struct { /* type of an element of memory pool */ void *ptr; /* pointer */ void (*del)(void *); /* deleting function */ } TCMPELEM; typedef struct { /* type of structure for a memory pool object */ void *mutex; /* mutex for operations */ TCMPELEM *elems; /* array of elements */ int anum; /* number of the elements of the array */ int num; /* number of used elements */ } TCMPOOL; /* Create a memory pool object. The return value is the new memory pool object. */ TCMPOOL *tcmpoolnew(void); /* Delete a memory pool object. `mpool' specifies the memory pool object. Note that the deleted object and its derivatives can not be used anymore. */ void tcmpooldel(TCMPOOL *mpool); /* Relegate an arbitrary object to a memory pool object. `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. */ void *tcmpoolpush(TCMPOOL *mpool, void *ptr, void (*del)(void *)); /* Relegate an allocated region to a memory pool object. `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. */ void *tcmpoolpushptr(TCMPOOL *mpool, void *ptr); /* Relegate an extensible string object to a memory pool object. `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. */ TCXSTR *tcmpoolpushxstr(TCMPOOL *mpool, TCXSTR *xstr); /* Relegate a list object to a memory pool object. `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. */ TCLIST *tcmpoolpushlist(TCMPOOL *mpool, TCLIST *list); /* Relegate a map object to a memory pool object. `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. */ TCMAP *tcmpoolpushmap(TCMPOOL *mpool, TCMAP *map); /* Relegate a tree object to a memory pool object. `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. */ TCTREE *tcmpoolpushtree(TCMPOOL *mpool, TCTREE *tree); /* Allocate a region relegated to a memory pool object. `mpool' specifies the memory pool object. The return value is the pointer to the allocated region under the memory pool. */ void *tcmpoolmalloc(TCMPOOL *mpool, size_t size); /* Create an extensible string object relegated to a memory pool object. The return value is the new extensible string object under the memory pool. */ TCXSTR *tcmpoolxstrnew(TCMPOOL *mpool); /* Create a list object relegated to a memory pool object. The return value is the new list object under the memory pool. */ TCLIST *tcmpoollistnew(TCMPOOL *mpool); /* Create a map object relegated to a memory pool object. The return value is the new map object under the memory pool. */ TCMAP *tcmpoolmapnew(TCMPOOL *mpool); /* Create a tree object relegated to a memory pool object. The return value is the new tree object under the memory pool. */ TCTREE *tcmpooltreenew(TCMPOOL *mpool); /* Remove the most recently installed cleanup handler of a memory pool object. `mpool' specifies the memory pool object. `exe' specifies whether to execute the destructor of the removed handler. */ void tcmpoolpop(TCMPOOL *mpool, bool exe); /* Remove all cleanup handler of a memory pool object. `mpool' specifies the memory pool object. `exe' specifies whether to execute the destructors of the removed handlers. */ void tcmpoolclear(TCMPOOL *mpool, bool exe); /* Get the global memory pool object. The return value is the global memory pool object. The global memory pool object is a singleton and assured to be deleted when the porcess is terminating normally. */ TCMPOOL *tcmpoolglobal(void); /************************************************************************************************* * miscellaneous utilities *************************************************************************************************/ /* Get the larger value of two integers. `a' specifies an integer. `b' specifies the other integer. The return value is the larger value of the two. */ long tclmax(long a, long b); /* Get the lesser value of two integers. `a' specifies an integer. `b' specifies the other integer. The return value is the lesser value of the two. */ long tclmin(long a, long b); /* Get a random number as long integer based on uniform distribution. 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. */ unsigned long tclrand(void); /* Get a random number as double decimal based on uniform distribution. 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. */ double tcdrand(void); /* Get a random number as double decimal based on normal distribution. `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. */ double tcdrandnd(double avg, double sd); /* Compare two strings with case insensitive evaluation. `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. */ int tcstricmp(const char *astr, const char *bstr); /* Check whether a string begins with a 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. */ bool tcstrfwm(const char *str, const char *key); /* Check whether a string begins with a key with case insensitive evaluation. `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. */ bool tcstrifwm(const char *str, const char *key); /* Check whether a string ends with a 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. */ bool tcstrbwm(const char *str, const char *key); /* Check whether a string ends with a key with case insensitive evaluation. `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. */ bool tcstribwm(const char *str, const char *key); /* Calculate the edit distance of two strings. `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. */ int tcstrdist(const char *astr, const char *bstr); /* Calculate the edit distance of two UTF-8 strings. `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. */ int tcstrdistutf(const char *astr, const char *bstr); /* Convert the letters of a string into upper case. `str' specifies the string to be converted. The return value is the string itself. */ char *tcstrtoupper(char *str); /* Convert the letters of a string into lower case. `str' specifies the string to be converted. The return value is the string itself. */ char *tcstrtolower(char *str); /* Cut space characters at head or tail of a string. `str' specifies the string to be converted. The return value is the string itself. */ char *tcstrtrim(char *str); /* Squeeze space characters in a string and trim it. `str' specifies the string to be converted. The return value is the string itself. */ char *tcstrsqzspc(char *str); /* Substitute characters in a string. `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. */ char *tcstrsubchr(char *str, const char *rstr, const char *sstr); /* Count the number of characters in a string of UTF-8. `str' specifies the string of UTF-8. The return value is the number of characters in the string. */ int tcstrcntutf(const char *str); /* Cut a string of UTF-8 at the specified number of characters. `str' specifies the string of UTF-8. `num' specifies the number of characters to be kept. The return value is the string itself. */ char *tcstrcututf(char *str, int num); /* Convert a UTF-8 string into a UCS-2 array. `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. */ void tcstrutftoucs(const char *str, uint16_t *ary, int *np); /* Convert a UCS-2 array into a UTF-8 string. `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. */ int tcstrucstoutf(const uint16_t *ary, int num, char *str); /* Create a list object by splitting a string. `str' specifies the source string. `delim' 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. */ TCLIST *tcstrsplit(const char *str, const char *delims); /* Create a string by joining all elements of a list object. `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. */ char *tcstrjoin(const TCLIST *list, char delim); /* Convert a string to an integer. `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. */ int64_t tcatoi(const char *str); /* Convert a string with a metric prefix to an integer. `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. */ int64_t tcatoix(const char *str); /* Convert a string to a real number. `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. */ double tcatof(const char *str); /* Check whether a string matches a regular expression. `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. */ bool tcregexmatch(const char *str, const char *regex); /* Replace each substring matching a regular expression string. `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. */ char *tcregexreplace(const char *str, const char *regex, const char *alt); /* Get the MD5 hash value of a serial object. `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. */ void tcmd5hash(const void *ptr, int size, char *buf); /* Cipher or decipher a serial object with the Arcfour stream cipher. `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. */ void tcarccipher(const void *ptr, int size, const void *kbuf, int ksiz, void *obuf); /* Get the time of day in seconds. The return value is the time of day in seconds. The accuracy is in microseconds. */ double tctime(void); /* Get the Gregorian calendar of a time. `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. */ void tccalendar(int64_t t, int jl, int *yearp, int *monp, int *dayp, int *hourp, int *minp, int *secp); /* Format a date as a string in W3CDTF. `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". */ void tcdatestrwww(int64_t t, int jl, char *buf); /* Format a date as a string in RFC 1123 format. `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". */ void tcdatestrhttp(int64_t t, int jl, char *buf); /* Get the time value of a date string. `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. */ int64_t tcstrmktime(const char *str); /* Get the jet lag of the local time. The return value is the jet lag of the local time in seconds. */ int tcjetlag(void); /* Get the day of week of a date. `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. */ int tcdayofweek(int year, int mon, int day); /************************************************************************************************* * miscellaneous utilities (for experts) *************************************************************************************************/ enum { /* enumeration for UCS normalization */ TCUNSPACE = 1 << 0, /* white space normalization */ TCUNLOWER = 1 << 1, /* lower case normalization */ TCUNNOACC = 1 << 2, /* strip accent marks */ TCUNWIDTH = 1 << 3 /* half-width normalization */ }; enum { /* enumeration for KWIC generator */ TCKWMUTAB = 1 << 0, /* mark up by tabs */ TCKWMUCTRL = 1 << 1, /* mark up by control characters */ TCKWMUBRCT = 1 << 2, /* mark up by brackets */ TCKWNOOVER = 1 << 24, /* no overlap */ TCKWPULEAD = 1 << 25 /* pick up the lead string */ }; typedef struct { /* type of structure for a consistent hashing node */ uint32_t seq; /* sequential number */ uint32_t hash; /* hash value */ } TCCHIDXNODE; typedef struct { /* type of structure for a consistent hashing object */ TCCHIDXNODE *nodes; /* node array */ int nnum; /* number of the node array */ } TCCHIDX; /* Check whether a string is numeric completely or not. `str' specifies the string to be checked. The return value is true if the string is numeric, else, it is false. */ bool tcstrisnum(const char *str); /* Convert a hexadecimal string to an integer. `str' specifies the string. The return value is the integer. If the string does not contain numeric expression, 0 is returned. */ int64_t tcatoih(const char *str); /* Skip space characters at head of a string. `str' specifies the string. The return value is the pointer to the first non-space character. */ const char *tcstrskipspc(const char *str); /* Normalize a UTF-8 string. `str' specifies the string of UTF-8. `opts' specifies options by bitwise-or: `TCUNSPACE' specifies that white space characters are normalized into the ASCII space and they are squeezed into one, `TCUNLOWER' specifies that alphabetical characters are normalized into lower cases, `TCUNNOACC' specifies that alphabetical characters with accent marks are normalized without accent marks, `TCUNWIDTH' specifies that full-width characters are normalized into half-width characters. The return value is the string itself. */ char *tcstrutfnorm(char *str, int opts); /* Normalize a UCS-2 array. `ary' specifies the array of UCS-2 codes. `num' specifies the number of elements of the array. `opts' specifies options by bitwise-or: `TCUNSPACE' specifies that white space characters are normalized into the ASCII space and they are squeezed into one, `TCUNLOWER' specifies that alphabetical characters are normalized into lower cases, `TCUNNOACC' specifies that alphabetical characters with accent marks are normalized without accent marks, `TCUNWIDTH' specifies that full-width characters are normalized into half-width characters. The return value is the number of elements of the result array. */ int tcstrucsnorm(uint16_t *ary, int num, int opts); /* Generate a keyword-in-context string from a text and keywords. `str' specifies the text string of UTF-8. `words' specifies a list object of the keyword strings. `width' specifies the width of strings picked up around each keyword. `opts' specifies options by bitwise-or: `TCKWMUTAB' specifies that each keyword is marked up between two tab characters, `TCKWMUCTRL' specifies that each keyword is marked up by the STX (0x02) code and the ETX (0x03) code, `TCKWMUBRCT' specifies that each keyword is marked up by the two square brackets, `TCKWNOOVER' specifies that each context does not overlap, `TCKWPULEAD' specifies that the lead string is picked up forcibly. The return value is the list object whose elements are strings around keywords. 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. */ TCLIST *tcstrkwic(const char *str, const TCLIST *words, int width, int opts); /* Tokenize a text separating by white space characters. `str' specifies the string. The return value is the list object whose elements are extracted tokens. 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. */ TCLIST *tcstrtokenize(const char *str); /* Create a list object by splitting a region by zero code. `ptr' specifies the pointer to the region. `size' specifies the size of the region. 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. */ TCLIST *tcstrsplit2(const void *ptr, int size); /* Create a map object by splitting a string. `str' specifies the source string where the key and the value of each record are situated one after the other. `delim' specifies a string containing delimiting characters. The return value is a map object of the split records. 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. */ TCMAP *tcstrsplit3(const char *str, const char *delims); /* Create a map object by splitting a region by zero code. `ptr' specifies the pointer to the region where the key and the value of each record are situated one after the other. `size' specifies the size of the region. The return value is a map object of the split records. 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. */ TCMAP *tcstrsplit4(const void *ptr, int size); /* Create a region separated by zero code by joining all elements of a list object. `list' specifies a list object. The return value is the result region. `sp' specifies the pointer to the variable into which the size of the region of the return value is assigned. 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. */ void *tcstrjoin2(const TCLIST *list, int *sp); /* Create a string by joining all records of a map object. `map' specifies a map object. `delim' specifies a delimiting character. The return value is the result string where the key and the value of each record are situated one after the other. 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. */ char *tcstrjoin3(const TCMAP *map, char delim); /* Create a region separated by zero code by joining all records of a map object. `list' specifies a list object. The return value is the result region, where the key and the value of each record are situated one after the other. `sp' specifies the pointer to the variable into which the size of the region of the return value is assigned. 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. */ void *tcstrjoin4(const TCMAP *map, int *sp); /* Sort top records of an array. `base' spacifies the pointer to an array. `nmemb' specifies the number of elements of the array. `size' specifies the size of each element. `top' specifies the number of top records. `compar' specifies the pointer to comparing function. The two arguments specify the pointers of elements. The comparing function should returns positive if the former is big, negative if the latter is big, 0 if both are equal. */ void tctopsort(void *base, size_t nmemb, size_t size, size_t top, int(*compar)(const void *, const void *)); /* Suspend execution of the current thread. `sec' specifies the interval of the suspension in seconds. If successful, the return value is true, else, it is false. */ bool tcsleep(double sec); /* Get the current system information. The return value is a map object of the current system information or `NULL' on failure. The key "utime" indicates the user time of the CPU. The key "stime" indicates the system time of the CPU. The key "size" indicates the process size in bytes. The "rss" indicates the resident set size in bytes. "total" indicates the total size of the real memory. "free" indicates the free size of the real memory. "cached" indicates the cached size of the real memory. 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. */ TCMAP *tcsysinfo(void); /* Create a consistent hashing object. `range' specifies the number of nodes. It should be more than 0. The range of hash values is from 0 to less than the specified number. The return value is the new consistent hashing object. Consistent hashing is useful because the addition or removal of one node does not significantly change the mapping of keys to nodes. */ TCCHIDX *tcchidxnew(int range); /* Delete a consistent hashing object. `chidx' specifies the consistent hashing object. */ void tcchidxdel(TCCHIDX *chidx); /* Get the consistent hashing value of a record. `chidx' specifies the consistent hashing object. `ptr' specifies the pointer to the region of the record. `size' specifies the size of the region. The return value is the hash value of the record. */ int tcchidxhash(TCCHIDX *chidx, const void *ptr, int size); /************************************************************************************************* * filesystem utilities *************************************************************************************************/ /* Get the canonicalized absolute path of a file. `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. */ char *tcrealpath(const char *path); /* Get the status information of a file. `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. */ bool tcstatfile(const char *path, bool *isdirp, int64_t *sizep, int64_t *mtimep); /* Read whole data of a file. `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. */ void *tcreadfile(const char *path, int limit, int *sp); /* Read every line of a file. `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. */ TCLIST *tcreadfilelines(const char *path); /* Write data into a file. `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. */ bool tcwritefile(const char *path, const void *ptr, int size); /* Copy a file. `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. */ bool tccopyfile(const char *src, const char *dest); /* Read names of files in a directory. `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. */ TCLIST *tcreaddir(const char *path); /* Expand a pattern into a list of matched paths. `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. */ TCLIST *tcglobpat(const char *pattern); /* Remove a file or a directory and its sub ones recursively. `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. */ bool tcremovelink(const char *path); /* Write data into a file. `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. */ bool tcwrite(int fd, const void *buf, size_t size); /* Read data from a file. `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. */ bool tcread(int fd, void *buf, size_t size); /* Lock a file. `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. */ bool tclock(int fd, bool ex, bool nb); /* Unlock a file. `fd' specifies the file descriptor. The return value is true if successful, else, it is false. */ bool tcunlock(int fd); /* Execute a shell command. `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. */ int tcsystem(const char **args, int anum); /************************************************************************************************* * encoding utilities *************************************************************************************************/ /* Encode a serial object with URL encoding. `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. */ char *tcurlencode(const char *ptr, int size); /* Decode a string encoded with URL encoding. `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. */ char *tcurldecode(const char *str, int *sp); /* Break up a URL into elements. `str' specifies the URL string. The return value is the map object whose keys are the name of elements. The key "self" specifies 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. */ TCMAP *tcurlbreak(const char *str); /* Resolve a relative URL with an absolute URL. `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. */ char *tcurlresolve(const char *base, const char *target); /* Encode a serial object with Base64 encoding. `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. */ char *tcbaseencode(const char *ptr, int size); /* Decode a string encoded with Base64 encoding. `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. */ char *tcbasedecode(const char *str, int *sp); /* Encode a serial object with Quoted-printable encoding. `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. */ char *tcquoteencode(const char *ptr, int size); /* Decode a string encoded with Quoted-printable encoding. `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. */ char *tcquotedecode(const char *str, int *sp); /* Encode a string with MIME encoding. `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. */ char *tcmimeencode(const char *str, const char *encname, bool base); /* Decode a string encoded with MIME encoding. `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. */ char *tcmimedecode(const char *str, char *enp); /* Split a string of MIME into headers and the body. `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. */ char *tcmimebreak(const char *ptr, int size, TCMAP *headers, int *sp); /* Split multipart data of MIME into its parts. `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. */ TCLIST *tcmimeparts(const char *ptr, int size, const char *boundary); /* Encode a serial object with hexadecimal encoding. `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. */ char *tchexencode(const char *ptr, int size); /* Decode a string encoded with hexadecimal encoding. `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. */ char *tchexdecode(const char *str, int *sp); /* Compress a serial object with Packbits encoding. `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. */ char *tcpackencode(const char *ptr, int size, int *sp); /* Decompress a serial object compressed with Packbits encoding. `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. */ char *tcpackdecode(const char *ptr, int size, int *sp); /* Compress a serial object with TCBS encoding. `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. */ char *tcbsencode(const char *ptr, int size, int *sp); /* Decompress a serial object compressed with TCBS encoding. `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. */ char *tcbsdecode(const char *ptr, int size, int *sp); /* Compress a serial object with Deflate encoding. `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. */ char *tcdeflate(const char *ptr, int size, int *sp); /* Decompress a serial object compressed with Deflate encoding. `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. */ char *tcinflate(const char *ptr, int size, int *sp); /* Compress a serial object with GZIP encoding. `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. */ char *tcgzipencode(const char *ptr, int size, int *sp); /* Decompress a serial object compressed with GZIP encoding. `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. */ char *tcgzipdecode(const char *ptr, int size, int *sp); /* Get the CRC32 checksum of a serial object. `ptr' specifies the pointer to the region. `size' specifies the size of the region. The return value is the CRC32 checksum of the object. */ unsigned int tcgetcrc(const char *ptr, int size); /* Compress a serial object with BZIP2 encoding. `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. */ char *tcbzipencode(const char *ptr, int size, int *sp); /* Decompress a serial object compressed with BZIP2 encoding. `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. */ char *tcbzipdecode(const char *ptr, int size, int *sp); /* Encode an array of nonnegative integers with BER encoding. `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. */ char *tcberencode(const unsigned int *ary, int anum, int *sp); /* Decode a serial object encoded with BER encoding. `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. */ unsigned int *tcberdecode(const char *ptr, int size, int *np); /* Escape meta characters in a string with the entity references of XML. `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. */ char *tcxmlescape(const char *str); /* Unescape entity references in a string of XML. `str' specifies the string. The return value is the unescaped string. This function restores 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. */ char *tcxmlunescape(const char *str); /************************************************************************************************* * encoding utilities (for experts) *************************************************************************************************/ /* Encode a map object into a string in the x-www-form-urlencoded format. `params' specifies a map object of parameters. 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. */ char *tcwwwformencode(const TCMAP *params); /* Decode a query string in the x-www-form-urlencoded format. `str' specifies the query string. `params' specifies a map object into which the result parameters are stored. */ void tcwwwformdecode(const char *str, TCMAP *params); /* Decode a data region in the x-www-form-urlencoded or multipart-form-data format. `ptr' specifies the pointer to the data region. `size' specifies the size of the data region. `type' specifies the value of the content-type header. If it is `NULL', the type is specified as x-www-form-urlencoded. `params' specifies a map object into which the result parameters are stored. */ void tcwwwformdecode2(const void *ptr, int size, const char *type, TCMAP *params); /* Split an XML string into tags and text sections. `str' specifies the string. The return value is the list object whose elements are strings of tags or text sections. 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. Because this function does not check validation, it can handle also HTML and SGML. */ TCLIST *tcxmlbreak(const char *str); /* Get the map of attributes of an XML tag. `str' specifies the pointer to the region of a tag string. The return value is the map object containing attribute names and their values which are unescaped. You can get the name of the tag with the key of an empty string. 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. */ TCMAP *tcxmlattrs(const char *str); /* Escape meta characters in a string with backslash escaping of the C language. `str' specifies the string. The return value is the pointer to the escaped 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. */ char *tccstrescape(const char *str); /* Unescape a string escaped by backslash escaping of the C language. `str' specifies the string. The return value is the unescaped 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. */ char *tccstrunescape(const char *str); /* Escape meta characters in a string with backslash escaping of JSON. `str' specifies the string. The return value is the pointer to the escaped 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. */ char *tcjsonescape(const char *str); /* Unescape a string escaped by backslash escaping of JSON. `str' specifies the string. The return value is the unescaped 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. */ char *tcjsonunescape(const char *str); /************************************************************************************************* * template serializer *************************************************************************************************/ typedef struct { /* type of structure for a template */ TCLIST *elems; /* elements separated by the separators */ char *begsep; /* beginning separator */ char *endsep; /* ending separator */ TCMAP *conf; /* configuration variables */ } TCTMPL; /* Create a template object. The return value is the new template object. */ TCTMPL *tctmplnew(void); /* Delete a template object. `tmpl' specifies the template object. */ void tctmpldel(TCTMPL *tmpl); /* Set the separator strings of a template object. `tmpl' specifies the template object. `begsep' specifies the beginning separator string. By default, it is "[%". `endsep' specifies the ending separator string. By default, it is "%]". */ void tctmplsetsep(TCTMPL *tmpl, const char *begsep, const char *endsep); /* Load a template string into a template object. `tmpl' specifies the template object. `str' specifies the template string. Directives between "[%" and "%]" can be included in the template string. If the variable name is specified in the directive, it is expanded as the value of the variable. "." is used in order to access a record of a hash variable. For example, "[% foo.bar.baz %]" is expanded as the value of the record whose key is "baz" in the hash variable of the record whose key is "bar" in the hash variable whose name is "foo". Moreover, control flow directives are also supported. "[% IF ... %]", "[% ELSE %]", and "[% END %]" are conditional directives. "[% FOREACH ... %]" and "[% END %]" are iterator directives for a list object. "[% SET ... %]" is a session variable setting directive. "[% CONF ... %]" is a configuration directive. If the ending separator of a directive is leaded by "\", the next linefeed character is ignored. Variable expansion directive needs the parameter for the variable name. The optional parameter "DEF" trailed by a string specifies the default value. The optional parameter "ENC" trailed by a string specifies the encoding format. "URL" for the URL escape encoding, "XML" for the XML escape encoding, "CSTR" for C-string escape encoding, and "JSON" for JSON escape encoding are supported. The conditional directive needs the parameter for the variable name. If the variable exists, the block to the correspondent ending directive is evaluated, else, the block is ignored. The optional parameter "EQ" trailed by a string specifies the string full matching test. The optional parameter "INC" trailed by a string specifies the string including matching test. The optional parameter "PRT" indicates the printable test. The optional parameter "RX" trailed by a string specifies the regular expression matching test. The optional parameter "NOT" inverts the logical determination. The iterator directive needs the parameter for the variable name of a list object. The block to the correspondent ending directive is evaluated for each element of the list. The optional parameter specifies the local variable name of each element. The session variable setting directive needs the parameters for the variable name and its value. The configuration directive needs the parameters for the variable name and its value. */ void tctmplload(TCTMPL *tmpl, const char *str); /* Load a template string from a file into a template object. `tmpl' specifies the template object. `path' specifies the input file. If successful, the return value is true, else, it is false. */ bool tctmplload2(TCTMPL *tmpl, const char *path); /* Serialize the template string of a template object. `tmpl' specifies the template object. `vars' specifies the variables to be applied into the template. The return value is the dumped template 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. */ char *tctmpldump(TCTMPL *tmpl, const TCMAP *vars); /* Get the value of a configuration variable of a template object. `tmpl' specifies the template object. `name' specifies the name of the configuration variable. The return value is the string value of the configuration variable or `NULL' if it is not defined. */ const char *tctmplconf(TCTMPL *tmpl, const char *name); /* Store a list object into a list object with the type information. `list' specifies the container list object. `obj' specifies the list object to be stored. */ void tclistpushlist(TCLIST *list, const TCLIST *obj); /* Store a map object into a list object with the type information. `list' specifies the container list object. `obj' specifies the map object to be stored. */ void tclistpushmap(TCLIST *list, const TCMAP *obj); /* Store a list object into a map object with the type information. `map' specifies the container map object. `kstr' specifies the string of the key. `obj' specifies the list object to be stored. */ void tcmapputlist(TCMAP *map, const char *kstr, const TCLIST *obj); /* Store a map object into a map object with the type information. `map' specifies the container map object. `kstr' specifies the string of the key. `obj' specifies the map object to be stored. */ void tcmapputmap(TCMAP *map, const char *kstr, const TCMAP *obj); /************************************************************************************************* * pointer list *************************************************************************************************/ typedef struct { /* type of structure for a pointer list */ void **array; /* array of pointers */ int anum; /* number of the elements of the array */ int start; /* start index of used elements */ int num; /* number of used elements */ } TCPTRLIST; /* Create a pointer list object. The return value is the new pointer list object. */ TCPTRLIST *tcptrlistnew(void); /* Create a pointer list object with expecting the number of elements. `anum' specifies the number of elements expected to be stored in the list. The return value is the new pointer list object. */ TCPTRLIST *tcptrlistnew2(int anum); /* Copy a pointer list object. `ptrlist' specifies the pointer list object. The return value is the new pointer list object equivalent to the specified object. */ TCPTRLIST *tcptrlistdup(const TCPTRLIST *ptrlist); /* Delete a pointer list object. `ptrlist' specifies the pointer list object. Note that the deleted object and its derivatives can not be used anymore. */ void tcptrlistdel(TCPTRLIST *ptrlist); /* Get the number of elements of a pointer list object. `ptrlist' specifies the pointer list object. The return value is the number of elements of the list. */ int tcptrlistnum(const TCPTRLIST *ptrlist); /* Get the pointer to the region of an element of a pointer list object. `ptrlist' specifies the pointer list object. `index' specifies the index of the element. The return value is the pointer to the region of the value. If `index' is equal to or more than the number of elements, the return value is `NULL'. */ void *tcptrlistval(const TCPTRLIST *ptrlist, int index); /* Add an element at the end of a pointer list object. `ptrlist' specifies the pointer list object. `ptr' specifies the pointer to the region of the new element. */ void tcptrlistpush(TCPTRLIST *ptrlist, void *ptr); /* Remove an element of the end of a pointer list object. `ptrlist' specifies the pointer list object. The return value is the pointer to the region of the removed element. If the list is empty, the return value is `NULL'. */ void *tcptrlistpop(TCPTRLIST *ptrlist); /* Add an element at the top of a pointer list object. `ptrlist' specifies the pointer list object. `ptr' specifies the pointer to the region of the new element. */ void tcptrlistunshift(TCPTRLIST *ptrlist, void *ptr); /* Remove an element of the top of a pointer list object. `ptrlist' specifies the pointer list object. The return value is the pointer to the region of the removed element. If the list is empty, the return value is `NULL'. */ void *tcptrlistshift(TCPTRLIST *ptrlist); /* Add an element at the specified location of a pointer list object. `ptrlist' specifies the pointer list object. `index' specifies the index of the new element. `ptr' specifies the pointer to the region of the new element. If `index' is equal to or more than the number of elements, this function has no effect. */ void tcptrlistinsert(TCPTRLIST *ptrlist, int index, void *ptr); /* Remove an element at the specified location of a pointer list object. `ptrlist' specifies the pointer list object. `index' specifies the index of the element to be removed. The return value is the pointer to the region of the removed element. If `index' is equal to or more than the number of elements, no element is removed and the return value is `NULL'. */ void *tcptrlistremove(TCPTRLIST *ptrlist, int index); /* Overwrite an element at the specified location of a pointer list object. `ptrlist' specifies the pointer list object. `index' specifies the index of the element to be overwritten. `ptr' specifies the pointer to the region of the new content. If `index' is equal to or more than the number of elements, this function has no effect. */ void tcptrlistover(TCPTRLIST *ptrlist, int index, void *ptr); /* Clear a pointer list object. `ptrlist' specifies the pointer list object. All elements are removed. */ void tcptrlistclear(TCPTRLIST *ptrlist); /************************************************************************************************* * bit operation utilities *************************************************************************************************/ typedef struct { /* type of structure for a bit stream object */ uint8_t *sp; /* start pointer */ uint8_t *cp; /* current pointer */ int idx; /* bit index */ int size; /* size of used region */ } TCBITSTRM; typedef unsigned char TCBITMAP; /* type of a bit map object */ /* Create a bitmap object. */ #define TCBITMAPNEW(TC_num) \ tccalloc(((TC_num) >> 3) + 1, 1); /* Delete a bitmap object */ #define TCBITMAPDEL(TC_bitmap) \ do { \ tcfree((TC_bitmap)); \ } while(false); /* Turn on a field of a bitmap object. */ #define TCBITMAPON(TC_bitmap, TC_idx) \ do { \ (TC_bitmap)[(TC_idx)>>3] |= 0x1 << ((TC_idx) & 0x7); \ } while(false); /* Turn off a field of a bitmap object. */ #define TCBITMAPOFF(TC_bitmap, TC_idx) \ do { \ (TC_bitmap)[(TC_idx)>>3] &= ~(0x1 << ((TC_idx) & 0x7)); \ } while(false); /* Check a field of a bitmap object. */ #define TCBITMAPCHECK(TC_bitmap, TC_idx) \ ((TC_bitmap)[(TC_idx)>>3] & 0x1 << ((TC_idx) & 0x7)) /* Initialize a bit stream object as writer. */ #define TCBITSTRMINITW(TC_bitstrm, TC_ptr) \ do { \ (TC_bitstrm).sp = (uint8_t *)(TC_ptr); \ (TC_bitstrm).cp = (TC_bitstrm).sp; \ *(TC_bitstrm).cp = 0; \ (TC_bitstrm).idx = 3; \ (TC_bitstrm).size = 1; \ } while(false); /* Concatenate a bit to a bit stream object. */ #define TCBITSTRMCAT(TC_bitstrm, sign) \ do { \ if((TC_bitstrm).idx >= 8){ \ *(++(TC_bitstrm).cp) = 0; \ (TC_bitstrm).idx = 0; \ (TC_bitstrm).size++; \ } \ *(TC_bitstrm).cp |= (sign << (TC_bitstrm).idx); \ (TC_bitstrm).idx++; \ } while(false); /* Set the end mark to a bit stream object. */ #define TCBITSTRMSETEND(TC_bitstrm) \ do { \ if((TC_bitstrm).idx >= 8){ \ *(++(TC_bitstrm).cp) = 0; \ (TC_bitstrm).idx = 0; \ (TC_bitstrm).size++; \ } \ *(TC_bitstrm).sp |= (TC_bitstrm).idx & 7; \ } while(false); /* Get the size of the used region of a bit stream object. */ #define TCBITSTRMSIZE(TC_bitstrm) \ ((TC_bitstrm).size) /* Initialize a bit stream object as reader. */ #define TCBITSTRMINITR(TC_bitstrm, TC_ptr, TC_size) \ do { \ (TC_bitstrm).sp = (uint8_t *)(TC_ptr); \ (TC_bitstrm).cp = (TC_bitstrm).sp; \ (TC_bitstrm).idx = 3; \ (TC_bitstrm).size = (TC_size); \ } while(false); /* Read a bit from a bit stream object. */ #define TCBITSTRMREAD(TC_bitstrm, TC_sign) \ do { \ if((TC_bitstrm).idx >= 8){ \ (TC_bitstrm).cp++; \ (TC_bitstrm).idx = 0; \ } \ (TC_sign) = (*((TC_bitstrm).cp) & (1 << (TC_bitstrm).idx)) > 0; \ (TC_bitstrm).idx++; \ } while(false); /* Get the number of bits of a bit stream object. */ #define TCBITSTRMNUM(TC_bitstrm) \ ((((TC_bitstrm).size - 1) << 3) + (*(TC_bitstrm).sp & 7) - 3) /************************************************************************************************* * features for experts *************************************************************************************************/ #include #define _TC_VERSION "1.4.48" #define _TC_LIBVER 911 #define _TC_FORMATVER "1.0" enum { /* enumeration for error codes */ TCESUCCESS, /* success */ TCETHREAD, /* threading error */ TCEINVALID, /* invalid operation */ TCENOFILE, /* file not found */ TCENOPERM, /* no permission */ TCEMETA, /* invalid meta data */ TCERHEAD, /* invalid record header */ TCEOPEN, /* open error */ TCECLOSE, /* close error */ TCETRUNC, /* trunc error */ TCESYNC, /* sync error */ TCESTAT, /* stat error */ TCESEEK, /* seek error */ TCEREAD, /* read error */ TCEWRITE, /* write error */ TCEMMAP, /* mmap error */ TCELOCK, /* lock error */ TCEUNLINK, /* unlink error */ TCERENAME, /* rename error */ TCEMKDIR, /* mkdir error */ TCERMDIR, /* rmdir error */ TCEKEEP, /* existing record */ TCENOREC, /* no record found */ TCEMISC = 9999 /* miscellaneous error */ }; enum { /* enumeration for database type */ TCDBTHASH, /* hash table */ TCDBTBTREE, /* B+ tree */ TCDBTFIXED, /* fixed-length */ TCDBTTABLE /* table */ }; /* Get the message string corresponding to an error code. `ecode' specifies the error code. The return value is the message string of the error code. */ const char *tcerrmsg(int ecode); /* Show error message on the standard error output and exit. `message' specifies an error message. This function does not return. */ void *tcmyfatal(const char *message); /* Allocate a large nullified region. `size' specifies the size of the region. The return value is the pointer to the allocated nullified region. This function handles failure of memory allocation implicitly. The region of the return value should be released with the function `tczerounmap' when it is no longer in use. */ void *tczeromap(uint64_t size); /* Free a large nullfied region. `ptr' specifies the pointer to the region. */ void tczerounmap(void *ptr); /* Lock the global mutex object. If successful, the return value is true, else, it is false. */ bool tcglobalmutexlock(void); /* Lock the global mutex object by shared locking. If successful, the return value is true, else, it is false. */ bool tcglobalmutexlockshared(void); /* Unlock the global mutex object. If successful, the return value is true, else, it is false. */ bool tcglobalmutexunlock(void); /* Lock the absolute path of a file. `path' specifies the path of the file. If successful, the return value is true, else, it is false. */ bool tcpathlock(const char *path); /* Unock the absolute path of a file. `path' specifies the path of the file. If successful, the return value is true, else, it is false. */ bool tcpathunlock(const char *path); /* Convert an integer to the string as binary numbers. `num' specifies the integer. `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 65 bytes. `col' specifies the number of columns. If it is not more than 0, it depends on the integer. `fc' specifies the filling character. The return value is the length of the result string. */ int tcnumtostrbin(uint64_t num, char *buf, int col, int fc); /* Compare two keys by lexical order. `aptr' specifies the pointer to the region of one key. `asiz' specifies the size of the region of one key. `bptr' specifies the pointer to the region of the other key. `bsiz' specifies the size of the region of the other key. `op' is ignored. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ int tccmplexical(const char *aptr, int asiz, const char *bptr, int bsiz, void *op); /* Compare two keys as decimal strings of real numbers. `aptr' specifies the pointer to the region of one key. `asiz' specifies the size of the region of one key. `bptr' specifies the pointer to the region of the other key. `bsiz' specifies the size of the region of the other key. `op' is ignored. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ int tccmpdecimal(const char *aptr, int asiz, const char *bptr, int bsiz, void *op); /* Compare two keys as 32-bit integers in the native byte order. `aptr' specifies the pointer to the region of one key. `asiz' specifies the size of the region of one key. `bptr' specifies the pointer to the region of the other key. `bsiz' specifies the size of the region of the other key. `op' is ignored. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ int tccmpint32(const char *aptr, int asiz, const char *bptr, int bsiz, void *op); /* Compare two keys as 64-bit integers in the native byte order. `aptr' specifies the pointer to the region of one key. `asiz' specifies the size of the region of one key. `bptr' specifies the pointer to the region of the other key. `bsiz' specifies the size of the region of the other key. `op' is ignored. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ int tccmpint64(const char *aptr, int asiz, const char *bptr, int bsiz, void *op); /* Encode a serial object with BWT encoding. `ptr' specifies the pointer to the region. `size' specifies the size of the region. `idxp' specifies the pointer to the variable into which the index of the original string in the rotation array is assigned. The return value is the pointer to the result 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. 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. */ char *tcbwtencode(const char *ptr, int size, int *idxp); /* Decode a serial object encoded with BWT encoding. `ptr' specifies the pointer to the region. `size' specifies the size of the region. `idx' specifies the index of the original string in the rotation array is assigned. The return value is the pointer to the result 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. 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. */ char *tcbwtdecode(const char *ptr, int size, int idx); /* Get the binary logarithm of an integer. `num' specifies an integer. The return value is the binary logarithm. */ long tclog2l(long num); /* Get the binary logarithm of a real number. `num' specifies a real number. The return value is the binary logarithm. */ double tclog2d(double num); /* Get the aligned offset of a file offset. `off' specifies the file offset. The return value is the aligned offset. */ uint64_t tcpagealign(uint64_t off); /* Print debug information with a formatted string as with `printf'. */ #if __STDC_VERSION__ >= 199901L #define TCDPRINTF(...) \ do { \ fprintf(stderr, "%s:%d:%s: ", __FILE__, __LINE__, __func__); \ fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, "\n"); \ } while(false); #else #define TCDPRINTF(TC_str) \ do { \ fprintf(stderr, "%s:%d:%s: %s\n", __FILE__, __LINE__, __func__, TC_str); \ } while(false); #endif /* Print hexadecimal pattern of a binary region. */ #define TCPRINTHEX(TC_ptr, TC_size) \ do { \ for(int TC_i = 0; TC_i < (TC_size); TC_i++){ \ if(TC_i > 0) putchar(' '); \ printf("%02X", ((unsigned char *)(TC_ptr))[TC_i]); \ } \ putchar('\n'); \ } while(false); /* Print an extensible string object. */ #define TCPRINTXSTR(TC_xstr) \ do { \ fwrite(tcxstrptr((TC_xstr)), tcxstrsize((TC_xstr)), 1, stdout); \ putchar('\n'); \ } while(false); /* Print all elements of a list object. */ #define TCPRINTLIST(TC_list) \ do { \ for(int TC_i = 0; TC_i < tclistnum((TC_list)); TC_i++){ \ int TC_size; \ const char *TC_ptr = tclistval((TC_list), TC_i, &TC_size); \ printf("%p\t", (void *)(TC_list)); \ fwrite(TC_ptr, TC_size, 1, stdout); \ putchar('\n'); \ } \ putchar('\n'); \ } while(false); /* Print all records of a list object. */ #define TCPRINTMAP(TC_map) \ do { \ TCLIST *TC_keys = tcmapkeys((TC_map)); \ for(int TC_i = 0; TC_i < tclistnum(TC_keys); TC_i++){ \ int TC_ksiz; \ const char *TC_kbuf = tclistval(TC_keys, TC_i, &TC_ksiz); \ int TC_vsiz; \ const char *TC_vbuf = tcmapget((TC_map), TC_kbuf, TC_ksiz, &TC_vsiz); \ printf("%p\t", (void *)(TC_map)); \ fwrite(TC_kbuf, TC_ksiz, 1, stdout); \ putchar('\t'); \ fwrite(TC_vbuf, TC_vsiz, 1, stdout); \ putchar('\n'); \ } \ putchar('\n'); \ tclistdel(TC_keys); \ } while(false); /* Alias of `tcmalloc'. */ #if defined(_MYFASTEST) #define TCMALLOC(TC_res, TC_size) \ do { \ (TC_res) = MYMALLOC(TC_size); \ } while(false) #else #define TCMALLOC(TC_res, TC_size) \ do { \ if(!((TC_res) = MYMALLOC(TC_size))) tcmyfatal("out of memory"); \ } while(false) #endif /* Alias of `tccalloc'. */ #if defined(_MYFASTEST) #define TCCALLOC(TC_res, TC_nmemb, TC_size) \ do { \ (TC_res) = MYCALLOC((TC_nmemb), (TC_size)); \ } while(false) #else #define TCCALLOC(TC_res, TC_nmemb, TC_size) \ do { \ if(!((TC_res) = MYCALLOC((TC_nmemb), (TC_size)))) tcmyfatal("out of memory"); \ } while(false) #endif /* Alias of `tcrealloc'. */ #if defined(_MYFASTEST) #define TCREALLOC(TC_res, TC_ptr, TC_size) \ do { \ (TC_res) = MYREALLOC((TC_ptr), (TC_size)); \ } while(false) #else #define TCREALLOC(TC_res, TC_ptr, TC_size) \ do { \ if(!((TC_res) = MYREALLOC((TC_ptr), (TC_size)))) tcmyfatal("out of memory"); \ } while(false) #endif /* Alias of `tcmemdup'. */ #define TCMEMDUP(TC_res, TC_ptr, TC_size) \ do { \ TCMALLOC((TC_res), (TC_size) + 1); \ memcpy((TC_res), (TC_ptr), (TC_size)); \ (TC_res)[TC_size] = '\0'; \ } while(false) /* Alias of `tcfree'. */ #define TCFREE(TC_ptr) \ do { \ MYFREE(TC_ptr); \ } while(false) /* Get the alignment of a variable type. */ #define TCALIGNOF(TC_a) \ ((int)offsetof(struct { int8_t TC_top; TC_a TC_bot; }, TC_bot)) /* Get the size of padding bytes for pointer alignment. */ typedef union { int32_t i; int64_t l; double d; void *p; TCCMP f; } tcgeneric_t; #define TCALIGNPAD(TC_hsiz) \ (((TC_hsiz | ~-TCALIGNOF(tcgeneric_t)) + 1) - TC_hsiz) /* Alias of `tcxstrcat'. */ #define TCXSTRCAT(TC_xstr, TC_ptr, TC_size) \ do { \ int TC_mysize = (TC_size); \ int TC_nsize = (TC_xstr)->size + TC_mysize + 1; \ if((TC_xstr)->asize < TC_nsize){ \ while((TC_xstr)->asize < TC_nsize){ \ (TC_xstr)->asize *= 2; \ if((TC_xstr)->asize < TC_nsize) (TC_xstr)->asize = TC_nsize; \ } \ TCREALLOC((TC_xstr)->ptr, (TC_xstr)->ptr, (TC_xstr)->asize); \ } \ memcpy((TC_xstr)->ptr + (TC_xstr)->size, (TC_ptr), TC_mysize); \ (TC_xstr)->size += TC_mysize; \ (TC_xstr)->ptr[(TC_xstr)->size] = '\0'; \ } while(false) /* Alias of `tcxstrptr'. */ #define TCXSTRPTR(TC_xstr) \ ((TC_xstr)->ptr) /* Alias of `tcxstrsize'. */ #define TCXSTRSIZE(TC_xstr) \ ((TC_xstr)->size) /* Alias of `tclistnum'. */ #define TCLISTNUM(TC_list) \ ((TC_list)->num) /* Alias of `tclistval' but not checking size. */ #define TCLISTVAL(TC_ptr, TC_list, TC_index, TC_size) \ do { \ (TC_ptr) = (TC_list)->array[(TC_index)+(TC_list)->start].ptr; \ (TC_size) = (TC_list)->array[(TC_index)+(TC_list)->start].size; \ } while(false) /* Alias of `tclistval' but not checking size and not using the third parameter. */ #define TCLISTVALPTR(TC_list, TC_index) \ ((void *)((TC_list)->array[(TC_index)+(TC_list)->start].ptr)) /* Alias of `tclistval' but not checking size and returning the size of the value. */ #define TCLISTVALSIZ(TC_list, TC_index) \ ((TC_list)->array[(TC_index)+(TC_list)->start].size) /* Alias of `tclistpush'. */ #define TCLISTPUSH(TC_list, TC_ptr, TC_size) \ do { \ int TC_mysize = (TC_size); \ int TC_index = (TC_list)->start + (TC_list)->num; \ if(TC_index >= (TC_list)->anum){ \ (TC_list)->anum += (TC_list)->num + 1; \ TCREALLOC((TC_list)->array, (TC_list)->array, \ (TC_list)->anum * sizeof((TC_list)->array[0])); \ } \ TCLISTDATUM *array = (TC_list)->array; \ TCMALLOC(array[TC_index].ptr, TC_mysize + 1); \ memcpy(array[TC_index].ptr, (TC_ptr), TC_mysize); \ array[TC_index].ptr[TC_mysize] = '\0'; \ array[TC_index].size = TC_mysize; \ (TC_list)->num++; \ } while(false) /* Alias of `tclistinsert'. */ #define TCLISTINSERT(TC_list, TC_index, TC_ptr, TC_size) \ do { \ int TC_myindex = (TC_index); \ TC_myindex += (TC_list)->start; \ if((TC_list)->start + (TC_list)->num >= (TC_list)->anum){ \ (TC_list)->anum += (TC_list)->num + 1; \ TCREALLOC((TC_list)->array, (TC_list)->array, \ (TC_list)->anum * sizeof((TC_list)->array[0])); \ } \ memmove((TC_list)->array + TC_myindex + 1, (TC_list)->array + TC_myindex, \ sizeof((TC_list)->array[0]) * ((TC_list)->start + (TC_list)->num - TC_myindex)); \ TCMALLOC((TC_list)->array[TC_myindex].ptr, (TC_size) + 1); \ memcpy((TC_list)->array[TC_myindex].ptr, (TC_ptr), (TC_size)); \ (TC_list)->array[TC_myindex].ptr[(TC_size)] = '\0'; \ (TC_list)->array[TC_myindex].size = (TC_size); \ (TC_list)->num++; \ } while(false) /* Truncate a list object. */ #define TCLISTTRUNC(TC_list, TC_num) \ do { \ while((TC_list)->num > (TC_num)){ \ TCFREE((TC_list)->array[--(TC_list)->num].ptr); \ } \ } while(false) /* Alias of `tcmaprnum'. */ #define TCMAPRNUM(TC_map) \ ((TC_map)->rnum) /* Alias of `tcptrlistnum'. */ #define TCPTRLISTNUM(TC_ptrlist) \ ((TC_ptrlist)->num) /* Alias of `tcptrlistval'. */ #define TCPTRLISTVAL(TC_ptrlist, TC_index) \ ((void *)((TC_ptrlist)->array[(TC_index)+(TC_ptrlist)->start])) /* Alias of `tcptrlistpush'. */ #define TCPTRLISTPUSH(TC_ptrlist, TC_ptr) \ do { \ int TC_index = (TC_ptrlist)->start + (TC_ptrlist)->num; \ if(TC_index >= (TC_ptrlist)->anum){ \ (TC_ptrlist)->anum += (TC_ptrlist)->num + 1; \ TCREALLOC((TC_ptrlist)->array, (TC_ptrlist)->array, \ (TC_ptrlist)->anum * sizeof((TC_ptrlist)->array[0])); \ } \ (TC_ptrlist)->array[TC_index] = (TC_ptr); \ (TC_ptrlist)->num++; \ } while(false) /* Alias of `tcptrlistinsert'. */ #define TCPTRLISTINSERT(TC_ptrlist, TC_index, TC_ptr) \ do { \ int TC_myindex = (TC_index); \ TC_myindex += (TC_ptrlist)->start; \ if((TC_ptrlist)->start + (TC_ptrlist)->num >= (TC_ptrlist)->anum){ \ (TC_ptrlist)->anum += (TC_ptrlist)->num + 1; \ TCREALLOC((TC_ptrlist)->array, (TC_ptrlist)->array, \ (TC_ptrlist)->anum * sizeof((TC_ptrlist)->array[0])); \ } \ memmove((TC_ptrlist)->array + TC_myindex + 1, (TC_ptrlist)->array + TC_myindex, \ sizeof((TC_ptrlist)->array[0]) * ((TC_ptrlist)->start + \ (TC_ptrlist)->num - TC_myindex)); \ (TC_ptrlist)->array[TC_myindex] = (TC_ptr); \ (TC_ptrlist)->num++; \ } while(false) /* Truncate a pointer list object. */ #define TCPTRLISTTRUNC(TC_ptrlist, TC_num) \ do { \ (TC_ptrlist)->num = (TC_num); \ } while(false) /* tricks for backward compatibility */ #define BDBCMP TCCMP #define tcbdbrange3 tcbdbfwmkeys2 #define tcbdbcmplexical tccmplexical #define tcbdbcmpdecimal tccmpdecimal #define tcbdbcmpint32 tccmpint32 #define tcbdbcmpint64 tccmpint64 #define tctdbqryprocout tctdbqrysearchout #define tctdbqrysetmax(TC_tdb, TC_max) \ tctdbqrysetlimit((TC_tdb), (TC_max), 0) __TCUTIL_CLINKAGEEND #endif /* duplication check */ /* END OF FILE */ tokyocabinet-1.4.48/configure.in0000644000175000017500000002472312013573270015626 0ustar mikiomikio# Source of configuration for Tokyo Cabinet #================================================================ # Generic Settings #================================================================ # Package name AC_INIT(tokyocabinet, 1.4.48) # Package information MYLIBVER=9 MYLIBREV=11 MYFORMATVER="1.0" # Targets MYHEADERFILES="tcutil.h tchdb.h tcbdb.h tcfdb.h tctdb.h tcadb.h" MYLIBRARYFILES="libtokyocabinet.a" MYLIBOBJFILES="tcutil.o tchdb.o tcbdb.o tcfdb.o tctdb.o tcadb.o myconf.o md5.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" MYPCFILES="tokyocabinet.pc" # Building flags 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 test -n "$CFLAGS" && MYCFLAGS="$CFLAGS $MYCFLAGS" test -n "$CPPFLAGS" && MYCPPFLAGS="$CPPFLAGS $MYCPPFLAGS" test -n "$LDFLAGS" && MYLDFLAGS="$LDFLAGS $MYLDFLAGS" # 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(tokyocabinet, main, AC_MSG_WARN([old version of Tokyo Cabinet was detected])) # 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 libtokyocabinet.$MYLIBVER.$MYLIBREV.0.dylib" MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.$MYLIBVER.dylib" MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.dylib" MYLDLIBPATHENV="DYLD_LIBRARY_PATH" else MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so.$MYLIBVER.$MYLIBREV.0" MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so.$MYLIBVER" MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.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 tokyocabinet.pc) # Messages printf '#================================================================\n' printf '# Ready to make.\n' printf '#================================================================\n' # END OF FILE tokyocabinet-1.4.48/tctmttest.c0000644000175000017500000013513012013574446015515 0ustar mikiomikio/************************************************************************************************* * The test cases of the table database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" #define RECBUFSIZ 48 // buffer for records typedef struct { // type of structure for write thread TCTDB *tdb; int rnum; bool rnd; int id; } TARGWRITE; typedef struct { // type of structure for read thread TCTDB *tdb; int rnum; bool rnd; int id; } TARGREAD; typedef struct { // type of structure for remove thread TCTDB *tdb; int rnum; bool rnd; int id; } TARGREMOVE; typedef struct { // type of structure for wicked thread TCTDB *tdb; int rnum; int id; } TARGWICKED; typedef struct { // type of structure for typical thread TCTDB *tdb; int rnum; int rratio; int id; } TARGTYPICAL; /* global variables */ const char *g_progname; // program name unsigned int g_randseed; // random seed int g_dbgfd; // debugging output /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void iprintf(const char *format, ...); static void iputchar(int c); static void eprint(TCTDB *tdb, int line, const char *func); static void sysprint(void); static int myrand(int range); static int myrandnd(int range); static int runwrite(int argc, char **argv); static int runread(int argc, char **argv); static int runremove(int argc, char **argv); static int runwicked(int argc, char **argv); static int runtypical(int argc, char **argv); static int procwrite(const char *path, int tnum, int rnum, int bnum, int apow, int fpow, int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int iflags, int omode, bool rnd); static int procread(const char *path, int tnum, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int omode, bool rnd); static int procremove(const char *path, int tnum, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int omode, bool rnd); static int procwicked(const char *path, int tnum, int rnum, int opts, int omode); static int proctypical(const char *path, int tnum, int rnum, int bnum, int apow, int fpow, int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int omode, int rratio); static void *threadwrite(void *targ); static void *threadread(void *targ); static void *threadremove(void *targ); static void *threadwicked(void *targ); static void *threadtypical(void *targ); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; const char *ebuf = getenv("TCRNDSEED"); g_randseed = ebuf ? tcatoix(ebuf) : tctime() * 1000; srand(g_randseed); ebuf = getenv("TCDBGFD"); g_dbgfd = ebuf ? tcatoix(ebuf) : UINT16_MAX; if(argc < 2) usage(); int 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], "remove")){ rv = runremove(argc, argv); } else if(!strcmp(argv[1], "wicked")){ rv = runwicked(argc, argv); } else if(!strcmp(argv[1], "typical")){ rv = runtypical(argc, argv); } else { usage(); } if(rv != 0){ printf("FAILED: TCRNDSEED=%u PID=%d", g_randseed, (int)getpid()); for(int i = 0; i < argc; i++){ printf(" %s", argv[i]); } printf("\n\n"); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: test cases of the table database API of Tokyo Cabinet\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s 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]]]\n", g_progname); fprintf(stderr, " %s read [-rc num] [-lc num] [-nc num] [-xm num] [-df num] [-nl|-nb] [-rnd]" " path tnum\n", g_progname); fprintf(stderr, " %s remove [-rc num] [-lc num] [-nc num] [-xm num] [-df num]" " [-nl|-nb] [-rnd] path tnum\n", g_progname); fprintf(stderr, " %s wicked [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path tnum rnum\n", g_progname); fprintf(stderr, " %s 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]]]\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); } /* print a character and flush the buffer */ static void iputchar(int c){ putchar(c); fflush(stdout); } /* print error message of table database */ static void eprint(TCTDB *tdb, int line, const char *func){ const char *path = tctdbpath(tdb); int ecode = tctdbecode(tdb); fprintf(stderr, "%s: %s: %d: %s: error: %d: %s\n", g_progname, path ? path : "-", line, func, ecode, tctdberrmsg(ecode)); } /* print system information */ static void sysprint(void){ TCMAP *info = tcsysinfo(); if(info){ tcmapiterinit(info); const char *kbuf; while((kbuf = tcmapiternext2(info)) != NULL){ iprintf("sys_%s: %s\n", kbuf, tcmapiterval2(kbuf)); } tcmapdel(info); } } /* get a random number */ static int myrand(int range){ if(range < 2) return 0; int high = (unsigned int)rand() >> 4; int low = range * (rand() / (RAND_MAX + 1.0)); low &= (unsigned int)INT_MAX >> 4; return (high + low) % range; } /* get a random number based on normal distribution */ static int myrandnd(int range){ int num = (int)tcdrandnd(range >> 1, range / 10); return (num < 0 || num >= range) ? 0 : num; } /* parse arguments of write command */ static int runwrite(int argc, char **argv){ char *path = NULL; char *tstr = NULL; char *rstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; int opts = 0; int rcnum = 0; int lcnum = 0; int ncnum = 0; int xmsiz = -1; int dfunit = 0; int iflags = 0; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tl")){ opts |= TDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= TDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= TDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= TDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= TDBTEXCODEC; } else if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-lc")){ if(++i >= argc) usage(); lcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nc")){ if(++i >= argc) usage(); ncnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-ip")){ iflags |= 1 << 0; } else if(!strcmp(argv[i], "-is")){ iflags |= 1 << 1; } else if(!strcmp(argv[i], "-in")){ iflags |= 1 << 2; } else if(!strcmp(argv[i], "-it")){ iflags |= 1 << 3; } else if(!strcmp(argv[i], "-if")){ iflags |= 1 << 4; } else if(!strcmp(argv[i], "-ix")){ iflags |= 1 << 5; } else if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = procwrite(path, tnum, rnum, bnum, apow, fpow, opts, rcnum, lcnum, ncnum, xmsiz, dfunit, iflags, omode, rnd); return rv; } /* parse arguments of read command */ static int runread(int argc, char **argv){ char *path = NULL; char *tstr = NULL; int rcnum = 0; int lcnum = 0; int ncnum = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-lc")){ if(++i >= argc) usage(); lcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nc")){ if(++i >= argc) usage(); ncnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else { usage(); } } if(!path || !tstr) usage(); int tnum = tcatoix(tstr); if(tnum < 1) usage(); int rv = procread(path, tnum, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); return rv; } /* parse arguments of remove command */ static int runremove(int argc, char **argv){ char *path = NULL; char *tstr = NULL; int rcnum = 0; int lcnum = 0; int ncnum = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-lc")){ if(++i >= argc) usage(); lcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nc")){ if(++i >= argc) usage(); ncnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else { usage(); } } if(!path || !tstr) usage(); int tnum = tcatoix(tstr); if(tnum < 1) usage(); int rv = procremove(path, tnum, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); return rv; } /* parse arguments of wicked command */ static int runwicked(int argc, char **argv){ char *path = NULL; char *tstr = NULL; char *rstr = NULL; int opts = 0; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tl")){ opts |= TDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= TDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= TDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= TDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= TDBTEXCODEC; } else if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!path || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int rv = procwicked(path, tnum, rnum, opts, omode); return rv; } /* parse arguments of typical command */ static int runtypical(int argc, char **argv){ char *path = NULL; char *tstr = NULL; char *rstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; int opts = 0; int rcnum = 0; int lcnum = 0; int ncnum = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; int rratio = -1; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tl")){ opts |= TDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= TDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= TDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= TDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= TDBTEXCODEC; } else if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-lc")){ if(++i >= argc) usage(); lcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nc")){ if(++i >= argc) usage(); ncnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-rr")){ if(++i >= argc) usage(); rratio = tcatoix(argv[i]); } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = proctypical(path, tnum, rnum, bnum, apow, fpow, opts, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rratio); return rv; } /* perform write command */ static int procwrite(const char *path, int tnum, int rnum, int bnum, int apow, int fpow, int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int iflags, int omode, bool rnd){ iprintf("\n seed=%u path=%s tnum=%d rnum=%d bnum=%d apow=%d fpow=%d" " opts=%d rcnum=%d lcnum=%d ncnum=%d xmsiz=%d dfunit=%d iflags=%d" " omode=%d rnd=%d\n\n", g_randseed, path, tnum, rnum, bnum, apow, fpow, opts, rcnum, lcnum, ncnum, xmsiz, dfunit, iflags, omode, rnd); bool err = false; double stime = tctime(); TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(!tctdbsetmutex(tdb)){ eprint(tdb, __LINE__, "tctdbsetmutex"); err = true; } if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(tdb, __LINE__, "tctdbsetcodecfunc"); err = true; } if(!tctdbtune(tdb, bnum, apow, fpow, opts)){ eprint(tdb, __LINE__, "tctdbtune"); err = true; } if(!tctdbsetcache(tdb, rcnum, lcnum, ncnum)){ eprint(tdb, __LINE__, "tctdbsetcache"); err = true; } if(xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)){ eprint(tdb, __LINE__, "tctdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)){ eprint(tdb, __LINE__, "tctdbsetdfunit"); err = true; } if(!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | TDBOTRUNC | omode)){ eprint(tdb, __LINE__, "tctdbopen"); err = true; } if((iflags & (1 << 0)) && !tctdbsetindex(tdb, "", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if((iflags & (1 << 1)) && !tctdbsetindex(tdb, "str", TDBITLEXICAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if((iflags & (1 << 2)) && !tctdbsetindex(tdb, "num", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if((iflags & (1 << 3)) && !tctdbsetindex(tdb, "type", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if((iflags & (1 << 4)) && !tctdbsetindex(tdb, "flag", TDBITTOKEN)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if((iflags & (1 << 5)) && !tctdbsetindex(tdb, "text", TDBITQGRAM)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } TARGWRITE targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].tdb = tdb; targs[0].rnum = rnum; targs[0].rnd = rnd; targs[0].id = 0; if(threadwrite(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].tdb = tdb; targs[i].rnum = rnum; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadwrite, targs + i) != 0){ eprint(tdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(tdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tctdbrnum(tdb)); iprintf("size: %llu\n", (unsigned long long)tctdbfsiz(tdb)); sysprint(); if(!tctdbclose(tdb)){ eprint(tdb, __LINE__, "tctdbclose"); err = true; } tctdbdel(tdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform read command */ static int procread(const char *path, int tnum, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int omode, bool rnd){ iprintf("\n seed=%u path=%s tnum=%d rcnum=%d lcnum=%d ncnum=%d" " xmsiz=%d dfunit=%d omode=%d rnd=%d\n\n", g_randseed, path, tnum, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); bool err = false; double stime = tctime(); TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(!tctdbsetmutex(tdb)){ eprint(tdb, __LINE__, "tctdbsetmutex"); err = true; } if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(tdb, __LINE__, "tctdbsetcodecfunc"); err = true; } if(!tctdbsetcache(tdb, rcnum, lcnum, ncnum)){ eprint(tdb, __LINE__, "tctdbsetcache"); err = true; } if(xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)){ eprint(tdb, __LINE__, "tctdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)){ eprint(tdb, __LINE__, "tctdbsetdfunit"); err = true; } if(!tctdbopen(tdb, path, TDBOREADER | omode)){ eprint(tdb, __LINE__, "tctdbopen"); err = true; } int rnum = tctdbrnum(tdb) / tnum; TARGREAD targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].tdb = tdb; targs[0].rnum = rnum; targs[0].rnd = rnd; targs[0].id = 0; if(threadread(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].tdb = tdb; targs[i].rnum = rnum; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){ eprint(tdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(tdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tctdbrnum(tdb)); iprintf("size: %llu\n", (unsigned long long)tctdbfsiz(tdb)); sysprint(); if(!tctdbclose(tdb)){ eprint(tdb, __LINE__, "tctdbclose"); err = true; } tctdbdel(tdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform remove command */ static int procremove(const char *path, int tnum, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int omode, bool rnd){ iprintf("\n seed=%u path=%s tnum=%d rcnum=%d lcnum=%d ncnum=%d" " xmsiz=%d dfunit=%d omode=%d rnd=%d\n\n", g_randseed, path, tnum, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); bool err = false; double stime = tctime(); TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(!tctdbsetmutex(tdb)){ eprint(tdb, __LINE__, "tctdbsetmutex"); err = true; } if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(tdb, __LINE__, "tctdbsetcodecfunc"); err = true; } if(!tctdbsetcache(tdb, rcnum, lcnum, ncnum)){ eprint(tdb, __LINE__, "tctdbsetcache"); err = true; } if(xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)){ eprint(tdb, __LINE__, "tctdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)){ eprint(tdb, __LINE__, "tctdbsetdfunit"); err = true; } if(!tctdbopen(tdb, path, TDBOWRITER | omode)){ eprint(tdb, __LINE__, "tctdbopen"); err = true; } int rnum = tctdbrnum(tdb) / tnum; TARGREMOVE targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].tdb = tdb; targs[0].rnum = rnum; targs[0].rnd = rnd; targs[0].id = 0; if(threadremove(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].tdb = tdb; targs[i].rnum = rnum; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadremove, targs + i) != 0){ eprint(tdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(tdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tctdbrnum(tdb)); iprintf("size: %llu\n", (unsigned long long)tctdbfsiz(tdb)); sysprint(); if(!tctdbclose(tdb)){ eprint(tdb, __LINE__, "tctdbclose"); err = true; } tctdbdel(tdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform wicked command */ static int procwicked(const char *path, int tnum, int rnum, int opts, int omode){ iprintf("\n seed=%u path=%s tnum=%d rnum=%d opts=%d omode=%d\n\n", g_randseed, path, tnum, rnum, opts, omode); bool err = false; double stime = tctime(); TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(!tctdbsetmutex(tdb)){ eprint(tdb, __LINE__, "tctdbsetmutex"); err = true; } if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(tdb, __LINE__, "tctdbsetcodecfunc"); err = true; } if(!tctdbtune(tdb, rnum / 50, 2, -1, opts)){ eprint(tdb, __LINE__, "tctdbtune"); err = true; } if(!tctdbsetcache(tdb, rnum / 10, 128, 256)){ eprint(tdb, __LINE__, "tctdbsetcache"); err = true; } if(!tctdbsetxmsiz(tdb, rnum * sizeof(int))){ eprint(tdb, __LINE__, "tctdbsetxmsiz"); err = true; } if(!tctdbsetdfunit(tdb, 8)){ eprint(tdb, __LINE__, "tctdbsetdfunit"); err = true; } if(!tctdbsetinvcache(tdb, -1, 0.5)){ eprint(tdb, __LINE__, "tctdbsetinvcache"); err = true; } if(!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | TDBOTRUNC | omode)){ eprint(tdb, __LINE__, "tctdbopen"); err = true; } if(!tctdbsetindex(tdb, "", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "str", TDBITLEXICAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "num", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "type", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "flag", TDBITTOKEN)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "text", TDBITQGRAM)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbiterinit(tdb)){ eprint(tdb, __LINE__, "tctdbiterinit"); err = true; } TARGWICKED targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].tdb = tdb; targs[0].rnum = rnum; targs[0].id = 0; if(threadwicked(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].tdb = tdb; targs[i].rnum = rnum; targs[i].id = i; if(pthread_create(threads + i, NULL, threadwicked, targs + i) != 0){ eprint(tdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(tdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tctdbrnum(tdb)); iprintf("size: %llu\n", (unsigned long long)tctdbfsiz(tdb)); sysprint(); if(!tctdbclose(tdb)){ eprint(tdb, __LINE__, "tctdbclose"); err = true; } tctdbdel(tdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform typical command */ static int proctypical(const char *path, int tnum, int rnum, int bnum, int apow, int fpow, int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int omode, int rratio){ iprintf("\n seed=%u path=%s tnum=%d rnum=%d bnum=%d apow=%d" " fpow=%d opts=%d rcnum=%d lcnum=%d ncnum=%d xmsiz=%d dfunit=%d" " omode=%d rratio=%d\n\n", g_randseed, path, tnum, rnum, bnum, apow, fpow, opts, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rratio); bool err = false; double stime = tctime(); TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(!tctdbsetmutex(tdb)){ eprint(tdb, __LINE__, "tctdbsetmutex"); err = true; } if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(tdb, __LINE__, "tctdbsetcodecfunc"); err = true; } if(!tctdbtune(tdb, bnum, apow, fpow, opts)){ eprint(tdb, __LINE__, "tctdbtune"); err = true; } if(!tctdbsetcache(tdb, rcnum, lcnum, ncnum)){ eprint(tdb, __LINE__, "tctdbsetcache"); err = true; } if(xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)){ eprint(tdb, __LINE__, "tctdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)){ eprint(tdb, __LINE__, "tctdbsetdfunit"); err = true; } if(!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | TDBOTRUNC | omode)){ eprint(tdb, __LINE__, "tctdbopen"); err = true; } if(!tctdbsetindex(tdb, "", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "str", TDBITLEXICAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "num", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "type", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "flag", TDBITTOKEN)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "text", TDBITQGRAM)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } TARGTYPICAL targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].tdb = tdb; targs[0].rnum = rnum; targs[0].rratio = rratio; targs[0].id = 0; if(threadtypical(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].tdb = tdb; targs[i].rnum = rnum; targs[i].rratio= rratio; targs[i].id = i; if(pthread_create(threads + i, NULL, threadtypical, targs + i) != 0){ eprint(tdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(tdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tctdbrnum(tdb)); iprintf("size: %llu\n", (unsigned long long)tctdbfsiz(tdb)); sysprint(); if(!tctdbclose(tdb)){ eprint(tdb, __LINE__, "tctdbclose"); err = true; } tctdbdel(tdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* thread the write function */ static void *threadwrite(void *targ){ TCTDB *tdb = ((TARGWRITE *)targ)->tdb; int rnum = ((TARGWRITE *)targ)->rnum; bool rnd = ((TARGWRITE *)targ)->rnd; int id = ((TARGWRITE *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum; i++){ int uid = rnd ? (base + myrand(i) + 1) : tctdbgenuid(tdb); char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", uid); TCMAP *cols = tcmapnew2(7); char vbuf[RECBUFSIZ*5]; int vsiz = sprintf(vbuf, "%d", uid); tcmapput(cols, "str", 3, vbuf, vsiz); if(myrand(3) == 0){ vsiz = sprintf(vbuf, "%.2f", (myrand(i * 100) + 1) / 100.0); } else { vsiz = sprintf(vbuf, "%d", myrand(i) + 1); } tcmapput(cols, "num", 3, vbuf, vsiz); vsiz = sprintf(vbuf, "%d", myrand(32) + 1); tcmapput(cols, "type", 4, vbuf, vsiz); int num = myrand(5); int pt = 0; char *wp = vbuf; for(int j = 0; j < num; j++){ pt += myrand(5) + 1; if(wp > vbuf) *(wp++) = ','; wp += sprintf(wp, "%d", pt); } *wp = '\0'; if(*vbuf != '\0'){ tcmapput(cols, "flag", 4, vbuf, wp - vbuf); tcmapput(cols, "text", 4, vbuf, wp - vbuf); } if(!tctdbput(tdb, pkbuf, pksiz, cols)){ eprint(tdb, __LINE__, "tctdbput"); err = true; break; } tcmapdel(cols); if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the read function */ static void *threadread(void *targ){ TCTDB *tdb = ((TARGREAD *)targ)->tdb; int rnum = ((TARGREAD *)targ)->rnum; bool rnd = ((TARGREAD *)targ)->rnd; int id = ((TARGREAD *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum && !err; i++){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", base + (rnd ? myrandnd(i) : i)); TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); if(cols){ tcmapdel(cols); } else if(!rnd || tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbget"); err = true; } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the remove function */ static void *threadremove(void *targ){ TCTDB *tdb = ((TARGREMOVE *)targ)->tdb; int rnum = ((TARGREMOVE *)targ)->rnum; bool rnd = ((TARGREMOVE *)targ)->rnd; int id = ((TARGREMOVE *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum; i++){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", base + (rnd ? myrand(i + 1) : i)); if(!tctdbout(tdb, pkbuf, pksiz) && (!rnd || tctdbecode(tdb) != TCENOREC)){ eprint(tdb, __LINE__, "tctdbout"); err = true; break; } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the wicked function */ static void *threadwicked(void *targ){ TCTDB *tdb = ((TARGWICKED *)targ)->tdb; int rnum = ((TARGWICKED *)targ)->rnum; int id = ((TARGWICKED *)targ)->id; bool err = false; const char *names[] = { "", "str", "num", "type", "flag", "c1" }; int ops[] = { TDBQCSTREQ, TDBQCSTRINC, TDBQCSTRBW, TDBQCSTREW, TDBQCSTRAND, TDBQCSTROR, TDBQCSTROREQ, TDBQCSTRRX, TDBQCNUMEQ, TDBQCNUMGT, TDBQCNUMGE, TDBQCNUMLT, TDBQCNUMLE, TDBQCNUMBT, TDBQCNUMOREQ }; int ftsops[] = { TDBQCFTSPH, TDBQCFTSAND, TDBQCFTSOR, TDBQCFTSEX }; int types[] = { TDBQOSTRASC, TDBQOSTRDESC, TDBQONUMASC, TDBQONUMDESC }; for(int i = 1; i <= rnum && !err; i++){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", myrand(rnum * (id + 1))); TCMAP *cols = tcmapnew2(7); char vbuf[RECBUFSIZ*5]; int vsiz = sprintf(vbuf, "%d", id); tcmapput(cols, "str", 3, vbuf, vsiz); if(myrand(3) == 0){ vsiz = sprintf(vbuf, "%.2f", (myrand(i * 100) + 1) / 100.0); } else { vsiz = sprintf(vbuf, "%d", myrand(i) + 1); } tcmapput(cols, "num", 3, vbuf, vsiz); vsiz = sprintf(vbuf, "%d", myrand(32) + 1); tcmapput(cols, "type", 4, vbuf, vsiz); int num = myrand(5); int pt = 0; char *wp = vbuf; for(int j = 0; j < num; j++){ pt += myrand(5) + 1; if(wp > vbuf) *(wp++) = ','; wp += sprintf(wp, "%d", pt); } *wp = '\0'; if(*vbuf != '\0'){ tcmapput(cols, "flag", 4, vbuf, wp - vbuf); tcmapput(cols, "text", 4, vbuf, wp - vbuf); } char nbuf[RECBUFSIZ]; int nsiz = sprintf(nbuf, "c%d", myrand(i) + 1); vsiz = sprintf(vbuf, "%d", myrand(i) + 1); tcmapput(cols, nbuf, nsiz, vbuf, vsiz); char *cbuf; int csiz; TCMAP *ncols; TDBQRY *qry; switch(myrand(17)){ case 0: if(id == 0) iputchar('0'); if(!tctdbput(tdb, pkbuf, pksiz, cols)){ eprint(tdb, __LINE__, "tctdbput"); err = true; } break; case 1: if(id == 0) iputchar('1'); cbuf = tcstrjoin4(cols, &csiz); if(!tctdbput2(tdb, pkbuf, pksiz, cbuf, csiz)){ eprint(tdb, __LINE__, "tctdbput2"); err = true; } tcfree(cbuf); break; case 2: if(id == 0) iputchar('2'); cbuf = tcstrjoin3(cols, '\t'); if(!tctdbput3(tdb, pkbuf, cbuf)){ eprint(tdb, __LINE__, "tctdbput3"); err = true; } tcfree(cbuf); break; case 3: if(id == 0) iputchar('3'); if(!tctdbputkeep(tdb, pkbuf, pksiz, cols) && tctdbecode(tdb) != TCEKEEP){ eprint(tdb, __LINE__, "tctdbputkeep"); err = true; } break; case 4: if(id == 0) iputchar('4'); cbuf = tcstrjoin4(cols, &csiz); if(!tctdbputkeep2(tdb, pkbuf, pksiz, cbuf, csiz) && tctdbecode(tdb) != TCEKEEP){ eprint(tdb, __LINE__, "tctdbputkeep2"); err = true; } tcfree(cbuf); break; case 5: if(id == 0) iputchar('5'); cbuf = tcstrjoin3(cols, '\t'); if(!tctdbputkeep3(tdb, pkbuf, cbuf) && tctdbecode(tdb) != TCEKEEP){ eprint(tdb, __LINE__, "tctdbputkeep3"); err = true; } tcfree(cbuf); break; case 6: if(id == 0) iputchar('6'); if(!tctdbputcat(tdb, pkbuf, pksiz, cols)){ eprint(tdb, __LINE__, "tctdbputcat"); err = true; } break; case 7: if(id == 0) iputchar('7'); cbuf = tcstrjoin4(cols, &csiz); if(!tctdbputcat2(tdb, pkbuf, pksiz, cbuf, csiz)){ eprint(tdb, __LINE__, "tctdbputcat2"); err = true; } tcfree(cbuf); break; case 8: if(id == 0) iputchar('8'); cbuf = tcstrjoin3(cols, '\t'); if(!tctdbputcat3(tdb, pkbuf, cbuf)){ eprint(tdb, __LINE__, "tctdbputcat3"); err = true; } tcfree(cbuf); break; case 9: if(id == 0) iputchar('9'); if(myrand(2) == 0){ if(!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbout"); err = true; } } break; case 10: if(id == 0) iputchar('A'); if(myrand(2) == 0){ if(!tctdbout2(tdb, pkbuf) && tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbout2"); err = true; } } break; case 11: if(id == 0) iputchar('B'); ncols = tctdbget(tdb, pkbuf, pksiz); if(ncols){ tcmapdel(ncols); } else if(tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbget"); err = true; } break; case 12: if(id == 0) iputchar('C'); cbuf = tctdbget2(tdb, pkbuf, pksiz, &csiz); if(cbuf){ tcfree(cbuf); } else if(tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbget2"); err = true; } break; case 13: if(id == 0) iputchar('D'); cbuf = tctdbget3(tdb, pkbuf); if(cbuf){ tcfree(cbuf); } else if(tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbget3"); err = true; } break; case 14: if(id == 0) iputchar('E'); if(myrand(rnum / 50) == 0){ if(!tctdbiterinit(tdb)){ eprint(tdb, __LINE__, "tctdbiterinit"); err = true; } } for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){ int iksiz; char *ikbuf = tctdbiternext(tdb, &iksiz); if(ikbuf){ tcfree(ikbuf); } else { int ecode = tctdbecode(tdb); if(ecode != TCEINVALID && ecode != TCENOREC){ eprint(tdb, __LINE__, "tctdbiternext"); err = true; } } } break; case 15: if(id == 0) iputchar('F'); qry = tctdbqrynew(tdb); if(myrand(10) != 0){ char expr[RECBUFSIZ]; sprintf(expr, "%d", myrand(i) + 1); switch(myrand(6)){ default: tctdbqryaddcond(qry, "str", TDBQCSTREQ, expr); break; case 1: tctdbqryaddcond(qry, "str", TDBQCSTRBW, expr); break; case 2: tctdbqryaddcond(qry, "str", TDBQCSTROREQ, expr); break; case 3: tctdbqryaddcond(qry, "num", TDBQCNUMEQ, expr); break; case 4: tctdbqryaddcond(qry, "num", TDBQCNUMGT, expr); break; case 5: tctdbqryaddcond(qry, "num", TDBQCNUMLT, expr); break; } switch(myrand(5)){ case 0: tctdbqrysetorder(qry, "str", TDBQOSTRASC); break; case 1: tctdbqrysetorder(qry, "str", TDBQOSTRDESC); break; case 2: tctdbqrysetorder(qry, "num", TDBQONUMASC); break; case 3: tctdbqrysetorder(qry, "num", TDBQONUMDESC); break; } tctdbqrysetlimit(qry, 10, myrand(10)); } else { int cnum = myrand(4); if(cnum < 1 && myrand(5) != 0) cnum = 1; for(int j = 0; j < cnum; j++){ const char *name = names[myrand(sizeof(names) / sizeof(*names))]; int op = ops[myrand(sizeof(ops) / sizeof(*ops))]; if(myrand(10) == 0) op = ftsops[myrand(sizeof(ftsops) / sizeof(*ftsops))]; if(myrand(20) == 0) op |= TDBQCNEGATE; if(myrand(20) == 0) op |= TDBQCNOIDX; char expr[RECBUFSIZ*3]; char *wp = expr; if(myrand(3) == 0){ wp += sprintf(expr, "%f", myrand(i * 100) / 100.0); } else { wp += sprintf(expr, "%d", myrand(i)); } if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); tctdbqryaddcond(qry, name, op, expr); } if(myrand(3) != 0){ const char *name = names[myrand(sizeof(names) / sizeof(*names))]; int type = types[myrand(sizeof(types) / sizeof(*types))]; tctdbqrysetorder(qry, name, type); } if(myrand(3) != 0) tctdbqrysetlimit(qry, myrand(i), myrand(10)); } if(myrand(10) == 0){ TCLIST *res = tctdbqrysearch(qry); tclistdel(res); } tctdbqrydel(qry); break; default: if(id == 0) iputchar('@'); if(tctdbtranbegin(tdb)){ if(myrand(2) == 0){ if(!tctdbput(tdb, pkbuf, pksiz, cols)){ eprint(tdb, __LINE__, "tctdbput"); err = true; } } else { if(!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbout"); err = true; } } if(myrand(2) == 0){ if(!tctdbtranabort(tdb)){ eprint(tdb, __LINE__, "tctdbtranabort"); err = true; } } else { if(!tctdbtrancommit(tdb)){ eprint(tdb, __LINE__, "tctdbtrancommit"); err = true; } } } else { eprint(tdb, __LINE__, "tctdbtranbegin"); err = true; } if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX); break; } tcmapdel(cols); if(id == 0){ if(i % 50 == 0) iprintf(" (%08d)\n", i); if(id == 0 && i == rnum / 4){ if(!tctdboptimize(tdb, rnum / 50, -1, -1, -1) && tctdbecode(tdb) != TCEINVALID){ eprint(tdb, __LINE__, "tctdboptimize"); err = true; } if(!tctdbiterinit(tdb)){ eprint(tdb, __LINE__, "tctdbiterinit"); err = true; } } } } return err ? "error" : NULL; } /* thread the typical function */ static void *threadtypical(void *targ){ TCTDB *tdb = ((TARGTYPICAL *)targ)->tdb; int rnum = ((TARGTYPICAL *)targ)->rnum; int rratio = ((TARGTYPICAL *)targ)->rratio; int id = ((TARGTYPICAL *)targ)->id; bool err = false; int base = id * rnum; int mrange = tclmax(50 + rratio, 100); for(int i = 1; !err && i <= rnum; i++){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%08d", base + myrandnd(i)); int rnd = myrand(mrange); if(rnd < 20){ TCMAP *cols = tcmapnew2(7); char vbuf[RECBUFSIZ*5]; int vsiz = sprintf(vbuf, "%d", id); tcmapput(cols, "str", 3, vbuf, vsiz); if(myrand(3) == 0){ vsiz = sprintf(vbuf, "%.2f", (myrand(i * 100) + 1) / 100.0); } else { vsiz = sprintf(vbuf, "%d", myrand(i) + 1); } tcmapput(cols, "num", 3, vbuf, vsiz); vsiz = sprintf(vbuf, "%d", myrand(32) + 1); tcmapput(cols, "type", 4, vbuf, vsiz); int num = myrand(5); int pt = 0; char *wp = vbuf; for(int j = 0; j < num; j++){ pt += myrand(5) + 1; if(wp > vbuf) *(wp++) = ','; wp += sprintf(wp, "%d", pt); } *wp = '\0'; if(*vbuf != '\0'){ tcmapput(cols, "flag", 4, vbuf, wp - vbuf); tcmapput(cols, "text", 4, vbuf, wp - vbuf); } char nbuf[RECBUFSIZ]; int nsiz = sprintf(nbuf, "c%d", myrand(i) + 1); vsiz = sprintf(vbuf, "%d", myrand(i) + 1); tcmapput(cols, nbuf, nsiz, vbuf, vsiz); if(myrand(2) == 0){ if(!tctdbput(tdb, pkbuf, pksiz, cols)){ eprint(tdb, __LINE__, "tctdbput"); err = true; } } else { if(!tctdbput(tdb, pkbuf, pksiz, cols) && tctdbecode(tdb) && tctdbecode(tdb) != TCEKEEP){ eprint(tdb, __LINE__, "tctdbput"); err = true; } } tcmapdel(cols); } else if(rnd < 30){ if(!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) && tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbout"); err = true; } } else if(rnd < 31){ if(myrand(10) == 0 && !tctdbiterinit(tdb) && tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbiterinit"); err = true; } for(int j = 0; !err && j < 10; j++){ int ksiz; char *kbuf = tctdbiternext(tdb, &ksiz); if(kbuf){ tcfree(kbuf); } else if(tctdbecode(tdb) != TCEINVALID && tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbiternext"); err = true; } } } else { TDBQRY *qry = tctdbqrynew(tdb); char expr[RECBUFSIZ]; sprintf(expr, "%d", myrand(i) + 1); switch(myrand(6) * 1){ default: tctdbqryaddcond(qry, "str", TDBQCSTREQ, expr); break; case 1: tctdbqryaddcond(qry, "str", TDBQCSTRBW, expr); break; case 2: tctdbqryaddcond(qry, "str", TDBQCSTROREQ, expr); break; case 3: tctdbqryaddcond(qry, "num", TDBQCNUMEQ, expr); break; case 4: tctdbqryaddcond(qry, "num", TDBQCNUMGT, expr); break; case 5: tctdbqryaddcond(qry, "num", TDBQCNUMLT, expr); break; } tctdbqrysetlimit(qry, 10, myrand(5) * 10); TCLIST *res = tctdbqrysearch(qry); tclistdel(res); tctdbqrydel(qry); } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } // END OF FILE tokyocabinet-1.4.48/tcttest.c0000644000175000017500000017504212013574446015162 0ustar mikiomikio/************************************************************************************************* * The test cases of the table database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" #define RECBUFSIZ 48 // buffer for records /* global variables */ const char *g_progname; // program name unsigned int g_randseed; // random seed int g_dbgfd; // debugging output /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void iprintf(const char *format, ...); static void iputchar(int c); static void eprint(TCTDB *tdb, int line, const char *func); static void sysprint(void); static int myrand(int range); static void *pdprocfunc(const void *vbuf, int vsiz, int *sp, void *op); static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op); static int runwrite(int argc, char **argv); static int runread(int argc, char **argv); static int runremove(int argc, char **argv); static int runrcat(int argc, char **argv); static int runmisc(int argc, char **argv); static int runwicked(int argc, char **argv); static int procwrite(const char *path, int rnum, int bnum, int apow, int fpow, bool mt, int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int iflags, int omode, bool rnd); static int procread(const char *path, bool mt, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int omode, bool rnd); static int procremove(const char *path, bool mt, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int omode, bool rnd); static int procrcat(const char *path, int rnum, int bnum, int apow, int fpow, bool mt, int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int iflags, int omode, int pnum, bool dai, bool dad, bool rl, bool ru); static int procmisc(const char *path, int rnum, bool mt, int opts, int omode); static int procwicked(const char *path, int rnum, bool mt, int opts, int omode); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; const char *ebuf = getenv("TCRNDSEED"); g_randseed = ebuf ? tcatoix(ebuf) : tctime() * 1000; srand(g_randseed); ebuf = getenv("TCDBGFD"); g_dbgfd = ebuf ? tcatoix(ebuf) : UINT16_MAX; if(argc < 2) usage(); int 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], "remove")){ rv = runremove(argc, argv); } else if(!strcmp(argv[1], "rcat")){ rv = runrcat(argc, argv); } else if(!strcmp(argv[1], "misc")){ rv = runmisc(argc, argv); } else if(!strcmp(argv[1], "wicked")){ rv = runwicked(argc, argv); } else { usage(); } if(rv != 0){ printf("FAILED: TCRNDSEED=%u PID=%d", g_randseed, (int)getpid()); for(int i = 0; i < argc; i++){ printf(" %s", argv[i]); } printf("\n\n"); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: test cases of the table database API of Tokyo Cabinet\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s 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]]]\n", g_progname); fprintf(stderr, " %s read [-mt] [-rc num] [-lc num] [-nc num] [-xm num] [-df num]" " [-nl|-nb] [-rnd] path\n", g_progname); fprintf(stderr, " %s remove [-mt] [-rc num] [-lc num] [-nc num] [-xm num] [-df num]" " [-nl|-nb] [-rnd] path\n", g_progname); fprintf(stderr, " %s 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]]]\n", g_progname); fprintf(stderr, " %s misc [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum\n", g_progname); fprintf(stderr, " %s wicked [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path 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); } /* print a character and flush the buffer */ static void iputchar(int c){ putchar(c); fflush(stdout); } /* print error message of table database */ static void eprint(TCTDB *tdb, int line, const char *func){ const char *path = tctdbpath(tdb); int ecode = tctdbecode(tdb); fprintf(stderr, "%s: %s: %d: %s: error: %d: %s\n", g_progname, path ? path : "-", line, func, ecode, tctdberrmsg(ecode)); } /* print system information */ static void sysprint(void){ TCMAP *info = tcsysinfo(); if(info){ tcmapiterinit(info); const char *kbuf; while((kbuf = tcmapiternext2(info)) != NULL){ iprintf("sys_%s: %s\n", kbuf, tcmapiterval2(kbuf)); } tcmapdel(info); } } /* get a random number */ static int myrand(int range){ if(range < 2) return 0; int high = (unsigned int)rand() >> 4; int low = range * (rand() / (RAND_MAX + 1.0)); low &= (unsigned int)INT_MAX >> 4; return (high + low) % range; } /* duplication callback function */ static void *pdprocfunc(const void *vbuf, int vsiz, int *sp, void *op){ if(myrand(4) == 0) return (void *)-1; if(myrand(2) == 0) return NULL; int len = myrand(RECBUFSIZ - 5); char buf[RECBUFSIZ]; memcpy(buf, "proc", 5); memset(buf + 5, '*', len); *sp = len + 5; return tcmemdup(buf, len + 5); } /* iterator function */ static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op){ unsigned int sum = 0; while(--ksiz >= 0){ sum += ((char *)kbuf)[ksiz]; } while(--vsiz >= 0){ sum += ((char *)vbuf)[vsiz]; } return myrand(100 + (sum & 0xff)) > 0; } /* parse arguments of write command */ static int runwrite(int argc, char **argv){ char *path = NULL; char *rstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; bool mt = false; int opts = 0; int rcnum = 0; int lcnum = 0; int ncnum = 0; int xmsiz = -1; int dfunit = 0; int iflags = 0; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-tl")){ opts |= TDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= TDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= TDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= TDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= TDBTEXCODEC; } else if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-lc")){ if(++i >= argc) usage(); lcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nc")){ if(++i >= argc) usage(); ncnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-ip")){ iflags |= 1 << 0; } else if(!strcmp(argv[i], "-is")){ iflags |= 1 << 1; } else if(!strcmp(argv[i], "-in")){ iflags |= 1 << 2; } else if(!strcmp(argv[i], "-it")){ iflags |= 1 << 3; } else if(!strcmp(argv[i], "-if")){ iflags |= 1 << 4; } else if(!strcmp(argv[i], "-ix")){ iflags |= 1 << 5; } else if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = procwrite(path, rnum, bnum, apow, fpow, mt, opts, rcnum, lcnum, ncnum, xmsiz, dfunit, iflags, omode, rnd); return rv; } /* parse arguments of read command */ static int runread(int argc, char **argv){ char *path = NULL; bool mt = false; int rcnum = 0; int lcnum = 0; int ncnum = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-lc")){ if(++i >= argc) usage(); lcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nc")){ if(++i >= argc) usage(); ncnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procread(path, mt, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); return rv; } /* parse arguments of remove command */ static int runremove(int argc, char **argv){ char *path = NULL; bool mt = false; int rcnum = 0; int lcnum = 0; int ncnum = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-lc")){ if(++i >= argc) usage(); lcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nc")){ if(++i >= argc) usage(); ncnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procremove(path, mt, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); return rv; } /* parse arguments of rcat command */ static int runrcat(int argc, char **argv){ char *path = NULL; char *rstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; bool mt = false; int opts = 0; int rcnum = 0; int lcnum = 0; int ncnum = 0; int xmsiz = -1; int dfunit = 0; int iflags = 0; int omode = 0; int pnum = 0; bool dai = false; bool dad = false; bool rl = false; bool ru = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-tl")){ opts |= TDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= TDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= TDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= TDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= TDBTEXCODEC; } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-lc")){ if(++i >= argc) usage(); lcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nc")){ if(++i >= argc) usage(); ncnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-ip")){ iflags |= 1 << 0; } else if(!strcmp(argv[i], "-is")){ iflags |= 1 << 1; } else if(!strcmp(argv[i], "-in")){ iflags |= 1 << 2; } else if(!strcmp(argv[i], "-it")){ iflags |= 1 << 3; } else if(!strcmp(argv[i], "-if")){ iflags |= 1 << 4; } else if(!strcmp(argv[i], "-ix")){ iflags |= 1 << 5; } else if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else if(!strcmp(argv[i], "-pn")){ if(++i >= argc) usage(); pnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-dai")){ dai = true; } else if(!strcmp(argv[i], "-dad")){ dad = true; } else if(!strcmp(argv[i], "-rl")){ rl = true; } else if(!strcmp(argv[i], "-ru")){ ru = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = procrcat(path, rnum, bnum, apow, fpow, mt, opts, rcnum, lcnum, ncnum, xmsiz, dfunit, iflags, omode, pnum, dai, dad, rl, ru); return rv; } /* parse arguments of misc command */ static int runmisc(int argc, char **argv){ char *path = NULL; char *rstr = NULL; bool mt = false; int opts = 0; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-tl")){ opts |= TDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= TDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= TDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= TDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= TDBTEXCODEC; } else if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int rv = procmisc(path, rnum, mt, opts, omode); return rv; } /* parse arguments of wicked command */ static int runwicked(int argc, char **argv){ char *path = NULL; char *rstr = NULL; bool mt = false; int opts = 0; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-tl")){ opts |= TDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= TDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= TDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= TDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= TDBTEXCODEC; } else if(!strcmp(argv[i], "-nl")){ omode |= TDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= TDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int rv = procwicked(path, rnum, mt, opts, omode); return rv; } /* perform write command */ static int procwrite(const char *path, int rnum, int bnum, int apow, int fpow, bool mt, int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int iflags, int omode, bool rnd){ iprintf("\n seed=%u path=%s rnum=%d bnum=%d apow=%d fpow=%d mt=%d" " opts=%d rcnum=%d lcnum=%d ncnum=%d xmsiz=%d dfunit=%d iflags=%d" " omode=%d rnd=%d\n\n", g_randseed, path, rnum, bnum, apow, fpow, mt, opts, rcnum, lcnum, ncnum, xmsiz, dfunit, iflags, omode, rnd); bool err = false; double stime = tctime(); TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(mt && !tctdbsetmutex(tdb)){ eprint(tdb, __LINE__, "tctdbsetmutex"); err = true; } if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(tdb, __LINE__, "tctdbsetcodecfunc"); err = true; } if(!tctdbtune(tdb, bnum, apow, fpow, opts)){ eprint(tdb, __LINE__, "tctdbtune"); err = true; } if(!tctdbsetcache(tdb, rcnum, lcnum, ncnum)){ eprint(tdb, __LINE__, "tctdbsetcache"); err = true; } if(xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)){ eprint(tdb, __LINE__, "tctdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)){ eprint(tdb, __LINE__, "tctdbsetdfunit"); err = true; } if(!rnd) omode |= TDBOTRUNC; if(!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | omode)){ eprint(tdb, __LINE__, "tctdbopen"); err = true; } if((iflags & (1 << 0)) && !tctdbsetindex(tdb, "", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if((iflags & (1 << 1)) && !tctdbsetindex(tdb, "str", TDBITLEXICAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if((iflags & (1 << 2)) && !tctdbsetindex(tdb, "num", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if((iflags & (1 << 3)) && !tctdbsetindex(tdb, "type", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if((iflags & (1 << 4)) && !tctdbsetindex(tdb, "flag", TDBITTOKEN)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if((iflags & (1 << 5)) && !tctdbsetindex(tdb, "text", TDBITQGRAM)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } for(int i = 1; i <= rnum; i++){ int id = rnd ? myrand(rnum) + 1 : (int)tctdbgenuid(tdb); char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", id); TCMAP *cols = tcmapnew2(7); char vbuf[RECBUFSIZ*5]; int vsiz = sprintf(vbuf, "%d", id); tcmapput(cols, "str", 3, vbuf, vsiz); if(myrand(3) == 0){ vsiz = sprintf(vbuf, "%.2f", (myrand(i * 100) + 1) / 100.0); } else { vsiz = sprintf(vbuf, "%d", myrand(i) + 1); } tcmapput(cols, "num", 3, vbuf, vsiz); vsiz = sprintf(vbuf, "%d", myrand(32) + 1); tcmapput(cols, "type", 4, vbuf, vsiz); int num = myrand(5); int pt = 0; char *wp = vbuf; for(int j = 0; j < num; j++){ pt += myrand(5) + 1; if(wp > vbuf) *(wp++) = ','; wp += sprintf(wp, "%d", pt); } *wp = '\0'; if(*vbuf != '\0'){ tcmapput(cols, "flag", 4, vbuf, wp - vbuf); tcmapput(cols, "text", 4, vbuf, wp - vbuf); } if(!tctdbput(tdb, pkbuf, pksiz, cols)){ eprint(tdb, __LINE__, "tctdbput"); err = true; break; } tcmapdel(cols); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tctdbrnum(tdb)); iprintf("size: %llu\n", (unsigned long long)tctdbfsiz(tdb)); sysprint(); if(!tctdbclose(tdb)){ eprint(tdb, __LINE__, "tctdbclose"); err = true; } tctdbdel(tdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform read command */ static int procread(const char *path, bool mt, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int omode, bool rnd){ iprintf("\n seed=%u path=%s mt=%d rcnum=%d lcnum=%d ncnum=%d" " xmsiz=%d dfunit=%d omode=%d rnd=%d\n\n", g_randseed, path, mt, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); bool err = false; double stime = tctime(); TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(mt && !tctdbsetmutex(tdb)){ eprint(tdb, __LINE__, "tctdbsetmutex"); err = true; } if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(tdb, __LINE__, "tctdbsetcodecfunc"); err = true; } if(!tctdbsetcache(tdb, rcnum, lcnum, ncnum)){ eprint(tdb, __LINE__, "tctdbsetcache"); err = true; } if(xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)){ eprint(tdb, __LINE__, "tctdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)){ eprint(tdb, __LINE__, "tctdbsetdfunit"); err = true; } if(!tctdbopen(tdb, path, TDBOREADER | omode)){ eprint(tdb, __LINE__, "tctdbopen"); err = true; } int rnum = tctdbrnum(tdb); for(int i = 1; i <= rnum; i++){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", rnd ? myrand(rnum) + 1 : i); TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); if(cols){ tcmapdel(cols); } else if(!rnd || tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbget"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tctdbrnum(tdb)); iprintf("size: %llu\n", (unsigned long long)tctdbfsiz(tdb)); sysprint(); if(!tctdbclose(tdb)){ eprint(tdb, __LINE__, "tctdbclose"); err = true; } tctdbdel(tdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform remove command */ static int procremove(const char *path, bool mt, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int omode, bool rnd){ iprintf("\n seed=%u path=%s mt=%d rcnum=%d lcnum=%d ncnum=%d" " xmsiz=%d dfunit=%d omode=%d rnd=%d\n\n", g_randseed, path, mt, rcnum, lcnum, ncnum, xmsiz, dfunit, omode, rnd); bool err = false; double stime = tctime(); TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(mt && !tctdbsetmutex(tdb)){ eprint(tdb, __LINE__, "tctdbsetmutex"); err = true; } if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(tdb, __LINE__, "tctdbsetcodecfunc"); err = true; } if(!tctdbsetcache(tdb, rcnum, lcnum, ncnum)){ eprint(tdb, __LINE__, "tctdbsetcache"); err = true; } if(xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)){ eprint(tdb, __LINE__, "tctdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)){ eprint(tdb, __LINE__, "tctdbsetdfunit"); err = true; } if(!tctdbopen(tdb, path, TDBOWRITER | omode)){ eprint(tdb, __LINE__, "tctdbopen"); err = true; } int rnum = tctdbrnum(tdb); for(int i = 1; i <= rnum; i++){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", rnd ? myrand(rnum) + 1 : i); if(!tctdbout(tdb, pkbuf, pksiz) && !(rnd && tctdbecode(tdb) == TCENOREC)){ eprint(tdb, __LINE__, "tctdbout"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tctdbrnum(tdb)); iprintf("size: %llu\n", (unsigned long long)tctdbfsiz(tdb)); sysprint(); if(!tctdbclose(tdb)){ eprint(tdb, __LINE__, "tctdbclose"); err = true; } tctdbdel(tdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform rcat command */ static int procrcat(const char *path, int rnum, int bnum, int apow, int fpow, bool mt, int opts, int rcnum, int lcnum, int ncnum, int xmsiz, int dfunit, int iflags, int omode, int pnum, bool dai, bool dad, bool rl, bool ru){ iprintf("\n" " seed=%u path=%s rnum=%d bnum=%d apow=%d fpow=%d mt=%d opts=%d" " rcnum=%d lcnum=%d ncnum=%d xmsiz=%d dfunit=%d iflags=%d" " omode=%d pnum=%d dai=%d dad=%d rl=%d ru=%d\n\n", g_randseed, path, rnum, bnum, apow, fpow, mt, opts, rcnum, lcnum, rcnum, xmsiz, dfunit, iflags, omode, pnum, dai, dad, rl, ru); if(pnum < 1) pnum = rnum; bool err = false; double stime = tctime(); TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(mt && !tctdbsetmutex(tdb)){ eprint(tdb, __LINE__, "tctdbsetmutex"); err = true; } if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(tdb, __LINE__, "tctdbsetcodecfunc"); err = true; } if(!tctdbtune(tdb, bnum, apow, fpow, opts)){ eprint(tdb, __LINE__, "tctdbtune"); err = true; } if(!tctdbsetcache(tdb, rcnum, lcnum, ncnum)){ eprint(tdb, __LINE__, "tctdbsetcache"); err = true; } if(xmsiz >= 0 && !tctdbsetxmsiz(tdb, xmsiz)){ eprint(tdb, __LINE__, "tctdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tctdbsetdfunit(tdb, dfunit)){ eprint(tdb, __LINE__, "tctdbsetdfunit"); err = true; } if(!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | TDBOTRUNC | omode)){ eprint(tdb, __LINE__, "tctdbopen"); err = true; } if((iflags & (1 << 0)) && !tctdbsetindex(tdb, "", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if((iflags & (1 << 1)) && !tctdbsetindex(tdb, "str", TDBITLEXICAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if((iflags & (1 << 2)) && !tctdbsetindex(tdb, "num", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if((iflags & (1 << 3)) && !tctdbsetindex(tdb, "type", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if((iflags & (1 << 4)) && !tctdbsetindex(tdb, "flag", TDBITTOKEN)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if((iflags & (1 << 5)) && !tctdbsetindex(tdb, "text", TDBITQGRAM)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } for(int i = 1; i <= rnum; i++){ int id = myrand(pnum) + 1; char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", id); TCMAP *cols = tcmapnew2(7); char vbuf[RECBUFSIZ*5]; int vsiz = sprintf(vbuf, "%d", id); tcmapput(cols, "str", 3, vbuf, vsiz); if(myrand(3) == 0){ vsiz = sprintf(vbuf, "%.2f", (myrand(i * 100) + 1) / 100.0); } else { vsiz = sprintf(vbuf, "%d", myrand(i) + 1); } tcmapput(cols, "num", 3, vbuf, vsiz); vsiz = sprintf(vbuf, "%d", myrand(32) + 1); tcmapput(cols, "type", 4, vbuf, vsiz); int num = myrand(5); int pt = 0; char *wp = vbuf; for(int j = 0; j < num; j++){ pt += myrand(5) + 1; if(wp > vbuf) *(wp++) = ','; wp += sprintf(wp, "%d", pt); } *wp = '\0'; if(*vbuf != '\0'){ tcmapput(cols, "flag", 4, vbuf, wp - vbuf); tcmapput(cols, "text", 4, vbuf, wp - vbuf); } char nbuf[RECBUFSIZ]; int nsiz = sprintf(nbuf, "c%d", myrand(pnum) + 1); vsiz = sprintf(vbuf, "%d", myrand(rnum) + 1); tcmapput(cols, nbuf, nsiz, vbuf, vsiz); if(ru){ switch(myrand(8)){ case 0: if(!tctdbput(tdb, pkbuf, pksiz, cols)){ eprint(tdb, __LINE__, "tctdbput"); err = true; } break; case 1: if(!tctdbputkeep(tdb, pkbuf, pksiz, cols) && tctdbecode(tdb) != TCEKEEP){ eprint(tdb, __LINE__, "tctdbputkeep"); err = true; } break; case 2: if(!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbout"); err = true; } break; case 3: if(tctdbaddint(tdb, pkbuf, pksiz, 1) == INT_MIN && tctdbecode(tdb) != TCEKEEP){ eprint(tdb, __LINE__, "tctdbaddint"); err = true; } break; case 4: if(isnan(tctdbadddouble(tdb, pkbuf, pksiz, 1.0)) && tctdbecode(tdb) != TCEKEEP){ eprint(tdb, __LINE__, "tctdbadddouble"); err = true; } break; case 5: if(myrand(2) == 0){ if(!tctdbputproc(tdb, pkbuf, pksiz, pkbuf, pksiz, pdprocfunc, NULL) && tctdbecode(tdb) != TCEKEEP){ eprint(tdb, __LINE__, "tctdbputproc"); err = true; } } else { if(!tctdbputproc(tdb, pkbuf, pksiz, NULL, 0, pdprocfunc, NULL) && tctdbecode(tdb) != TCEKEEP && tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbputproc"); err = true; } } break; default: if(!tctdbputcat(tdb, pkbuf, pksiz, cols)){ eprint(tdb, __LINE__, "tctdbputcat"); err = true; } break; } if(err) break; } else { if(dai){ if(tctdbaddint(tdb, pkbuf, pksiz, myrand(3)) == INT_MIN){ eprint(tdb, __LINE__, "tctdbaddint"); err = true; break; } } else if(dad){ if(isnan(tctdbadddouble(tdb, pkbuf, pksiz, myrand(30) / 10.0))){ eprint(tdb, __LINE__, "tctdbadddouble"); err = true; break; } } else if(rl){ char fbuf[PATH_MAX]; int fsiz = myrand(PATH_MAX); for(int j = 0; j < fsiz; j++){ fbuf[j] = myrand(0x100); } tcmapput(cols, "bin", 3, fbuf, fsiz); if(!tctdbputcat(tdb, pkbuf, pksiz, cols)){ eprint(tdb, __LINE__, "tctdbputcat"); err = true; break; } } else { if(!tctdbputcat(tdb, pkbuf, pksiz, cols)){ eprint(tdb, __LINE__, "tctdbputcat"); err = true; break; } } } tcmapdel(cols); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tctdbrnum(tdb)); iprintf("size: %llu\n", (unsigned long long)tctdbfsiz(tdb)); sysprint(); if(!tctdbclose(tdb)){ eprint(tdb, __LINE__, "tctdbclose"); err = true; } tctdbdel(tdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform misc command */ static int procmisc(const char *path, int rnum, bool mt, int opts, int omode){ iprintf("\n seed=%u path=%s rnum=%d mt=%d opts=%d omode=%d\n\n", g_randseed, path, rnum, mt, opts, omode); bool err = false; double stime = tctime(); TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(mt && !tctdbsetmutex(tdb)){ eprint(tdb, __LINE__, "tctdbsetmutex"); err = true; } if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(tdb, __LINE__, "tctdbsetcodecfunc"); err = true; } if(!tctdbtune(tdb, rnum / 50, 2, -1, opts)){ eprint(tdb, __LINE__, "tctdbtune"); err = true; } if(!tctdbsetcache(tdb, rnum / 10, 128, 256)){ eprint(tdb, __LINE__, "tctdbsetcache"); err = true; } if(!tctdbsetxmsiz(tdb, rnum * sizeof(int))){ eprint(tdb, __LINE__, "tctdbsetxmsiz"); err = true; } if(!tctdbsetdfunit(tdb, 8)){ eprint(tdb, __LINE__, "tctdbsetdfunit"); err = true; } if(!tctdbsetinvcache(tdb, -1, 0.5)){ eprint(tdb, __LINE__, "tctdbsetinvcache"); err = true; } if(!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | TDBOTRUNC | omode)){ eprint(tdb, __LINE__, "tctdbopen"); err = true; } if(!tctdbsetindex(tdb, "", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "str", TDBITLEXICAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "num", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "type", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "flag", TDBITTOKEN)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "text", TDBITQGRAM)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(TCUSEPTHREAD){ TCTDB *tdbdup = tctdbnew(); if(tctdbopen(tdbdup, path, TDBOREADER)){ eprint(tdb, __LINE__, "(validation)"); err = true; } else if(tctdbecode(tdbdup) != TCETHREAD){ eprint(tdb, __LINE__, "(validation)"); err = true; } tctdbdel(tdbdup); } iprintf("writing:\n"); for(int i = 1; i <= rnum; i++){ int id = (int)tctdbgenuid(tdb); char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", id); TCMAP *cols = tcmapnew2(7); char vbuf[RECBUFSIZ*5]; int vsiz = sprintf(vbuf, "%d", id); tcmapput(cols, "str", 3, vbuf, vsiz); if(myrand(3) == 0){ vsiz = sprintf(vbuf, "%.2f", (myrand(i * 100) + 1) / 100.0); } else { vsiz = sprintf(vbuf, "%d", myrand(i) + 1); } tcmapput(cols, "num", 3, vbuf, vsiz); vsiz = sprintf(vbuf, "%d", myrand(32) + 1); tcmapput(cols, "type", 4, vbuf, vsiz); int num = myrand(5); int pt = 0; char *wp = vbuf; for(int j = 0; j < num; j++){ pt += myrand(5) + 1; if(wp > vbuf) *(wp++) = ','; wp += sprintf(wp, "%d", pt); } *wp = '\0'; if(*vbuf != '\0'){ tcmapput(cols, "flag", 4, vbuf, wp - vbuf); tcmapput(cols, "text", 4, vbuf, wp - vbuf); } char nbuf[RECBUFSIZ]; int nsiz = sprintf(nbuf, "c%d", myrand(32) + 1); vsiz = sprintf(vbuf, "%d", myrand(32) + 1); tcmapput(cols, nbuf, nsiz, vbuf, vsiz); if(i % 3 == 0){ if(!tctdbputkeep(tdb, pkbuf, pksiz, cols)){ eprint(tdb, __LINE__, "tctdbputkeep"); err = true; break; } } else { if(!tctdbputcat(tdb, pkbuf, pksiz, cols)){ eprint(tdb, __LINE__, "tctdbputcat"); err = true; break; } } tcmapdel(cols); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("reading:\n"); for(int i = 1; i <= rnum; i++){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", i); TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); if(cols){ const char *vbuf = tcmapget2(cols, "str"); if(!vbuf || strcmp(vbuf, pkbuf)){ eprint(tdb, __LINE__, "(validation)"); tcmapdel(cols); err = true; break; } tcmapdel(cols); } else { eprint(tdb, __LINE__, "tctdbget"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("erasing:\n"); for(int i = 1; i <= rnum; i++){ if(i % 2 == 1){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", i); if(!tctdbout(tdb, pkbuf, pksiz)){ eprint(tdb, __LINE__, "tctdbout"); err = true; break; } if(tctdbout(tdb, pkbuf, pksiz) || tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbout"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(tctdbrnum(tdb) != rnum / 2){ eprint(tdb, __LINE__, "tctdbrnum"); err = true; } if(tctdbuidseed(tdb) != rnum){ eprint(tdb, __LINE__, "tctdbuidseed"); err = true; } iprintf("searching:\n"); TDBQRY *qry = tctdbqrynew(tdb); TCLIST *res = tctdbqrysearch(qry); if(tclistnum(res) != rnum / 2){ eprint(tdb, __LINE__, "tctdbqrysearch"); err = true; } tclistdel(res); tctdbqrydel(qry); const char *names[] = { "", "str", "num", "type", "flag", "text", "c1" }; int ops[] = { TDBQCSTREQ, TDBQCSTRINC, TDBQCSTRBW, TDBQCSTREW, TDBQCSTRAND, TDBQCSTROR, TDBQCSTROREQ, TDBQCSTRRX, TDBQCNUMEQ, TDBQCNUMGT, TDBQCNUMGE, TDBQCNUMLT, TDBQCNUMLE, TDBQCNUMBT, TDBQCNUMOREQ }; int ftsops[] = { TDBQCFTSPH, TDBQCFTSAND, TDBQCFTSOR, TDBQCFTSEX }; int types[] = { TDBQOSTRASC, TDBQOSTRDESC, TDBQONUMASC, TDBQONUMDESC }; qry = tctdbqrynew(tdb); for(int i = 1; i <= rnum; i++){ if(myrand(100) != 0){ tctdbqrydel(qry); qry = tctdbqrynew(tdb); if(myrand(10) != 0){ char expr[RECBUFSIZ]; sprintf(expr, "%d", myrand(i) + 1); switch(myrand(6)){ default: tctdbqryaddcond(qry, "str", TDBQCSTREQ, expr); break; case 1: tctdbqryaddcond(qry, "str", TDBQCSTRBW, expr); break; case 2: tctdbqryaddcond(qry, "str", TDBQCSTROREQ, expr); break; case 3: tctdbqryaddcond(qry, "num", TDBQCNUMEQ, expr); break; case 4: tctdbqryaddcond(qry, "num", TDBQCNUMGT, expr); break; case 5: tctdbqryaddcond(qry, "num", TDBQCNUMLT, expr); break; } switch(myrand(5)){ case 0: tctdbqrysetorder(qry, "str", TDBQOSTRASC); break; case 1: tctdbqrysetorder(qry, "str", TDBQOSTRDESC); break; case 2: tctdbqrysetorder(qry, "num", TDBQONUMASC); break; case 3: tctdbqrysetorder(qry, "num", TDBQONUMDESC); break; } tctdbqrysetlimit(qry, 10, myrand(5) * 10); } else { int cnum = myrand(4); if(cnum < 1 && myrand(5) != 0) cnum = 1; for(int j = 0; j < cnum; j++){ const char *name = names[myrand(sizeof(names) / sizeof(*names))]; int op = ops[myrand(sizeof(ops) / sizeof(*ops))]; if(myrand(10) == 0) op = ftsops[myrand(sizeof(ftsops) / sizeof(*ftsops))]; if(myrand(20) == 0) op |= TDBQCNEGATE; if(myrand(20) == 0) op |= TDBQCNOIDX; char expr[RECBUFSIZ*3]; char *wp = expr; if(myrand(3) == 0){ wp += sprintf(expr, "%f", myrand(i * 100) / 100.0); } else { wp += sprintf(expr, "%d", myrand(i)); } if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); tctdbqryaddcond(qry, name, op, expr); } if(myrand(3) != 0){ const char *name = names[myrand(sizeof(names) / sizeof(*names))]; int type = types[myrand(sizeof(types) / sizeof(*types))]; tctdbqrysetorder(qry, name, type); } if(myrand(3) != 0) tctdbqrysetlimit(qry, myrand(i), myrand(10)); } } if(myrand(10) == 0){ TDBQRY *qrys[4]; int num = myrand(5); for(int j = 0; j < num; j++){ qrys[j] = qry; } TCLIST *res = tctdbmetasearch(qrys, num, TDBMSUNION + myrand(3)); if(num > 0){ for(int j = 0; j < 3 && j < tclistnum(res); j++){ int pksiz; const char *pkbuf = tclistval(res, j, &pksiz); TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); if(cols){ TCLIST *texts = tctdbqrykwic(qrys[0], cols, NULL, myrand(10), TCKWNOOVER); tclistdel(texts); tcmapdel(cols); } else { eprint(tdb, __LINE__, "tctdbget"); err = true; } } } tclistdel(res); } else { TCLIST *res = tctdbqrysearch(qry); tclistdel(res); } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } tctdbqrydel(qry); iprintf("random writing and reopening:\n"); for(int i = 1; i <= rnum; i++){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", myrand(rnum) + 1); switch(myrand(4)){ default: if(!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbout"); err = true; } break; case 1: if(!tctdbaddint(tdb, pkbuf, pksiz, 1)){ eprint(tdb, __LINE__, "tctdbaddint"); err = true; } break; case 2: if(!tctdbadddouble(tdb, pkbuf, pksiz, 1.0)){ eprint(tdb, __LINE__, "tctdbadddouble"); err = true; } break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tctdbclose(tdb)){ eprint(tdb, __LINE__, "tctdbclose"); err = true; } if(!tctdbopen(tdb, path, TDBOWRITER | omode)){ eprint(tdb, __LINE__, "tctdbopen"); err = true; } iprintf("random updating:\n"); for(int i = 1; i <= rnum; i++){ if(myrand(2) == 0){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%X", myrand(rnum) + 1); TCMAP *cols = tcmapnew2(7); char vbuf[RECBUFSIZ*2]; int vsiz = sprintf(vbuf, "%d", myrand(i) + 1); tcmapput(cols, "c1", 3, vbuf, vsiz); vsiz = sprintf(vbuf, " %d, %d ", myrand(i) + 1, rnum / (myrand(rnum) + 1)); tcmapput(cols, "flag", 4, vbuf, vsiz); tcmapput(cols, "text", 4, vbuf, vsiz); if(!tctdbputcat(tdb, pkbuf, pksiz, cols)){ eprint(tdb, __LINE__, "tctdbputcat"); err = true; break; } tcmapdel(cols); } else { char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%X", myrand(rnum) + 1); if(!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbout"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("checking iterator:\n"); int inum = 0; if(!tctdbiterinit(tdb)){ eprint(tdb, __LINE__, "tctdbiterinit"); err = true; } char *pkbuf; int pksiz; for(int i = 1; (pkbuf = tctdbiternext(tdb, &pksiz)) != NULL; i++, inum++){ TCMAP *cols = tctdbget(tdb, pkbuf, pksiz); if(!cols){ eprint(tdb, __LINE__, "tctdbget"); err = true; tcfree(pkbuf); break; } tcmapdel(cols); tcfree(pkbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); if(tctdbecode(tdb) != TCENOREC || inum != tctdbrnum(tdb)){ eprint(tdb, __LINE__, "(validation)"); err = true; } iprintf("checking search consistency:\n"); for(int i = 1; i <= rnum; i++){ TDBQRY *myqry = tctdbqrynew(tdb); qry = tctdbqrynew(tdb); int cnum = myrand(4); if(cnum < 1) cnum = 1; for(int j = 0; j < cnum; j++){ const char *name = names[myrand(sizeof(names) / sizeof(*names))]; int op = ops[myrand(sizeof(ops) / sizeof(*ops))]; if(myrand(10) == 0) op = ftsops[myrand(sizeof(ftsops) / sizeof(*ftsops))]; char expr[RECBUFSIZ*3]; char *wp = expr; if(myrand(3) == 0){ wp += sprintf(expr, "%f", myrand(i * 100) / 100.0); } else { wp += sprintf(expr, "%d", myrand(i)); } if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); tctdbqryaddcond(myqry, name, op | (myrand(2) == 0 ? TDBQCNOIDX : 0), expr); tctdbqryaddcond(qry, name, op | (myrand(2) == 0 ? TDBQCNOIDX : 0), expr); } int max = (myrand(10) == 0) ? 0 : myrand(10) + 1; if(max > 0){ tctdbqrysetlimit(myqry, max, 0); tctdbqrysetlimit(qry, max, 0); TCLIST *myres = tctdbqrysearch(myqry); res = tctdbqrysearch(qry); if(tclistnum(myres) != tclistnum(res)){ eprint(tdb, __LINE__, "(validation)"); err = true; } tclistdel(res); tclistdel(myres); } else { TCLIST *myres = tctdbqrysearch(myqry); res = tctdbqrysearch(qry); if(tclistnum(myres) == tclistnum(res)){ tclistsort(myres); tclistsort(res); int rnum = tclistnum(myres); for(int j = 0; j < rnum; j++){ int myrsiz; const char *myrbuf = tclistval(myres, j, &myrsiz); int rsiz; const char *rbuf = tclistval(res, j, &rsiz); if(myrsiz != rsiz || memcmp(myrbuf, rbuf, myrsiz)){ eprint(tdb, __LINE__, "(validation)"); err = true; break; } } } else { eprint(tdb, __LINE__, "(validation)"); err = true; } tclistdel(res); tclistdel(myres); } tctdbqrydel(qry); tctdbqrydel(myqry); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } qry = tctdbqrynew(tdb); tctdbqryaddcond(qry, "", TDBQCSTRBW, "1"); if(!tctdbqrysearchout(qry)){ eprint(tdb, __LINE__, "tctdbqrysearchout"); err = true; } tctdbqrydel(qry); qry = tctdbqrynew(tdb); tctdbqryaddcond(qry, "", TDBQCSTRBW, "2"); if(!tctdbqrysearchout2(qry)){ eprint(tdb, __LINE__, "tctdbqrysearchout2"); err = true; } tctdbqrydel(qry); if(myrand(4) == 0 && !tctdbdefrag(tdb, 0)){ eprint(tdb, __LINE__, "tctdbdefrag"); err = true; } if(myrand(4) == 0 && !tctdbcacheclear(tdb)){ eprint(tdb, __LINE__, "tctdbcacheclear"); err = true; } iprintf("checking transaction commit:\n"); if(!tctdbtranbegin(tdb)){ eprint(tdb, __LINE__, "tctdbtranbegin"); err = true; } for(int i = 1; i <= rnum; i++){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", myrand(rnum)); switch(myrand(4)){ default: if(!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbout"); err = true; } break; case 1: if(!tctdbaddint(tdb, pkbuf, pksiz, 1)){ eprint(tdb, __LINE__, "tctdbaddint"); err = true; } break; case 2: if(!tctdbadddouble(tdb, pkbuf, pksiz, 1.0)){ eprint(tdb, __LINE__, "tctdbadddouble"); err = true; } break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tctdbtrancommit(tdb)){ eprint(tdb, __LINE__, "tctdbtrancommit"); err = true; } iprintf("checking transaction abort:\n"); uint64_t ornum = tctdbrnum(tdb); uint64_t ofsiz = tctdbfsiz(tdb); if(!tctdbtranbegin(tdb)){ eprint(tdb, __LINE__, "tctdbtranbegin"); err = true; } for(int i = 1; i <= rnum; i++){ char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", myrand(rnum)); switch(myrand(4)){ default: if(!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbout"); err = true; } break; case 1: if(!tctdbaddint(tdb, pkbuf, pksiz, 1)){ eprint(tdb, __LINE__, "tctdbaddint"); err = true; } break; case 2: if(!tctdbadddouble(tdb, pkbuf, pksiz, 1.0)){ eprint(tdb, __LINE__, "tctdbadddouble"); err = true; } break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tctdbtranabort(tdb)){ eprint(tdb, __LINE__, "tctdbtranabort"); err = true; } if(tctdbrnum(tdb) != ornum || tctdbfsiz(tdb) != ofsiz){ eprint(tdb, __LINE__, "(validation)"); err = true; } if(!tctdbvanish(tdb)){ eprint(tdb, __LINE__, "tctdbvanish"); err = true; } if(!tctdbput3(tdb, "mikio", "str\tMIKIO\tbirth\t19780211")){ eprint(tdb, __LINE__, "tctdbput3"); err = true; } if(!tctdbtranbegin(tdb)){ eprint(tdb, __LINE__, "tctdbtranbegin"); err = true; } if(!tctdbput3(tdb, "mikio", "str\tMEKEO\tsex\tmale")){ eprint(tdb, __LINE__, "tctdbput3"); err = true; } for(int i = 0; i < 10; i++){ char pkbuf[RECBUFSIZ]; sprintf(pkbuf, "%d", myrand(10) + 1); char vbuf[RECBUFSIZ*2]; sprintf(vbuf, "%d\t%d", myrand(10) + 1, myrand(rnum) + 1); if(!tctdbput3(tdb, pkbuf, vbuf)){ eprint(tdb, __LINE__, "tctdbput"); err = true; } } if(!tctdbforeach(tdb, iterfunc, NULL)){ eprint(tdb, __LINE__, "tctdbforeach"); err = true; } iprintf("record number: %llu\n", (unsigned long long)tctdbrnum(tdb)); iprintf("size: %llu\n", (unsigned long long)tctdbfsiz(tdb)); sysprint(); if(!tctdbclose(tdb)){ eprint(tdb, __LINE__, "tctdbclose"); err = true; } tctdbdel(tdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform wicked command */ static int procwicked(const char *path, int rnum, bool mt, int opts, int omode){ iprintf("\n seed=%u path=%s rnum=%d mt=%d opts=%d omode=%d\n\n", g_randseed, path, rnum, mt, opts, omode); bool err = false; double stime = tctime(); TCTDB *tdb = tctdbnew(); if(g_dbgfd >= 0) tctdbsetdbgfd(tdb, g_dbgfd); if(mt && !tctdbsetmutex(tdb)){ eprint(tdb, __LINE__, "tctdbsetmutex"); err = true; } if(!tctdbsetcodecfunc(tdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(tdb, __LINE__, "tctdbsetcodecfunc"); err = true; } if(!tctdbtune(tdb, rnum / 50, 2, -1, opts)){ eprint(tdb, __LINE__, "tctdbtune"); err = true; } if(!tctdbsetcache(tdb, rnum / 10, 128, 256)){ eprint(tdb, __LINE__, "tctdbsetcache"); err = true; } if(!tctdbsetxmsiz(tdb, rnum * sizeof(int))){ eprint(tdb, __LINE__, "tctdbsetxmsiz"); err = true; } if(!tctdbsetinvcache(tdb, -1, 0.5)){ eprint(tdb, __LINE__, "tctdbsetinvcache"); err = true; } if(!tctdbsetdfunit(tdb, 8)){ eprint(tdb, __LINE__, "tctdbsetdfunit"); err = true; } if(!tctdbopen(tdb, path, TDBOWRITER | TDBOCREAT | TDBOTRUNC | omode)){ eprint(tdb, __LINE__, "tctdbopen"); err = true; } if(!tctdbsetindex(tdb, "", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "str", TDBITLEXICAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "num", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "type", TDBITDECIMAL)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "flag", TDBITTOKEN)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } if(!tctdbsetindex(tdb, "text", TDBITQGRAM)){ eprint(tdb, __LINE__, "tctdbsetindex"); err = true; } const char *names[] = { "", "str", "num", "type", "flag", "text", "c1" }; int ops[] = { TDBQCSTREQ, TDBQCSTRINC, TDBQCSTRBW, TDBQCSTREW, TDBQCSTRAND, TDBQCSTROR, TDBQCSTROREQ, TDBQCSTRRX, TDBQCNUMEQ, TDBQCNUMGT, TDBQCNUMGE, TDBQCNUMLT, TDBQCNUMLE, TDBQCNUMBT, TDBQCNUMOREQ }; int ftsops[] = { TDBQCFTSPH, TDBQCFTSAND, TDBQCFTSOR, TDBQCFTSEX }; int types[] = { TDBQOSTRASC, TDBQOSTRDESC, TDBQONUMASC, TDBQONUMDESC }; for(int i = 1; i <= rnum; i++){ int id = myrand(2) == 0 ? myrand(rnum) + 1 : (int)tctdbgenuid(tdb); char pkbuf[RECBUFSIZ]; int pksiz = sprintf(pkbuf, "%d", id); TCMAP *cols = tcmapnew2(7); char vbuf[RECBUFSIZ*5]; int vsiz = sprintf(vbuf, "%d", id); tcmapput(cols, "str", 3, vbuf, vsiz); if(myrand(3) == 0){ vsiz = sprintf(vbuf, "%.2f", (myrand(i * 100) + 1) / 100.0); } else { vsiz = sprintf(vbuf, "%d", myrand(i) + 1); } tcmapput(cols, "num", 3, vbuf, vsiz); vsiz = sprintf(vbuf, "%d", myrand(32) + 1); tcmapput(cols, "type", 4, vbuf, vsiz); int num = myrand(5); int pt = 0; char *wp = vbuf; for(int j = 0; j < num; j++){ pt += myrand(5) + 1; if(wp > vbuf) *(wp++) = ','; wp += sprintf(wp, "%d", pt); } *wp = '\0'; if(*vbuf != '\0'){ tcmapput(cols, "flag", 4, vbuf, wp - vbuf); tcmapput(cols, "text", 4, vbuf, wp - vbuf); } char nbuf[RECBUFSIZ]; int nsiz = sprintf(nbuf, "c%d", myrand(i) + 1); vsiz = sprintf(vbuf, "%d", myrand(i) + 1); tcmapput(cols, nbuf, nsiz, vbuf, vsiz); char *cbuf; int csiz; TCMAP *ncols; TDBQRY *qry; TCLIST *res; switch(myrand(17)){ case 0: iputchar('0'); if(!tctdbput(tdb, pkbuf, pksiz, cols)){ eprint(tdb, __LINE__, "tctdbput"); err = true; } break; case 1: iputchar('1'); cbuf = tcstrjoin4(cols, &csiz); if(!tctdbput2(tdb, pkbuf, pksiz, cbuf, csiz)){ eprint(tdb, __LINE__, "tctdbput2"); err = true; } tcfree(cbuf); break; case 2: iputchar('2'); cbuf = tcstrjoin3(cols, '\t'); if(!tctdbput3(tdb, pkbuf, cbuf)){ eprint(tdb, __LINE__, "tctdbput3"); err = true; } tcfree(cbuf); break; case 3: iputchar('3'); if(!tctdbputkeep(tdb, pkbuf, pksiz, cols) && tctdbecode(tdb) != TCEKEEP){ eprint(tdb, __LINE__, "tctdbputkeep"); err = true; } break; case 4: iputchar('4'); cbuf = tcstrjoin4(cols, &csiz); if(!tctdbputkeep2(tdb, pkbuf, pksiz, cbuf, csiz) && tctdbecode(tdb) != TCEKEEP){ eprint(tdb, __LINE__, "tctdbputkeep2"); err = true; } tcfree(cbuf); break; case 5: iputchar('5'); cbuf = tcstrjoin3(cols, '\t'); if(!tctdbputkeep3(tdb, pkbuf, cbuf) && tctdbecode(tdb) != TCEKEEP){ eprint(tdb, __LINE__, "tctdbputkeep3"); err = true; } tcfree(cbuf); break; case 6: iputchar('6'); if(!tctdbputcat(tdb, pkbuf, pksiz, cols)){ eprint(tdb, __LINE__, "tctdbputcat"); err = true; } break; case 7: iputchar('7'); cbuf = tcstrjoin4(cols, &csiz); if(!tctdbputcat2(tdb, pkbuf, pksiz, cbuf, csiz)){ eprint(tdb, __LINE__, "tctdbputcat2"); err = true; } tcfree(cbuf); break; case 8: iputchar('8'); cbuf = tcstrjoin3(cols, '\t'); if(!tctdbputcat3(tdb, pkbuf, cbuf)){ eprint(tdb, __LINE__, "tctdbputcat3"); err = true; } tcfree(cbuf); break; case 9: iputchar('9'); if(!tctdbout(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbout"); err = true; } break; case 10: iputchar('A'); if(!tctdbout2(tdb, pkbuf) && tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbout2"); err = true; } break; case 11: iputchar('B'); ncols = tctdbget(tdb, pkbuf, pksiz); if(ncols){ tcmapdel(ncols); } else if(tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbget"); err = true; } break; case 12: iputchar('C'); cbuf = tctdbget2(tdb, pkbuf, pksiz, &csiz); if(cbuf){ tcfree(cbuf); } else if(tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbget2"); err = true; } break; case 13: iputchar('D'); cbuf = tctdbget3(tdb, pkbuf); if(cbuf){ tcfree(cbuf); } else if(tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbget3"); err = true; } break; case 14: iputchar('E'); if(myrand(rnum / 128) == 0){ if(myrand(2) == 0){ if(!tctdbiterinit(tdb)){ eprint(tdb, __LINE__, "tctdbiterinit"); err = true; } } else { if(!tctdbiterinit2(tdb, pkbuf, pksiz) && tctdbecode(tdb) != TCENOREC){ eprint(tdb, __LINE__, "tctdbiterinit2"); err = true; } } } for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){ if(j % 3 == 0){ ncols = tctdbiternext3(tdb); if(ncols){ tcmapdel(ncols); } else { int ecode = tctdbecode(tdb); if(ecode != TCEINVALID && ecode != TCENOREC){ eprint(tdb, __LINE__, "tctdbiternext3"); err = true; } } } else { int iksiz; char *ikbuf = tctdbiternext(tdb, &iksiz); if(ikbuf){ tcfree(ikbuf); } else { int ecode = tctdbecode(tdb); if(ecode != TCEINVALID && ecode != TCENOREC){ eprint(tdb, __LINE__, "tctdbiternext"); err = true; } } } } break; case 15: iputchar('F'); qry = tctdbqrynew(tdb); if(myrand(10) != 0){ char expr[RECBUFSIZ]; sprintf(expr, "%d", myrand(i) + 1); switch(myrand(6)){ default: tctdbqryaddcond(qry, "str", TDBQCSTREQ, expr); break; case 1: tctdbqryaddcond(qry, "str", TDBQCSTRBW, expr); break; case 2: tctdbqryaddcond(qry, "str", TDBQCSTROREQ, expr); break; case 3: tctdbqryaddcond(qry, "num", TDBQCNUMEQ, expr); break; case 4: tctdbqryaddcond(qry, "num", TDBQCNUMGT, expr); break; case 5: tctdbqryaddcond(qry, "num", TDBQCNUMLT, expr); break; } switch(myrand(5)){ case 0: tctdbqrysetorder(qry, "str", TDBQOSTRASC); break; case 1: tctdbqrysetorder(qry, "str", TDBQOSTRDESC); break; case 2: tctdbqrysetorder(qry, "num", TDBQONUMASC); break; case 3: tctdbqrysetorder(qry, "num", TDBQONUMDESC); break; } tctdbqrysetlimit(qry, 10, myrand(10)); } else { int cnum = myrand(4); if(cnum < 1 && myrand(5) != 0) cnum = 1; for(int j = 0; j < cnum; j++){ const char *name = names[myrand(sizeof(names) / sizeof(*names))]; int op = ops[myrand(sizeof(ops) / sizeof(*ops))]; if(myrand(10) == 0) op = ftsops[myrand(sizeof(ftsops) / sizeof(*ftsops))]; if(myrand(20) == 0) op |= TDBQCNEGATE; if(myrand(20) == 0) op |= TDBQCNOIDX; char expr[RECBUFSIZ*3]; char *wp = expr; if(myrand(3) == 0){ wp += sprintf(expr, "%f", myrand(i * 100) / 100.0); } else { wp += sprintf(expr, "%d", myrand(i)); } if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); if(myrand(10) == 0) wp += sprintf(wp, ",%d", myrand(i)); tctdbqryaddcond(qry, name, op, expr); } if(myrand(3) != 0){ const char *name = names[myrand(sizeof(names) / sizeof(*names))]; int type = types[myrand(sizeof(types) / sizeof(*types))]; tctdbqrysetorder(qry, name, type); } if(myrand(3) != 0) tctdbqrysetlimit(qry, myrand(i), myrand(10)); } res = tctdbqrysearch(qry); tclistdel(res); tctdbqrydel(qry); break; default: iputchar('@'); if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX); break; } tcmapdel(cols); if(i % 50 == 0) iprintf(" (%08d)\n", i); if(i == rnum / 2){ if(!tctdbclose(tdb)){ eprint(tdb, __LINE__, "tctdbclose"); err = true; } if(!tctdbopen(tdb, path, TDBOWRITER | omode)){ eprint(tdb, __LINE__, "tctdbopen"); err = true; } } else if(i == rnum / 4){ char *npath = tcsprintf("%s-tmp", path); if(!tctdbcopy(tdb, npath)){ eprint(tdb, __LINE__, "tctdbcopy"); err = true; } TCTDB *ntdb = tctdbnew(); if(!tctdbsetcodecfunc(ntdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(ntdb, __LINE__, "tctdbsetcodecfunc"); err = true; } if(!tctdbopen(ntdb, npath, TDBOREADER | omode)){ eprint(ntdb, __LINE__, "tctdbopen"); err = true; } tctdbdel(ntdb); unlink(npath); tcfree(npath); if(!tctdboptimize(tdb, rnum / 50, -1, -1, -1)){ eprint(tdb, __LINE__, "tctdboptimize"); err = true; } if(!tctdbiterinit(tdb)){ eprint(tdb, __LINE__, "tctdbiterinit"); err = true; } } else if(i == rnum / 8){ if(!tctdbtranbegin(tdb)){ eprint(tdb, __LINE__, "tctdbtranbegin"); err = true; } } else if(i == rnum / 8 + rnum / 16){ if(!tctdbtrancommit(tdb)){ eprint(tdb, __LINE__, "tctdbtrancommit"); err = true; } } } if(!tctdbsync(tdb)){ eprint(tdb, __LINE__, "tctdbsync"); err = true; } iprintf("record number: %llu\n", (unsigned long long)tctdbrnum(tdb)); iprintf("size: %llu\n", (unsigned long long)tctdbfsiz(tdb)); sysprint(); if(!tctdbclose(tdb)){ eprint(tdb, __LINE__, "tctdbclose"); err = true; } tctdbdel(tdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } // END OF FILE tokyocabinet-1.4.48/myconf.c0000644000175000017500000002674312013574446014766 0ustar mikiomikio/************************************************************************************************* * System-dependent configurations of Tokyo Cabinet * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include "myconf.h" /************************************************************************************************* * common settings *************************************************************************************************/ int _tc_dummy_cnt = 0; int _tc_dummyfunc(void){ return 0; } int _tc_dummyfuncv(int a, ...){ return 0; } /************************************************************************************************* * for ZLIB *************************************************************************************************/ #if TCUSEZLIB #include #define ZLIBBUFSIZ 8192 static char *_tc_deflate_impl(const char *ptr, int size, int *sp, int mode); static char *_tc_inflate_impl(const char *ptr, int size, int *sp, int mode); static unsigned int _tc_getcrc_impl(const char *ptr, int size); char *(*_tc_deflate)(const char *, int, int *, int) = _tc_deflate_impl; char *(*_tc_inflate)(const char *, int, int *, int) = _tc_inflate_impl; unsigned int (*_tc_getcrc)(const char *, int) = _tc_getcrc_impl; static char *_tc_deflate_impl(const char *ptr, int size, int *sp, int mode){ assert(ptr && size >= 0 && sp); z_stream zs; zs.zalloc = Z_NULL; zs.zfree = Z_NULL; zs.opaque = Z_NULL; switch(mode){ case _TCZMRAW: if(deflateInit2(&zs, 5, Z_DEFLATED, -15, 7, Z_DEFAULT_STRATEGY) != Z_OK) return NULL; break; case _TCZMGZIP: if(deflateInit2(&zs, 6, Z_DEFLATED, 15 + 16, 9, Z_DEFAULT_STRATEGY) != Z_OK) return NULL; break; default: if(deflateInit2(&zs, 6, Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY) != Z_OK) return NULL; break; } int asiz = size + 16; if(asiz < ZLIBBUFSIZ) asiz = ZLIBBUFSIZ; char *buf; if(!(buf = MYMALLOC(asiz))){ deflateEnd(&zs); return NULL; } unsigned char obuf[ZLIBBUFSIZ]; int bsiz = 0; zs.next_in = (unsigned char *)ptr; zs.avail_in = size; zs.next_out = obuf; zs.avail_out = ZLIBBUFSIZ; int rv; while((rv = deflate(&zs, Z_FINISH)) == Z_OK){ int osiz = ZLIBBUFSIZ - zs.avail_out; if(bsiz + osiz > asiz){ asiz = asiz * 2 + osiz; char *swap; if(!(swap = MYREALLOC(buf, asiz))){ MYFREE(buf); deflateEnd(&zs); return NULL; } buf = swap; } memcpy(buf + bsiz, obuf, osiz); bsiz += osiz; zs.next_out = obuf; zs.avail_out = ZLIBBUFSIZ; } if(rv != Z_STREAM_END){ MYFREE(buf); deflateEnd(&zs); return NULL; } int osiz = ZLIBBUFSIZ - zs.avail_out; if(bsiz + osiz + 1 > asiz){ asiz = asiz * 2 + osiz; char *swap; if(!(swap = MYREALLOC(buf, asiz))){ MYFREE(buf); deflateEnd(&zs); return NULL; } buf = swap; } memcpy(buf + bsiz, obuf, osiz); bsiz += osiz; buf[bsiz] = '\0'; if(mode == _TCZMRAW) bsiz++; *sp = bsiz; deflateEnd(&zs); return buf; } static char *_tc_inflate_impl(const char *ptr, int size, int *sp, int mode){ assert(ptr && size >= 0 && sp); z_stream zs; zs.zalloc = Z_NULL; zs.zfree = Z_NULL; zs.opaque = Z_NULL; switch(mode){ case _TCZMRAW: if(inflateInit2(&zs, -15) != Z_OK) return NULL; break; case _TCZMGZIP: if(inflateInit2(&zs, 15 + 16) != Z_OK) return NULL; break; default: if(inflateInit2(&zs, 15) != Z_OK) return NULL; break; } int asiz = size * 2 + 16; if(asiz < ZLIBBUFSIZ) asiz = ZLIBBUFSIZ; char *buf; if(!(buf = MYMALLOC(asiz))){ inflateEnd(&zs); return NULL; } unsigned char obuf[ZLIBBUFSIZ]; int bsiz = 0; zs.next_in = (unsigned char *)ptr; zs.avail_in = size; zs.next_out = obuf; zs.avail_out = ZLIBBUFSIZ; int rv; while((rv = inflate(&zs, Z_NO_FLUSH)) == Z_OK){ int osiz = ZLIBBUFSIZ - zs.avail_out; if(bsiz + osiz >= asiz){ asiz = asiz * 2 + osiz; char *swap; if(!(swap = MYREALLOC(buf, asiz))){ MYFREE(buf); inflateEnd(&zs); return NULL; } buf = swap; } memcpy(buf + bsiz, obuf, osiz); bsiz += osiz; zs.next_out = obuf; zs.avail_out = ZLIBBUFSIZ; } if(rv != Z_STREAM_END){ MYFREE(buf); inflateEnd(&zs); return NULL; } int osiz = ZLIBBUFSIZ - zs.avail_out; if(bsiz + osiz >= asiz){ asiz = asiz * 2 + osiz; char *swap; if(!(swap = MYREALLOC(buf, asiz))){ MYFREE(buf); inflateEnd(&zs); return NULL; } buf = swap; } memcpy(buf + bsiz, obuf, osiz); bsiz += osiz; buf[bsiz] = '\0'; *sp = bsiz; inflateEnd(&zs); return buf; } static unsigned int _tc_getcrc_impl(const char *ptr, int size){ assert(ptr && size >= 0); int crc = crc32(0, Z_NULL, 0); return crc32(crc, (unsigned char *)ptr, size); } #else char *(*_tc_deflate)(const char *, int, int *, int) = NULL; char *(*_tc_inflate)(const char *, int, int *, int) = NULL; unsigned int (*_tc_getcrc)(const char *, int) = NULL; #endif /************************************************************************************************* * for BZIP2 *************************************************************************************************/ #if TCUSEBZIP #include #define BZIPBUFSIZ 8192 static char *_tc_bzcompress_impl(const char *ptr, int size, int *sp); static char *_tc_bzdecompress_impl(const char *ptr, int size, int *sp); char *(*_tc_bzcompress)(const char *, int, int *) = _tc_bzcompress_impl; char *(*_tc_bzdecompress)(const char *, int, int *) = _tc_bzdecompress_impl; static char *_tc_bzcompress_impl(const char *ptr, int size, int *sp){ assert(ptr && size >= 0 && sp); bz_stream zs; zs.bzalloc = NULL; zs.bzfree = NULL; zs.opaque = NULL; if(BZ2_bzCompressInit(&zs, 9, 0, 0) != BZ_OK) return NULL; int asiz = size + 16; if(asiz < BZIPBUFSIZ) asiz = BZIPBUFSIZ; char *buf; if(!(buf = MYMALLOC(asiz))){ BZ2_bzCompressEnd(&zs); return NULL; } char obuf[BZIPBUFSIZ]; int bsiz = 0; zs.next_in = (char *)ptr; zs.avail_in = size; zs.next_out = obuf; zs.avail_out = BZIPBUFSIZ; int rv; while((rv = BZ2_bzCompress(&zs, BZ_FINISH)) == BZ_FINISH_OK){ int osiz = BZIPBUFSIZ - zs.avail_out; if(bsiz + osiz > asiz){ asiz = asiz * 2 + osiz; char *swap; if(!(swap = MYREALLOC(buf, asiz))){ MYFREE(buf); BZ2_bzCompressEnd(&zs); return NULL; } buf = swap; } memcpy(buf + bsiz, obuf, osiz); bsiz += osiz; zs.next_out = obuf; zs.avail_out = BZIPBUFSIZ; } if(rv != BZ_STREAM_END){ MYFREE(buf); BZ2_bzCompressEnd(&zs); return NULL; } int osiz = BZIPBUFSIZ - zs.avail_out; if(bsiz + osiz + 1 > asiz){ asiz = asiz * 2 + osiz; char *swap; if(!(swap = MYREALLOC(buf, asiz))){ MYFREE(buf); BZ2_bzCompressEnd(&zs); return NULL; } buf = swap; } memcpy(buf + bsiz, obuf, osiz); bsiz += osiz; buf[bsiz] = '\0'; *sp = bsiz; BZ2_bzCompressEnd(&zs); return buf; } static char *_tc_bzdecompress_impl(const char *ptr, int size, int *sp){ assert(ptr && size >= 0 && sp); bz_stream zs; zs.bzalloc = NULL; zs.bzfree = NULL; zs.opaque = NULL; if(BZ2_bzDecompressInit(&zs, 0, 0) != BZ_OK) return NULL; int asiz = size * 2 + 16; if(asiz < BZIPBUFSIZ) asiz = BZIPBUFSIZ; char *buf; if(!(buf = MYMALLOC(asiz))){ BZ2_bzDecompressEnd(&zs); return NULL; } char obuf[BZIPBUFSIZ]; int bsiz = 0; zs.next_in = (char *)ptr; zs.avail_in = size; zs.next_out = obuf; zs.avail_out = BZIPBUFSIZ; int rv; while((rv = BZ2_bzDecompress(&zs)) == BZ_OK){ int osiz = BZIPBUFSIZ - zs.avail_out; if(bsiz + osiz >= asiz){ asiz = asiz * 2 + osiz; char *swap; if(!(swap = MYREALLOC(buf, asiz))){ MYFREE(buf); BZ2_bzDecompressEnd(&zs); return NULL; } buf = swap; } memcpy(buf + bsiz, obuf, osiz); bsiz += osiz; zs.next_out = obuf; zs.avail_out = BZIPBUFSIZ; } if(rv != BZ_STREAM_END){ MYFREE(buf); BZ2_bzDecompressEnd(&zs); return NULL; } int osiz = BZIPBUFSIZ - zs.avail_out; if(bsiz + osiz >= asiz){ asiz = asiz * 2 + osiz; char *swap; if(!(swap = MYREALLOC(buf, asiz))){ MYFREE(buf); BZ2_bzDecompressEnd(&zs); return NULL; } buf = swap; } memcpy(buf + bsiz, obuf, osiz); bsiz += osiz; buf[bsiz] = '\0'; *sp = bsiz; BZ2_bzDecompressEnd(&zs); return buf; } #else char *(*_tc_bzcompress)(const char *, int, int *) = NULL; char *(*_tc_bzdecompress)(const char *, int, int *) = NULL; #endif /************************************************************************************************* * for test of custom codec functions *************************************************************************************************/ #if TCUSEEXLZMA #include void *_tc_recencode(const void *ptr, int size, int *sp, void *op){ return lzma_compress(ptr, size, sp); } void *_tc_recdecode(const void *ptr, int size, int *sp, void *op){ return lzma_decompress(ptr, size, sp); } #elif TCUSEEXLZO #include bool _tc_lzo_init = false; void *_tc_recencode(const void *ptr, int size, int *sp, void *op){ if(!_tc_lzo_init){ if(lzo_init() != LZO_E_OK) return NULL; _tc_lzo_init = false; } lzo_bytep buf = MYMALLOC(size + (size >> 4) + 80); if(!buf) return NULL; lzo_uint bsiz; char wrkmem[LZO1X_1_MEM_COMPRESS]; if(lzo1x_1_compress((lzo_bytep)ptr, size, buf, &bsiz, wrkmem) != LZO_E_OK){ MYFREE(buf); return NULL; } buf[bsiz] = '\0'; *sp = bsiz; return (char *)buf; } void *_tc_recdecode(const void *ptr, int size, int *sp, void *op){ if(!_tc_lzo_init){ if(lzo_init() != LZO_E_OK) return NULL; _tc_lzo_init = false; } lzo_bytep buf; lzo_uint bsiz; int rat = 6; while(true){ bsiz = (size + 256) * rat + 3; buf = MYMALLOC(bsiz + 1); if(!buf) return NULL; int rv = lzo1x_decompress_safe((lzo_bytep)ptr, size, buf, &bsiz, NULL); if(rv == LZO_E_OK){ break; } else if(rv == LZO_E_OUTPUT_OVERRUN){ MYFREE(buf); rat *= 2; } else { MYFREE(buf); return NULL; } } buf[bsiz] = '\0'; if(sp) *sp = bsiz; return (char *)buf; } #else void *_tc_recencode(const void *ptr, int size, int *sp, void *op){ char *res = MYMALLOC(size + 1); if(!res) return NULL; memcpy(res, ptr, size); *sp = size; return res; } void *_tc_recdecode(const void *ptr, int size, int *sp, void *op){ char *res = MYMALLOC(size + 1); if(!res) return NULL; memcpy(res, ptr, size); *sp = size; return res; } #endif // END OF FILE tokyocabinet-1.4.48/tchdb.c0000644000175000017500000047622312013574446014561 0ustar mikiomikio/************************************************************************************************* * The hash database API of Tokyo Cabinet * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include "tcutil.h" #include "tchdb.h" #include "tcbdb.h" #include "myconf.h" #define HDBFILEMODE 00644 // permission of created files #define HDBIOBUFSIZ 8192 // size of an I/O buffer #define HDBMAGICDATA "ToKyO CaBiNeT" // magic data for identification #define HDBHEADSIZ 256 // size of the reagion of the header #define HDBTYPEOFF 32 // offset of the region for the database type #define HDBFLAGSOFF 33 // offset of the region for the additional flags #define HDBAPOWOFF 34 // offset of the region for the alignment power #define HDBFPOWOFF 35 // offset of the region for the free block pool power #define HDBOPTSOFF 36 // offset of the region for the options #define HDBBNUMOFF 40 // offset of the region for the bucket number #define HDBRNUMOFF 48 // offset of the region for the record number #define HDBFSIZOFF 56 // offset of the region for the file size #define HDBFRECOFF 64 // offset of the region for the first record offset #define HDBOPAQUEOFF 128 // offset of the region for the opaque field #define HDBDEFBNUM 131071 // default bucket number #define HDBDEFAPOW 4 // default alignment power #define HDBMAXAPOW 16 // maximum alignment power #define HDBDEFFPOW 10 // default free block pool power #define HDBMAXFPOW 20 // maximum free block pool power #define HDBDEFXMSIZ (64LL<<20) // default size of the extra mapped memory #define HDBXFSIZINC 32768 // increment of extra file size #define HDBMINRUNIT 48 // minimum record reading unit #define HDBMAXHSIZ 32 // maximum record header size #define HDBFBPALWRAT 2 // allowance ratio of the free block pool #define HDBFBPBSIZ 64 // base region size of the free block pool #define HDBFBPESIZ 4 // size of each region of the free block pool #define HDBFBPMGFREQ 4096 // frequency to merge the free block pool #define HDBDRPUNIT 65536 // unit size of the delayed record pool #define HDBDRPLAT 2048 // latitude size of the delayed record pool #define HDBDFRSRAT 2 // step ratio of auto defragmentation #define HDBFBMAXSIZ (INT32_MAX/4) // maximum size of a free block pool #define HDBCACHEOUT 128 // number of records in a process of cacheout #define HDBWALSUFFIX "wal" // suffix of write ahead logging file typedef struct { // type of structure for a record uint64_t off; // offset of the record uint32_t rsiz; // size of the whole record uint8_t magic; // magic number uint8_t hash; // second hash value uint64_t left; // offset of the left child record uint64_t right; // offset of the right child record uint32_t ksiz; // size of the key uint32_t vsiz; // size of the value uint16_t psiz; // size of the padding const char *kbuf; // pointer to the key const char *vbuf; // pointer to the value uint64_t boff; // offset of the body char *bbuf; // buffer of the body } TCHREC; typedef struct { // type of structure for a free block uint64_t off; // offset of the block uint32_t rsiz; // size of the block } HDBFB; enum { // enumeration for magic data HDBMAGICREC = 0xc8, // for data block HDBMAGICFB = 0xb0 // for free block }; enum { // enumeration for duplication behavior HDBPDOVER, // overwrite an existing value HDBPDKEEP, // keep the existing value HDBPDCAT, // concatenate values HDBPDADDINT, // add an integer HDBPDADDDBL, // add a real number HDBPDPROC // process by a callback function }; typedef struct { // type of structure for a duplication callback TCPDPROC proc; // function pointer void *op; // opaque pointer } HDBPDPROCOP; /* private macros */ #define HDBLOCKMETHOD(TC_hdb, TC_wr) \ ((TC_hdb)->mmtx ? tchdblockmethod((TC_hdb), (TC_wr)) : true) #define HDBUNLOCKMETHOD(TC_hdb) \ ((TC_hdb)->mmtx ? tchdbunlockmethod(TC_hdb) : true) #define HDBLOCKRECORD(TC_hdb, TC_bidx, TC_wr) \ ((TC_hdb)->mmtx ? tchdblockrecord((TC_hdb), (uint8_t)(TC_bidx), (TC_wr)) : true) #define HDBUNLOCKRECORD(TC_hdb, TC_bidx) \ ((TC_hdb)->mmtx ? tchdbunlockrecord((TC_hdb), (uint8_t)(TC_bidx)) : true) #define HDBLOCKALLRECORDS(TC_hdb, TC_wr) \ ((TC_hdb)->mmtx ? tchdblockallrecords((TC_hdb), (TC_wr)) : true) #define HDBUNLOCKALLRECORDS(TC_hdb) \ ((TC_hdb)->mmtx ? tchdbunlockallrecords(TC_hdb) : true) #define HDBLOCKDB(TC_hdb) \ ((TC_hdb)->mmtx ? tchdblockdb(TC_hdb) : true) #define HDBUNLOCKDB(TC_hdb) \ ((TC_hdb)->mmtx ? tchdbunlockdb(TC_hdb) : true) #define HDBLOCKWAL(TC_hdb) \ ((TC_hdb)->mmtx ? tchdblockwal(TC_hdb) : true) #define HDBUNLOCKWAL(TC_hdb) \ ((TC_hdb)->mmtx ? tchdbunlockwal(TC_hdb) : true) #define HDBTHREADYIELD(TC_hdb) \ do { if((TC_hdb)->mmtx) sched_yield(); } while(false) /* private function prototypes */ static uint64_t tcgetprime(uint64_t num); static bool tchdbseekwrite(TCHDB *hdb, off_t off, const void *buf, size_t size); static bool tchdbseekread(TCHDB *hdb, off_t off, void *buf, size_t size); static bool tchdbseekreadtry(TCHDB *hdb, off_t off, void *buf, size_t size); static void tchdbdumpmeta(TCHDB *hdb, char *hbuf); static void tchdbloadmeta(TCHDB *hdb, const char *hbuf); static void tchdbclear(TCHDB *hdb); static int32_t tchdbpadsize(TCHDB *hdb, uint64_t off); static void tchdbsetflag(TCHDB *hdb, int flag, bool sign); static uint64_t tchdbbidx(TCHDB *hdb, const char *kbuf, int ksiz, uint8_t *hp); static off_t tchdbgetbucket(TCHDB *hdb, uint64_t bidx); static void tchdbsetbucket(TCHDB *hdb, uint64_t bidx, uint64_t off); static bool tchdbsavefbp(TCHDB *hdb); static bool tchdbloadfbp(TCHDB *hdb); static void tcfbpsortbyoff(HDBFB *fbpool, int fbpnum); static void tcfbpsortbyrsiz(HDBFB *fbpool, int fbpnum); static void tchdbfbpmerge(TCHDB *hdb); static void tchdbfbpinsert(TCHDB *hdb, uint64_t off, uint32_t rsiz); static bool tchdbfbpsearch(TCHDB *hdb, TCHREC *rec); static bool tchdbfbpsplice(TCHDB *hdb, TCHREC *rec, uint32_t nsiz); static void tchdbfbptrim(TCHDB *hdb, uint64_t base, uint64_t next, uint64_t off, uint32_t rsiz); static bool tchdbwritefb(TCHDB *hdb, uint64_t off, uint32_t rsiz); static bool tchdbwriterec(TCHDB *hdb, TCHREC *rec, uint64_t bidx, off_t entoff); static bool tchdbreadrec(TCHDB *hdb, TCHREC *rec, char *rbuf); static bool tchdbreadrecbody(TCHDB *hdb, TCHREC *rec); static bool tchdbremoverec(TCHDB *hdb, TCHREC *rec, char *rbuf, uint64_t bidx, off_t entoff); static bool tchdbshiftrec(TCHDB *hdb, TCHREC *rec, char *rbuf, off_t destoff); static int tcreckeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz); static bool tchdbflushdrp(TCHDB *hdb); static void tchdbcacheadjust(TCHDB *hdb); static bool tchdbwalinit(TCHDB *hdb); static bool tchdbwalwrite(TCHDB *hdb, uint64_t off, int64_t size); static int tchdbwalrestore(TCHDB *hdb, const char *path); static bool tchdbwalremove(TCHDB *hdb, const char *path); static bool tchdbopenimpl(TCHDB *hdb, const char *path, int omode); static bool tchdbcloseimpl(TCHDB *hdb); static bool tchdbputimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash, const char *vbuf, int vsiz, int dmode); static void tchdbdrpappend(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz, uint8_t hash); static bool tchdbputasyncimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash, const char *vbuf, int vsiz); static bool tchdboutimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash); static char *tchdbgetimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash, int *sp); static int tchdbgetintobuf(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash, char *vbuf, int max); static char *tchdbgetnextimpl(TCHDB *hdb, const char *kbuf, int ksiz, int *sp, const char **vbp, int *vsp); static int tchdbvsizimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash); static bool tchdbiterinitimpl(TCHDB *hdb); static char *tchdbiternextimpl(TCHDB *hdb, int *sp); static bool tchdbiternextintoxstr(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr); static bool tchdboptimizeimpl(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts); static bool tchdbvanishimpl(TCHDB *hdb); static bool tchdbcopyimpl(TCHDB *hdb, const char *path); static bool tchdbdefragimpl(TCHDB *hdb, int64_t step); static bool tchdbiterjumpimpl(TCHDB *hdb, const char *kbuf, int ksiz); static bool tchdbforeachimpl(TCHDB *hdb, TCITER iter, void *op); static bool tchdblockmethod(TCHDB *hdb, bool wr); static bool tchdbunlockmethod(TCHDB *hdb); static bool tchdblockrecord(TCHDB *hdb, uint8_t bidx, bool wr); static bool tchdbunlockrecord(TCHDB *hdb, uint8_t bidx); static bool tchdblockallrecords(TCHDB *hdb, bool wr); static bool tchdbunlockallrecords(TCHDB *hdb); static bool tchdblockdb(TCHDB *hdb); static bool tchdbunlockdb(TCHDB *hdb); static bool tchdblockwal(TCHDB *hdb); static bool tchdbunlockwal(TCHDB *hdb); /* debugging function prototypes */ void tchdbprintmeta(TCHDB *hdb); void tchdbprintrec(TCHDB *hdb, TCHREC *rec); /************************************************************************************************* * API *************************************************************************************************/ /* Get the message string corresponding to an error code. */ const char *tchdberrmsg(int ecode){ return tcerrmsg(ecode); } /* Create a hash database object. */ TCHDB *tchdbnew(void){ TCHDB *hdb; TCMALLOC(hdb, sizeof(*hdb)); tchdbclear(hdb); return hdb; } /* Delete a hash database object. */ void tchdbdel(TCHDB *hdb){ assert(hdb); if(hdb->fd >= 0) tchdbclose(hdb); if(hdb->mmtx){ pthread_key_delete(*(pthread_key_t *)hdb->eckey); pthread_mutex_destroy(hdb->wmtx); pthread_mutex_destroy(hdb->dmtx); for(int i = UINT8_MAX; i >= 0; i--){ pthread_rwlock_destroy((pthread_rwlock_t *)hdb->rmtxs + i); } pthread_rwlock_destroy(hdb->mmtx); TCFREE(hdb->eckey); TCFREE(hdb->wmtx); TCFREE(hdb->dmtx); TCFREE(hdb->rmtxs); TCFREE(hdb->mmtx); } TCFREE(hdb); } /* Get the last happened error code of a hash database object. */ int tchdbecode(TCHDB *hdb){ assert(hdb); return hdb->mmtx ? (int)(intptr_t)pthread_getspecific(*(pthread_key_t *)hdb->eckey) : hdb->ecode; } /* Set mutual exclusion control of a hash database object for threading. */ bool tchdbsetmutex(TCHDB *hdb){ assert(hdb); if(!TCUSEPTHREAD) return true; if(hdb->mmtx || hdb->fd >= 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } pthread_mutexattr_t rma; pthread_mutexattr_init(&rma); TCMALLOC(hdb->mmtx, sizeof(pthread_rwlock_t)); TCMALLOC(hdb->rmtxs, (UINT8_MAX + 1) * sizeof(pthread_rwlock_t)); TCMALLOC(hdb->dmtx, sizeof(pthread_mutex_t)); TCMALLOC(hdb->wmtx, sizeof(pthread_mutex_t)); TCMALLOC(hdb->eckey, sizeof(pthread_key_t)); bool err = false; if(pthread_mutexattr_settype(&rma, PTHREAD_MUTEX_RECURSIVE) != 0) err = true; if(pthread_rwlock_init(hdb->mmtx, NULL) != 0) err = true; for(int i = 0; i <= UINT8_MAX; i++){ if(pthread_rwlock_init((pthread_rwlock_t *)hdb->rmtxs + i, NULL) != 0) err = true; } if(pthread_mutex_init(hdb->dmtx, &rma) != 0) err = true; if(pthread_mutex_init(hdb->wmtx, NULL) != 0) err = true; if(pthread_key_create(hdb->eckey, NULL) != 0) err = true; if(err){ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__); pthread_mutexattr_destroy(&rma); TCFREE(hdb->eckey); TCFREE(hdb->wmtx); TCFREE(hdb->dmtx); TCFREE(hdb->rmtxs); TCFREE(hdb->mmtx); hdb->eckey = NULL; hdb->wmtx = NULL; hdb->dmtx = NULL; hdb->rmtxs = NULL; hdb->mmtx = NULL; return false; } pthread_mutexattr_destroy(&rma); return true; } /* 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){ assert(hdb); if(hdb->fd >= 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } hdb->bnum = (bnum > 0) ? tcgetprime(bnum) : HDBDEFBNUM; hdb->apow = (apow >= 0) ? tclmin(apow, HDBMAXAPOW) : HDBDEFAPOW; hdb->fpow = (fpow >= 0) ? tclmin(fpow, HDBMAXFPOW) : HDBDEFFPOW; hdb->opts = opts; if(!_tc_deflate) hdb->opts &= ~HDBTDEFLATE; if(!_tc_bzcompress) hdb->opts &= ~HDBTBZIP; return true; } /* Set the caching parameters of a hash database object. */ bool tchdbsetcache(TCHDB *hdb, int32_t rcnum){ assert(hdb); if(hdb->fd >= 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } hdb->rcnum = (rcnum > 0) ? tclmin(tclmax(rcnum, HDBCACHEOUT * 2), INT_MAX / 4) : 0; return true; } /* Set the size of the extra mapped memory of a hash database object. */ bool tchdbsetxmsiz(TCHDB *hdb, int64_t xmsiz){ assert(hdb); if(hdb->fd >= 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } hdb->xmsiz = (xmsiz > 0) ? tcpagealign(xmsiz) : 0; return true; } /* Set the unit step number of auto defragmentation of a hash database object. */ bool tchdbsetdfunit(TCHDB *hdb, int32_t dfunit){ assert(hdb); if(hdb->fd >= 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } hdb->dfunit = (dfunit > 0) ? dfunit : 0; return true; } /* Open a database file and connect a hash database object. */ bool tchdbopen(TCHDB *hdb, const char *path, int omode){ assert(hdb && path); if(!HDBLOCKMETHOD(hdb, true)) return false; if(hdb->fd >= 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } char *rpath = tcrealpath(path); if(!rpath){ int ecode = TCEOPEN; switch(errno){ case EACCES: ecode = TCENOPERM; break; case ENOENT: ecode = TCENOFILE; break; case ENOTDIR: ecode = TCENOFILE; break; } tchdbsetecode(hdb, ecode, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(!tcpathlock(rpath)){ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__); TCFREE(rpath); HDBUNLOCKMETHOD(hdb); return false; } bool rv = tchdbopenimpl(hdb, path, omode); if(rv){ hdb->rpath = rpath; } else { tcpathunlock(rpath); TCFREE(rpath); } HDBUNLOCKMETHOD(hdb); return rv; } /* Close a database object. */ bool tchdbclose(TCHDB *hdb){ assert(hdb); if(!HDBLOCKMETHOD(hdb, true)) return false; if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } bool rv = tchdbcloseimpl(hdb); tcpathunlock(hdb->rpath); TCFREE(hdb->rpath); hdb->rpath = NULL; HDBUNLOCKMETHOD(hdb); return rv; } /* Store a record into a hash database object. */ bool tchdbput(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!HDBLOCKMETHOD(hdb, false)) return false; uint8_t hash; uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash); if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } if(!HDBLOCKRECORD(hdb, bidx, true)){ HDBUNLOCKMETHOD(hdb); return false; } if(hdb->zmode){ char *zbuf; if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzcompress(vbuf, vsiz, &vsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsencode(vbuf, vsiz, &vsiz); } else { zbuf = hdb->enc(vbuf, vsiz, &vsiz, hdb->encop); } if(!zbuf){ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); return false; } bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, zbuf, vsiz, HDBPDOVER); TCFREE(zbuf); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit && !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false; return rv; } bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, vbuf, vsiz, HDBPDOVER); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit && !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false; return rv; } /* Store a string record into a hash database object. */ bool tchdbput2(TCHDB *hdb, const char *kstr, const char *vstr){ assert(hdb && kstr && vstr); return tchdbput(hdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Store a new record into a hash database object. */ bool tchdbputkeep(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!HDBLOCKMETHOD(hdb, false)) return false; uint8_t hash; uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash); if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } if(!HDBLOCKRECORD(hdb, bidx, true)){ HDBUNLOCKMETHOD(hdb); return false; } if(hdb->zmode){ char *zbuf; if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzcompress(vbuf, vsiz, &vsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsencode(vbuf, vsiz, &vsiz); } else { zbuf = hdb->enc(vbuf, vsiz, &vsiz, hdb->encop); } if(!zbuf){ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); return false; } bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, zbuf, vsiz, HDBPDKEEP); TCFREE(zbuf); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit && !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false; return rv; } bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, vbuf, vsiz, HDBPDKEEP); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit && !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false; return rv; } /* Store a new string record into a hash database object. */ bool tchdbputkeep2(TCHDB *hdb, const char *kstr, const char *vstr){ return tchdbputkeep(hdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* 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){ assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!HDBLOCKMETHOD(hdb, false)) return false; uint8_t hash; uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash); if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } if(!HDBLOCKRECORD(hdb, bidx, true)){ HDBUNLOCKMETHOD(hdb); return false; } if(hdb->zmode){ char *zbuf; int osiz; char *obuf = tchdbgetimpl(hdb, kbuf, ksiz, bidx, hash, &osiz); if(obuf){ TCREALLOC(obuf, obuf, osiz + vsiz + 1); memcpy(obuf + osiz, vbuf, vsiz); if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_deflate(obuf, osiz + vsiz, &vsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzcompress(obuf, osiz + vsiz, &vsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsencode(obuf, osiz + vsiz, &vsiz); } else { zbuf = hdb->enc(obuf, osiz + vsiz, &vsiz, hdb->encop); } TCFREE(obuf); } else { if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzcompress(vbuf, vsiz, &vsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsencode(vbuf, vsiz, &vsiz); } else { zbuf = hdb->enc(vbuf, vsiz, &vsiz, hdb->encop); } } if(!zbuf){ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); return false; } bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, zbuf, vsiz, HDBPDOVER); TCFREE(zbuf); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit && !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false; return rv; } bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, vbuf, vsiz, HDBPDCAT); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit && !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false; return rv; } /* 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){ assert(hdb && kstr && vstr); return tchdbputcat(hdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* 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){ assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!HDBLOCKMETHOD(hdb, true)) return false; uint8_t hash; uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash); hdb->async = true; if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->zmode){ char *zbuf; if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzcompress(vbuf, vsiz, &vsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsencode(vbuf, vsiz, &vsiz); } else { zbuf = hdb->enc(vbuf, vsiz, &vsiz, hdb->encop); } if(!zbuf){ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } bool rv = tchdbputasyncimpl(hdb, kbuf, ksiz, bidx, hash, zbuf, vsiz); TCFREE(zbuf); HDBUNLOCKMETHOD(hdb); return rv; } bool rv = tchdbputasyncimpl(hdb, kbuf, ksiz, bidx, hash, vbuf, vsiz); HDBUNLOCKMETHOD(hdb); return rv; } /* Store a string record into a hash database object in asynchronous fashion. */ bool tchdbputasync2(TCHDB *hdb, const char *kstr, const char *vstr){ assert(hdb && kstr && vstr); return tchdbputasync(hdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Remove a record of a hash database object. */ bool tchdbout(TCHDB *hdb, const void *kbuf, int ksiz){ assert(hdb && kbuf && ksiz >= 0); if(!HDBLOCKMETHOD(hdb, false)) return false; uint8_t hash; uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash); if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } if(!HDBLOCKRECORD(hdb, bidx, true)){ HDBUNLOCKMETHOD(hdb); return false; } bool rv = tchdboutimpl(hdb, kbuf, ksiz, bidx, hash); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit && !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false; return rv; } /* Remove a string record of a hash database object. */ bool tchdbout2(TCHDB *hdb, const char *kstr){ assert(hdb && kstr); return tchdbout(hdb, kstr, strlen(kstr)); } /* Retrieve a record in a hash database object. */ void *tchdbget(TCHDB *hdb, const void *kbuf, int ksiz, int *sp){ assert(hdb && kbuf && ksiz >= 0 && sp); if(!HDBLOCKMETHOD(hdb, false)) return NULL; uint8_t hash; uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash); if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return NULL; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return NULL; } if(!HDBLOCKRECORD(hdb, bidx, false)){ HDBUNLOCKMETHOD(hdb); return false; } char *rv = tchdbgetimpl(hdb, kbuf, ksiz, bidx, hash, sp); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); return rv; } /* Retrieve a string record in a hash database object. */ char *tchdbget2(TCHDB *hdb, const char *kstr){ assert(hdb && kstr); int vsiz; return tchdbget(hdb, kstr, strlen(kstr), &vsiz); } /* 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){ assert(hdb && kbuf && ksiz >= 0 && vbuf && max >= 0); if(!HDBLOCKMETHOD(hdb, false)) return -1; uint8_t hash; uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash); if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return -1; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return -1; } if(!HDBLOCKRECORD(hdb, bidx, false)){ HDBUNLOCKMETHOD(hdb); return -1; } int rv = tchdbgetintobuf(hdb, kbuf, ksiz, bidx, hash, vbuf, max); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); return rv; } /* Get the size of the value of a record in a hash database object. */ int tchdbvsiz(TCHDB *hdb, const void *kbuf, int ksiz){ assert(hdb && kbuf && ksiz >= 0); if(!HDBLOCKMETHOD(hdb, false)) return -1; uint8_t hash; uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash); if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return -1; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return -1; } if(!HDBLOCKRECORD(hdb, bidx, false)){ HDBUNLOCKMETHOD(hdb); return -1; } int rv = tchdbvsizimpl(hdb, kbuf, ksiz, bidx, hash); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); return rv; } /* Get the size of the value of a string record in a hash database object. */ int tchdbvsiz2(TCHDB *hdb, const char *kstr){ assert(hdb && kstr); return tchdbvsiz(hdb, kstr, strlen(kstr)); } /* Initialize the iterator of a hash database object. */ bool tchdbiterinit(TCHDB *hdb){ assert(hdb); if(!HDBLOCKMETHOD(hdb, true)) return false; if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } bool rv = tchdbiterinitimpl(hdb); HDBUNLOCKMETHOD(hdb); return rv; } /* Get the next key of the iterator of a hash database object. */ void *tchdbiternext(TCHDB *hdb, int *sp){ assert(hdb && sp); if(!HDBLOCKMETHOD(hdb, true)) return NULL; if(hdb->fd < 0 || hdb->iter < 1){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return NULL; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return NULL; } char *rv = tchdbiternextimpl(hdb, sp); HDBUNLOCKMETHOD(hdb); return rv; } /* Get the next key string of the iterator of a hash database object. */ char *tchdbiternext2(TCHDB *hdb){ assert(hdb); int vsiz; return tchdbiternext(hdb, &vsiz); } /* Get the next extensible objects of the iterator of a hash database object. */ bool tchdbiternext3(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr){ assert(hdb && kxstr && vxstr); if(!HDBLOCKMETHOD(hdb, true)) return false; if(hdb->fd < 0 || hdb->iter < 1){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } bool rv = tchdbiternextintoxstr(hdb, kxstr, vxstr); HDBUNLOCKMETHOD(hdb); return rv; } /* Get forward matching keys in a hash database object. */ TCLIST *tchdbfwmkeys(TCHDB *hdb, const void *pbuf, int psiz, int max){ assert(hdb && pbuf && psiz >= 0); TCLIST* keys = tclistnew(); if(!HDBLOCKMETHOD(hdb, true)) return keys; if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return keys; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return keys; } if(max < 0) max = INT_MAX; uint64_t iter = hdb->iter; tchdbiterinitimpl(hdb); char *kbuf; int ksiz; while(TCLISTNUM(keys) < max && (kbuf = tchdbiternextimpl(hdb, &ksiz)) != NULL){ if(ksiz >= psiz && !memcmp(kbuf, pbuf, psiz)){ tclistpushmalloc(keys, kbuf, ksiz); } else { TCFREE(kbuf); } } hdb->iter = iter; HDBUNLOCKMETHOD(hdb); return keys; } /* Get forward matching string keys in a hash database object. */ TCLIST *tchdbfwmkeys2(TCHDB *hdb, const char *pstr, int max){ assert(hdb && pstr); return tchdbfwmkeys(hdb, pstr, strlen(pstr), max); } /* Add an integer to a record in a hash database object. */ int tchdbaddint(TCHDB *hdb, const void *kbuf, int ksiz, int num){ assert(hdb && kbuf && ksiz >= 0); if(!HDBLOCKMETHOD(hdb, false)) return INT_MIN; uint8_t hash; uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash); if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return INT_MIN; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return INT_MIN; } if(!HDBLOCKRECORD(hdb, bidx, true)){ HDBUNLOCKMETHOD(hdb); return INT_MIN; } if(hdb->zmode){ char *zbuf; int osiz; char *obuf = tchdbgetimpl(hdb, kbuf, ksiz, bidx, hash, &osiz); if(obuf){ if(osiz != sizeof(num)){ tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__); TCFREE(obuf); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); return INT_MIN; } num += *(int *)obuf; TCFREE(obuf); } int zsiz; if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_deflate((char *)&num, sizeof(num), &zsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzcompress((char *)&num, sizeof(num), &zsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsencode((char *)&num, sizeof(num), &zsiz); } else { zbuf = hdb->enc((char *)&num, sizeof(num), &zsiz, hdb->encop); } if(!zbuf){ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); return INT_MIN; } bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, zbuf, zsiz, HDBPDOVER); TCFREE(zbuf); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit && !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false; return rv ? num : INT_MIN; } bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, (char *)&num, sizeof(num), HDBPDADDINT); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit && !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false; return rv ? num : INT_MIN; } /* Add a real number to a record in a hash database object. */ double tchdbadddouble(TCHDB *hdb, const void *kbuf, int ksiz, double num){ assert(hdb && kbuf && ksiz >= 0); if(!HDBLOCKMETHOD(hdb, false)) return nan(""); uint8_t hash; uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash); if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return nan(""); } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return nan(""); } if(!HDBLOCKRECORD(hdb, bidx, true)){ HDBUNLOCKMETHOD(hdb); return nan(""); } if(hdb->zmode){ char *zbuf; int osiz; char *obuf = tchdbgetimpl(hdb, kbuf, ksiz, bidx, hash, &osiz); if(obuf){ if(osiz != sizeof(num)){ tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__); TCFREE(obuf); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); return nan(""); } num += *(double *)obuf; TCFREE(obuf); } int zsiz; if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_deflate((char *)&num, sizeof(num), &zsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzcompress((char *)&num, sizeof(num), &zsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsencode((char *)&num, sizeof(num), &zsiz); } else { zbuf = hdb->enc((char *)&num, sizeof(num), &zsiz, hdb->encop); } if(!zbuf){ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); return nan(""); } bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, zbuf, zsiz, HDBPDOVER); TCFREE(zbuf); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit && !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false; return rv ? num : nan(""); } bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, (char *)&num, sizeof(num), HDBPDADDDBL); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit && !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false; return rv ? num : nan(""); } /* Synchronize updated contents of a hash database object with the file and the device. */ bool tchdbsync(TCHDB *hdb){ assert(hdb); if(!HDBLOCKMETHOD(hdb, true)) return false; if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER) || hdb->tran){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } bool rv = tchdbmemsync(hdb, true); HDBUNLOCKMETHOD(hdb); return rv; } /* Optimize the file of a hash database object. */ bool tchdboptimize(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){ assert(hdb); if(!HDBLOCKMETHOD(hdb, true)) return false; if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER) || hdb->tran){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } HDBTHREADYIELD(hdb); bool rv = tchdboptimizeimpl(hdb, bnum, apow, fpow, opts); HDBUNLOCKMETHOD(hdb); return rv; } /* Remove all records of a hash database object. */ bool tchdbvanish(TCHDB *hdb){ assert(hdb); if(!HDBLOCKMETHOD(hdb, true)) return false; if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER) || hdb->tran){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } HDBTHREADYIELD(hdb); bool rv = tchdbvanishimpl(hdb); HDBUNLOCKMETHOD(hdb); return rv; } /* Copy the database file of a hash database object. */ bool tchdbcopy(TCHDB *hdb, const char *path){ assert(hdb && path); if(!HDBLOCKMETHOD(hdb, false)) return false; if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } if(!HDBLOCKALLRECORDS(hdb, false)){ HDBUNLOCKMETHOD(hdb); return false; } HDBTHREADYIELD(hdb); bool rv = tchdbcopyimpl(hdb, path); HDBUNLOCKALLRECORDS(hdb); HDBUNLOCKMETHOD(hdb); return rv; } /* Begin the transaction of a hash database object. */ bool tchdbtranbegin(TCHDB *hdb){ assert(hdb); for(double wsec = 1.0 / sysconf(_SC_CLK_TCK); true; wsec *= 2){ if(!HDBLOCKMETHOD(hdb, true)) return false; if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER) || hdb->fatal){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(!hdb->tran) break; HDBUNLOCKMETHOD(hdb); if(wsec > 1.0) wsec = 1.0; tcsleep(wsec); } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } if(!tchdbmemsync(hdb, false)){ HDBUNLOCKMETHOD(hdb); return false; } if((hdb->omode & HDBOTSYNC) && fsync(hdb->fd) == -1){ tchdbsetecode(hdb, TCESYNC, __FILE__, __LINE__, __func__); return false; } if(hdb->walfd < 0){ char *tpath = tcsprintf("%s%c%s", hdb->path, MYEXTCHR, HDBWALSUFFIX); int walfd = open(tpath, O_RDWR | O_CREAT | O_TRUNC, HDBFILEMODE); TCFREE(tpath); if(walfd < 0){ int ecode = TCEOPEN; switch(errno){ case EACCES: ecode = TCENOPERM; break; case ENOENT: ecode = TCENOFILE; break; case ENOTDIR: ecode = TCENOFILE; break; } tchdbsetecode(hdb, ecode, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } hdb->walfd = walfd; } tchdbsetflag(hdb, HDBFOPEN, false); if(!tchdbwalinit(hdb)){ tchdbsetflag(hdb, HDBFOPEN, true); HDBUNLOCKMETHOD(hdb); return false; } tchdbsetflag(hdb, HDBFOPEN, true); hdb->tran = true; HDBUNLOCKMETHOD(hdb); return true; } /* Commit the transaction of a hash database object. */ bool tchdbtrancommit(TCHDB *hdb){ assert(hdb); if(!HDBLOCKMETHOD(hdb, true)) return false; if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER) || hdb->fatal || !hdb->tran){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } bool err = false; if(hdb->async && !tchdbflushdrp(hdb)) err = true; if(!tchdbmemsync(hdb, hdb->omode & HDBOTSYNC)) err = true; if(!err && ftruncate(hdb->walfd, 0) == -1){ tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__); err = true; } hdb->tran = false; HDBUNLOCKMETHOD(hdb); return !err; } /* Abort the transaction of a hash database object. */ bool tchdbtranabort(TCHDB *hdb){ assert(hdb); if(!HDBLOCKMETHOD(hdb, true)) return false; if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER) || !hdb->tran){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } bool err = false; if(hdb->async && !tchdbflushdrp(hdb)) err = true; if(!tchdbmemsync(hdb, false)) err = true; if(!tchdbwalrestore(hdb, hdb->path)) err = true; char hbuf[HDBHEADSIZ]; if(lseek(hdb->fd, 0, SEEK_SET) == -1){ tchdbsetecode(hdb, TCESEEK, __FILE__, __LINE__, __func__); err = false; } else if(!tcread(hdb->fd, hbuf, HDBHEADSIZ)){ tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__); err = false; } else { tchdbloadmeta(hdb, hbuf); } hdb->dfcur = hdb->frec; hdb->iter = 0; hdb->xfsiz = 0; hdb->fbpnum = 0; if(hdb->recc) tcmdbvanish(hdb->recc); hdb->tran = false; HDBUNLOCKMETHOD(hdb); return !err; } /* Get the file path of a hash database object. */ const char *tchdbpath(TCHDB *hdb){ assert(hdb); if(!HDBLOCKMETHOD(hdb, false)) return NULL; if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return NULL; } const char *rv = hdb->path; HDBUNLOCKMETHOD(hdb); return rv; } /* Get the number of records of a hash database object. */ uint64_t tchdbrnum(TCHDB *hdb){ assert(hdb); if(!HDBLOCKMETHOD(hdb, false)) return 0; if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return 0; } uint64_t rv = hdb->rnum; HDBUNLOCKMETHOD(hdb); return rv; } /* Get the size of the database file of a hash database object. */ uint64_t tchdbfsiz(TCHDB *hdb){ assert(hdb); if(!HDBLOCKMETHOD(hdb, false)) return 0; if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return 0; } uint64_t rv = hdb->fsiz; HDBUNLOCKMETHOD(hdb); return rv; } /************************************************************************************************* * features for experts *************************************************************************************************/ /* Set the error code of a hash database object. */ void tchdbsetecode(TCHDB *hdb, int ecode, const char *filename, int line, const char *func){ assert(hdb && filename && line >= 1 && func); int myerrno = errno; if(!hdb->fatal){ if(hdb->mmtx){ pthread_setspecific(*(pthread_key_t *)hdb->eckey, (void *)(intptr_t)ecode); } else { hdb->ecode = ecode; } } if(ecode != TCESUCCESS && ecode != TCEINVALID && ecode != TCEKEEP && ecode != TCENOREC){ hdb->fatal = true; if(hdb->fd >= 0 && (hdb->omode & HDBOWRITER)) tchdbsetflag(hdb, HDBFFATAL, true); } if(hdb->dbgfd >= 0 && (hdb->dbgfd != UINT16_MAX || hdb->fatal)){ int dbgfd = (hdb->dbgfd == UINT16_MAX) ? 1 : hdb->dbgfd; char obuf[HDBIOBUFSIZ]; int osiz = sprintf(obuf, "ERROR:%s:%d:%s:%s:%d:%s:%d:%s\n", filename, line, func, hdb->path ? hdb->path : "-", ecode, tchdberrmsg(ecode), myerrno, strerror(myerrno)); tcwrite(dbgfd, obuf, osiz); } } /* Set the type of a hash database object. */ void tchdbsettype(TCHDB *hdb, uint8_t type){ assert(hdb); if(hdb->fd >= 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return; } hdb->type = type; } /* Set the file descriptor for debugging output. */ void tchdbsetdbgfd(TCHDB *hdb, int fd){ assert(hdb && fd >= 0); hdb->dbgfd = fd; } /* Get the file descriptor for debugging output. */ int tchdbdbgfd(TCHDB *hdb){ assert(hdb); return hdb->dbgfd; } /* Check whether mutual exclusion control is set to a hash database object. */ bool tchdbhasmutex(TCHDB *hdb){ assert(hdb); return hdb->mmtx != NULL; } /* Synchronize updating contents on memory of a hash database object. */ bool tchdbmemsync(TCHDB *hdb, bool phys){ assert(hdb); if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } bool err = false; char hbuf[HDBHEADSIZ]; tchdbdumpmeta(hdb, hbuf); memcpy(hdb->map, hbuf, HDBOPAQUEOFF); if(phys){ size_t xmsiz = (hdb->xmsiz > hdb->msiz) ? hdb->xmsiz : hdb->msiz; if(msync(hdb->map, xmsiz, MS_SYNC) == -1){ tchdbsetecode(hdb, TCEMMAP, __FILE__, __LINE__, __func__); err = true; } if(fsync(hdb->fd) == -1){ tchdbsetecode(hdb, TCESYNC, __FILE__, __LINE__, __func__); err = true; } } return !err; } /* Get the number of elements of the bucket array of a hash database object. */ uint64_t tchdbbnum(TCHDB *hdb){ assert(hdb); if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return hdb->bnum; } /* Get the record alignment a hash database object. */ uint32_t tchdbalign(TCHDB *hdb){ assert(hdb); if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return hdb->align; } /* Get the maximum number of the free block pool of a a hash database object. */ uint32_t tchdbfbpmax(TCHDB *hdb){ assert(hdb); if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return hdb->fbpmax; } /* Get the size of the extra mapped memory of a hash database object. */ uint64_t tchdbxmsiz(TCHDB *hdb){ assert(hdb); if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return hdb->xmsiz; } /* Get the inode number of the database file of a hash database object. */ uint64_t tchdbinode(TCHDB *hdb){ assert(hdb); if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return hdb->inode; } /* Get the modification time of the database file of a hash database object. */ time_t tchdbmtime(TCHDB *hdb){ assert(hdb); if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return hdb->mtime; } /* Get the connection mode of a hash database object. */ int tchdbomode(TCHDB *hdb){ assert(hdb); if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return hdb->omode; } /* Get the database type of a hash database object. */ uint8_t tchdbtype(TCHDB *hdb){ assert(hdb); if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return hdb->type; } /* Get the additional flags of a hash database object. */ uint8_t tchdbflags(TCHDB *hdb){ assert(hdb); if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return hdb->flags; } /* Get the options of a hash database object. */ uint8_t tchdbopts(TCHDB *hdb){ assert(hdb); if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return hdb->opts; } /* Get the pointer to the opaque field of a hash database object. */ char *tchdbopaque(TCHDB *hdb){ assert(hdb); if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return NULL; } return hdb->map + HDBOPAQUEOFF; } /* Get the number of used elements of the bucket array of a hash database object. */ uint64_t tchdbbnumused(TCHDB *hdb){ assert(hdb); if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } uint64_t unum = 0; if(hdb->ba64){ uint64_t *buckets = hdb->ba64; for(int i = 0; i < hdb->bnum; i++){ if(buckets[i]) unum++; } } else { uint32_t *buckets = hdb->ba32; for(int i = 0; i < hdb->bnum; i++){ if(buckets[i]) unum++; } } return unum; } /* Set the custom codec functions of a hash database object. */ bool tchdbsetcodecfunc(TCHDB *hdb, TCCODEC enc, void *encop, TCCODEC dec, void *decop){ assert(hdb && enc && dec); if(!HDBLOCKMETHOD(hdb, true)) return false; if(hdb->fd >= 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } hdb->enc = enc; hdb->encop = encop; hdb->dec = dec; hdb->decop = decop; HDBUNLOCKMETHOD(hdb); return true; } /* Get the unit step number of auto defragmentation of a hash database object. */ uint32_t tchdbdfunit(TCHDB *hdb){ assert(hdb); return hdb->dfunit; } /* Perform dynamic defragmentation of a hash database object. */ bool tchdbdefrag(TCHDB *hdb, int64_t step){ assert(hdb); if(step > 0){ if(!HDBLOCKMETHOD(hdb, true)) return false; if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } bool rv = tchdbdefragimpl(hdb, step); HDBUNLOCKMETHOD(hdb); return rv; } if(!HDBLOCKMETHOD(hdb, false)) return false; if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } bool err = false; if(HDBLOCKALLRECORDS(hdb, true)){ hdb->dfcur = hdb->frec; HDBUNLOCKALLRECORDS(hdb); } else { err = true; } bool stop = false; while(!err && !stop){ if(HDBLOCKALLRECORDS(hdb, true)){ uint64_t cur = hdb->dfcur; if(!tchdbdefragimpl(hdb, UINT8_MAX)) err = true; if(hdb->dfcur <= cur) stop = true; HDBUNLOCKALLRECORDS(hdb); HDBTHREADYIELD(hdb); } else { err = true; } } HDBUNLOCKMETHOD(hdb); return !err; } /* Clear the cache of a hash tree database object. */ bool tchdbcacheclear(TCHDB *hdb){ assert(hdb); if(!HDBLOCKMETHOD(hdb, true)) return false; if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } HDBTHREADYIELD(hdb); if(hdb->recc) tcmdbvanish(hdb->recc); HDBUNLOCKMETHOD(hdb); return true; } /* Store a record into a hash database object with a duplication handler. */ bool tchdbputproc(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op){ assert(hdb && kbuf && ksiz >= 0 && proc); if(!HDBLOCKMETHOD(hdb, false)) return false; uint8_t hash; uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash); if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER)){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } if(!HDBLOCKRECORD(hdb, bidx, true)){ HDBUNLOCKMETHOD(hdb); return false; } if(hdb->zmode){ char *zbuf; int osiz; char *obuf = tchdbgetimpl(hdb, kbuf, ksiz, bidx, hash, &osiz); if(obuf){ int nsiz; char *nbuf = proc(obuf, osiz, &nsiz, op); if(nbuf == (void *)-1){ bool rv = tchdboutimpl(hdb, kbuf, ksiz, bidx, hash); TCFREE(obuf); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); return rv; } else if(nbuf){ if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_deflate(nbuf, nsiz, &vsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzcompress(nbuf, nsiz, &vsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsencode(nbuf, nsiz, &vsiz); } else { zbuf = hdb->enc(nbuf, nsiz, &vsiz, hdb->encop); } TCFREE(nbuf); } else { zbuf = NULL; } TCFREE(obuf); } else if(vbuf){ if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_deflate(vbuf, vsiz, &vsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzcompress(vbuf, vsiz, &vsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsencode(vbuf, vsiz, &vsiz); } else { zbuf = hdb->enc(vbuf, vsiz, &vsiz, hdb->encop); } } else { tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); return false; } if(!zbuf){ tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); return false; } bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, zbuf, vsiz, HDBPDOVER); TCFREE(zbuf); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit && !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false; return rv; } HDBPDPROCOP procop; procop.proc = proc; procop.op = op; HDBPDPROCOP *procptr = &procop; tcgeneric_t stack[(TCNUMBUFSIZ*2)/sizeof(tcgeneric_t)+1]; char *rbuf; if(ksiz <= sizeof(stack) - sizeof(procptr)){ rbuf = (char *)stack; } else { TCMALLOC(rbuf, ksiz + sizeof(procptr)); } char *wp = rbuf; memcpy(wp, &procptr, sizeof(procptr)); wp += sizeof(procptr); memcpy(wp, kbuf, ksiz); kbuf = rbuf + sizeof(procptr); bool rv = tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, vbuf, vsiz, HDBPDPROC); if(rbuf != (char *)stack) TCFREE(rbuf); HDBUNLOCKRECORD(hdb, bidx); HDBUNLOCKMETHOD(hdb); if(hdb->dfunit > 0 && hdb->dfcnt > hdb->dfunit && !tchdbdefrag(hdb, hdb->dfunit * HDBDFRSRAT + 1)) rv = false; return rv; } /* Get the custom codec functions of a hash database object. */ void tchdbcodecfunc(TCHDB *hdb, TCCODEC *ep, void **eop, TCCODEC *dp, void **dop){ assert(hdb && ep && eop && dp && dop); *ep = hdb->enc; *eop = hdb->encop; *dp = hdb->dec; *dop = hdb->decop; } /* Retrieve the next record of a record in a hash database object. */ void *tchdbgetnext(TCHDB *hdb, const void *kbuf, int ksiz, int *sp){ assert(hdb && sp); if(!HDBLOCKMETHOD(hdb, true)) return NULL; if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return NULL; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return NULL; } char *rv = tchdbgetnextimpl(hdb, kbuf, ksiz, sp, NULL, NULL); HDBUNLOCKMETHOD(hdb); return rv; } /* Retrieve the next record of a string record in a hash database object. */ char *tchdbgetnext2(TCHDB *hdb, const char *kstr){ assert(hdb); int vsiz; return tchdbgetnext(hdb, kstr, strlen(kstr), &vsiz); } /* Retrieve the key and the value of the next record of a record in a hash database object. */ char *tchdbgetnext3(TCHDB *hdb, const char *kbuf, int ksiz, int *sp, const char **vbp, int *vsp){ assert(hdb && sp && vbp && vsp); if(!HDBLOCKMETHOD(hdb, true)) return NULL; if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return NULL; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return NULL; } char *rv = tchdbgetnextimpl(hdb, kbuf, ksiz, sp, vbp, vsp); HDBUNLOCKMETHOD(hdb); return rv; } /* Move the iterator to the record corresponding a key of a hash database object. */ bool tchdbiterinit2(TCHDB *hdb, const void *kbuf, int ksiz){ assert(hdb && kbuf && ksiz >= 0); if(!HDBLOCKMETHOD(hdb, true)) return false; if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } bool rv = tchdbiterjumpimpl(hdb, kbuf, ksiz); HDBUNLOCKMETHOD(hdb); return rv; } /* Move the iterator to the record corresponding a key string of a hash database object. */ bool tchdbiterinit3(TCHDB *hdb, const char *kstr){ assert(hdb && kstr); return tchdbiterinit2(hdb, kstr, strlen(kstr)); } /* Process each record atomically of a hash database object. */ bool tchdbforeach(TCHDB *hdb, TCITER iter, void *op){ assert(hdb && iter); if(!HDBLOCKMETHOD(hdb, false)) return false; if(hdb->fd < 0){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } if(hdb->async && !tchdbflushdrp(hdb)){ HDBUNLOCKMETHOD(hdb); return false; } if(!HDBLOCKALLRECORDS(hdb, false)){ HDBUNLOCKMETHOD(hdb); return false; } HDBTHREADYIELD(hdb); bool rv = tchdbforeachimpl(hdb, iter, op); HDBUNLOCKALLRECORDS(hdb); HDBUNLOCKMETHOD(hdb); return rv; } /* Void the transaction of a hash database object. */ bool tchdbtranvoid(TCHDB *hdb){ assert(hdb); if(!HDBLOCKMETHOD(hdb, true)) return false; if(hdb->fd < 0 || !(hdb->omode & HDBOWRITER) || hdb->fatal || !hdb->tran){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); HDBUNLOCKMETHOD(hdb); return false; } hdb->tran = false; HDBUNLOCKMETHOD(hdb); return true; } /************************************************************************************************* * private features *************************************************************************************************/ /* Get a natural prime number not less than a floor number. `num' specified the floor number. The return value is a prime number not less than the floor number. */ static uint64_t tcgetprime(uint64_t num){ uint64_t primes[] = { 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 43, 47, 53, 59, 61, 71, 79, 83, 89, 103, 109, 113, 127, 139, 157, 173, 191, 199, 223, 239, 251, 283, 317, 349, 383, 409, 443, 479, 509, 571, 631, 701, 761, 829, 887, 953, 1021, 1151, 1279, 1399, 1531, 1663, 1789, 1913, 2039, 2297, 2557, 2803, 3067, 3323, 3583, 3833, 4093, 4603, 5119, 5623, 6143, 6653, 7159, 7673, 8191, 9209, 10223, 11261, 12281, 13309, 14327, 15359, 16381, 18427, 20479, 22511, 24571, 26597, 28669, 30713, 32749, 36857, 40949, 45053, 49139, 53239, 57331, 61417, 65521, 73727, 81919, 90107, 98299, 106487, 114679, 122869, 131071, 147451, 163819, 180221, 196597, 212987, 229373, 245759, 262139, 294911, 327673, 360439, 393209, 425977, 458747, 491503, 524287, 589811, 655357, 720887, 786431, 851957, 917503, 982981, 1048573, 1179641, 1310719, 1441771, 1572853, 1703903, 1835003, 1966079, 2097143, 2359267, 2621431, 2883577, 3145721, 3407857, 3670013, 3932153, 4194301, 4718579, 5242877, 5767129, 6291449, 6815741, 7340009, 7864301, 8388593, 9437179, 10485751, 11534329, 12582893, 13631477, 14680063, 15728611, 16777213, 18874367, 20971507, 23068667, 25165813, 27262931, 29360087, 31457269, 33554393, 37748717, 41943023, 46137319, 50331599, 54525917, 58720253, 62914549, 67108859, 75497467, 83886053, 92274671, 100663291, 109051903, 117440509, 125829103, 134217689, 150994939, 167772107, 184549373, 201326557, 218103799, 234881011, 251658227, 268435399, 301989881, 335544301, 369098707, 402653171, 436207613, 469762043, 503316469, 536870909, 603979769, 671088637, 738197503, 805306357, 872415211, 939524087, 1006632947, 1073741789, 1207959503, 1342177237, 1476394991, 1610612711, 1744830457, 1879048183, 2013265907, 2576980349, 3092376431, 3710851741, 4718021527, 6133428047, 7973456459, 10365493393, 13475141413, 17517683831, 22772988923, 29604885677, 38486351381, 50032256819, 65041933867, 84554514043, 109920868241, 153889215497, 0 }; int i; for(i = 0; primes[i] > 0; i++){ if(num <= primes[i]) return primes[i]; } return primes[i-1]; } /* Seek and write data into a file. `hdb' specifies the hash database object. `off' specifies the offset of the region to seek. `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. */ static bool tchdbseekwrite(TCHDB *hdb, off_t off, const void *buf, size_t size){ assert(hdb && off >= 0 && buf && size >= 0); if(hdb->tran && !tchdbwalwrite(hdb, off, size)) return false; off_t end = off + size; if(end <= hdb->xmsiz){ if(end >= hdb->fsiz && end >= hdb->xfsiz){ uint64_t xfsiz = end + HDBXFSIZINC; if(ftruncate(hdb->fd, xfsiz) == -1){ tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__); return false; } hdb->xfsiz = xfsiz; } memcpy(hdb->map + off, buf, size); return true; } if(!TCUBCACHE && off < hdb->xmsiz){ if(end >= hdb->fsiz && end >= hdb->xfsiz){ uint64_t xfsiz = end + HDBXFSIZINC; if(ftruncate(hdb->fd, xfsiz) == -1){ tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__); return false; } hdb->xfsiz = xfsiz; } int head = hdb->xmsiz - off; memcpy(hdb->map + off, buf, head); off += head; buf = (char *)buf + head; size -= head; } while(true){ int wb = pwrite(hdb->fd, buf, size, off); if(wb >= size){ return true; } else if(wb > 0){ buf = (char *)buf + wb; size -= wb; off += wb; } else if(wb == -1){ if(errno != EINTR){ tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__); return false; } } else { if(size > 0){ tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__); return false; } } } return true; } /* Seek and read data from a file. `hdb' specifies the hash database object. `off' specifies the offset of the region to seek. `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. */ static bool tchdbseekread(TCHDB *hdb, off_t off, void *buf, size_t size){ assert(hdb && off >= 0 && buf && size >= 0); if(off + size <= hdb->xmsiz){ memcpy(buf, hdb->map + off, size); return true; } if(!TCUBCACHE && off < hdb->xmsiz){ int head = hdb->xmsiz - off; memcpy(buf, hdb->map + off, head); off += head; buf = (char *)buf + head; size -= head; } while(true){ int rb = pread(hdb->fd, buf, size, off); if(rb >= size){ break; } else if(rb > 0){ buf = (char *)buf + rb; size -= rb; off += rb; } else if(rb == -1){ if(errno != EINTR){ tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__); return false; } } else { if(size > 0){ tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__); return false; } } } return true; } /* Try to seek and read data from a file. `hdb' specifies the hash database object. `off' specifies the offset of the region to seek. `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. */ static bool tchdbseekreadtry(TCHDB *hdb, off_t off, void *buf, size_t size){ assert(hdb && off >= 0 && buf && size >= 0); off_t end = off + size; if(end > hdb->fsiz) return false; if(end <= hdb->xmsiz){ memcpy(buf, hdb->map + off, size); return true; } if(!TCUBCACHE && off < hdb->xmsiz){ int head = hdb->xmsiz - off; memcpy(buf, hdb->map + off, head); off += head; buf = (char *)buf + head; size -= head; } int rb = pread(hdb->fd, buf, size, off); if(rb == size) return true; if(rb == -1) tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__); return false; } /* Serialize meta data into a buffer. `hdb' specifies the hash database object. `hbuf' specifies the buffer. */ static void tchdbdumpmeta(TCHDB *hdb, char *hbuf){ memset(hbuf, 0, HDBHEADSIZ); sprintf(hbuf, "%s\n%s:%d\n", HDBMAGICDATA, _TC_FORMATVER, _TC_LIBVER); memcpy(hbuf + HDBTYPEOFF, &(hdb->type), sizeof(hdb->type)); memcpy(hbuf + HDBFLAGSOFF, &(hdb->flags), sizeof(hdb->flags)); memcpy(hbuf + HDBAPOWOFF, &(hdb->apow), sizeof(hdb->apow)); memcpy(hbuf + HDBFPOWOFF, &(hdb->fpow), sizeof(hdb->fpow)); memcpy(hbuf + HDBOPTSOFF, &(hdb->opts), sizeof(hdb->opts)); uint64_t llnum; llnum = hdb->bnum; llnum = TCHTOILL(llnum); memcpy(hbuf + HDBBNUMOFF, &llnum, sizeof(llnum)); llnum = hdb->rnum; llnum = TCHTOILL(llnum); memcpy(hbuf + HDBRNUMOFF, &llnum, sizeof(llnum)); llnum = hdb->fsiz; llnum = TCHTOILL(llnum); memcpy(hbuf + HDBFSIZOFF, &llnum, sizeof(llnum)); llnum = hdb->frec; llnum = TCHTOILL(llnum); memcpy(hbuf + HDBFRECOFF, &llnum, sizeof(llnum)); } /* Deserialize meta data from a buffer. `hdb' specifies the hash database object. `hbuf' specifies the buffer. */ static void tchdbloadmeta(TCHDB *hdb, const char *hbuf){ memcpy(&(hdb->type), hbuf + HDBTYPEOFF, sizeof(hdb->type)); memcpy(&(hdb->flags), hbuf + HDBFLAGSOFF, sizeof(hdb->flags)); memcpy(&(hdb->apow), hbuf + HDBAPOWOFF, sizeof(hdb->apow)); memcpy(&(hdb->fpow), hbuf + HDBFPOWOFF, sizeof(hdb->fpow)); memcpy(&(hdb->opts), hbuf + HDBOPTSOFF, sizeof(hdb->opts)); uint64_t llnum; memcpy(&llnum, hbuf + HDBBNUMOFF, sizeof(llnum)); hdb->bnum = TCITOHLL(llnum); memcpy(&llnum, hbuf + HDBRNUMOFF, sizeof(llnum)); hdb->rnum = TCITOHLL(llnum); memcpy(&llnum, hbuf + HDBFSIZOFF, sizeof(llnum)); hdb->fsiz = TCITOHLL(llnum); memcpy(&llnum, hbuf + HDBFRECOFF, sizeof(llnum)); hdb->frec = TCITOHLL(llnum); } /* Clear all members. `hdb' specifies the hash database object. */ static void tchdbclear(TCHDB *hdb){ assert(hdb); hdb->mmtx = NULL; hdb->rmtxs = NULL; hdb->dmtx = NULL; hdb->wmtx = NULL; hdb->eckey = NULL; hdb->rpath = NULL; hdb->type = TCDBTHASH; hdb->flags = 0; hdb->bnum = HDBDEFBNUM; hdb->apow = HDBDEFAPOW; hdb->fpow = HDBDEFFPOW; hdb->opts = 0; hdb->path = NULL; hdb->fd = -1; hdb->omode = 0; hdb->rnum = 0; hdb->fsiz = 0; hdb->frec = 0; hdb->dfcur = 0; hdb->iter = 0; hdb->map = NULL; hdb->msiz = 0; hdb->xmsiz = HDBDEFXMSIZ; hdb->xfsiz = 0; hdb->ba32 = NULL; hdb->ba64 = NULL; hdb->align = 0; hdb->runit = 0; hdb->zmode = false; hdb->fbpmax = 0; hdb->fbpool = NULL; hdb->fbpnum = 0; hdb->fbpmis = 0; hdb->async = false; hdb->drpool = NULL; hdb->drpdef = NULL; hdb->drpoff = 0; hdb->recc = NULL; hdb->rcnum = 0; hdb->enc = NULL; hdb->encop = NULL; hdb->dec = NULL; hdb->decop = NULL; hdb->ecode = TCESUCCESS; hdb->fatal = false; hdb->inode = 0; hdb->mtime = 0; hdb->dfunit = 0; hdb->dfcnt = 0; hdb->tran = false; hdb->walfd = -1; hdb->walend = 0; hdb->dbgfd = -1; hdb->cnt_writerec = -1; hdb->cnt_reuserec = -1; hdb->cnt_moverec = -1; hdb->cnt_readrec = -1; hdb->cnt_searchfbp = -1; hdb->cnt_insertfbp = -1; hdb->cnt_splicefbp = -1; hdb->cnt_dividefbp = -1; hdb->cnt_mergefbp = -1; hdb->cnt_reducefbp = -1; hdb->cnt_appenddrp = -1; hdb->cnt_deferdrp = -1; hdb->cnt_flushdrp = -1; hdb->cnt_adjrecc = -1; hdb->cnt_defrag = -1; hdb->cnt_shiftrec = -1; hdb->cnt_trunc = -1; TCDODEBUG(hdb->cnt_writerec = 0); TCDODEBUG(hdb->cnt_reuserec = 0); TCDODEBUG(hdb->cnt_moverec = 0); TCDODEBUG(hdb->cnt_readrec = 0); TCDODEBUG(hdb->cnt_searchfbp = 0); TCDODEBUG(hdb->cnt_insertfbp = 0); TCDODEBUG(hdb->cnt_splicefbp = 0); TCDODEBUG(hdb->cnt_dividefbp = 0); TCDODEBUG(hdb->cnt_mergefbp = 0); TCDODEBUG(hdb->cnt_reducefbp = 0); TCDODEBUG(hdb->cnt_appenddrp = 0); TCDODEBUG(hdb->cnt_deferdrp = 0); TCDODEBUG(hdb->cnt_flushdrp = 0); TCDODEBUG(hdb->cnt_adjrecc = 0); TCDODEBUG(hdb->cnt_defrag = 0); TCDODEBUG(hdb->cnt_shiftrec = 0); TCDODEBUG(hdb->cnt_trunc = 0); } /* Get the padding size to record alignment. `hdb' specifies the hash database object. `off' specifies the current offset. The return value is the padding size. */ static int32_t tchdbpadsize(TCHDB *hdb, uint64_t off){ assert(hdb); int32_t diff = off & (hdb->align - 1); return (diff > 0) ? hdb->align - diff : 0; } /* Set the open flag. `hdb' specifies the hash database object. `flag' specifies the flag value. `sign' specifies the sign. */ static void tchdbsetflag(TCHDB *hdb, int flag, bool sign){ assert(hdb); char *fp = (char *)hdb->map + HDBFLAGSOFF; if(sign){ *fp |= (uint8_t)flag; } else { *fp &= ~(uint8_t)flag; } hdb->flags = *fp; } /* Get the bucket index of a record. `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. `hp' specifies the pointer to the variable into which the second hash value is assigned. The return value is the bucket index. */ static uint64_t tchdbbidx(TCHDB *hdb, const char *kbuf, int ksiz, uint8_t *hp){ assert(hdb && kbuf && ksiz >= 0 && hp); uint64_t idx = 19780211; uint32_t hash = 751; const char *rp = kbuf + ksiz; while(ksiz--){ idx = idx * 37 + *(uint8_t *)kbuf++; hash = (hash * 31) ^ *(uint8_t *)--rp; } *hp = hash; return idx % hdb->bnum; } /* Get the offset of the record of a bucket element. `hdb' specifies the hash database object. `bidx' specifies the index of the bucket. The return value is the offset of the record. */ static off_t tchdbgetbucket(TCHDB *hdb, uint64_t bidx){ assert(hdb && bidx >= 0); if(hdb->ba64){ uint64_t llnum = hdb->ba64[bidx]; return TCITOHLL(llnum) << hdb->apow; } uint32_t lnum = hdb->ba32[bidx]; return (off_t)TCITOHL(lnum) << hdb->apow; } /* Get the offset of the record of a bucket element. `hdb' specifies the hash database object. `bidx' specifies the index of the record. `off' specifies the offset of the record. */ static void tchdbsetbucket(TCHDB *hdb, uint64_t bidx, uint64_t off){ assert(hdb && bidx >= 0); if(hdb->ba64){ uint64_t llnum = off >> hdb->apow; if(hdb->tran) tchdbwalwrite(hdb, HDBHEADSIZ + bidx * sizeof(llnum), sizeof(llnum)); hdb->ba64[bidx] = TCHTOILL(llnum); } else { uint32_t lnum = off >> hdb->apow; if(hdb->tran) tchdbwalwrite(hdb, HDBHEADSIZ + bidx * sizeof(lnum), sizeof(lnum)); hdb->ba32[bidx] = TCHTOIL(lnum); } } /* Load the free block pool from the file. The return value is true if successful, else, it is false. */ static bool tchdbsavefbp(TCHDB *hdb){ assert(hdb); if(hdb->fbpnum > hdb->fbpmax){ tchdbfbpmerge(hdb); } else if(hdb->fbpnum > 1){ tcfbpsortbyoff(hdb->fbpool, hdb->fbpnum); } int bsiz = hdb->frec - hdb->msiz; char *buf; TCMALLOC(buf, bsiz); char *wp = buf; HDBFB *cur = hdb->fbpool; HDBFB *end = cur + hdb->fbpnum; uint64_t base = 0; bsiz -= sizeof(HDBFB) + sizeof(uint8_t) + sizeof(uint8_t); while(cur < end && bsiz > 0){ uint64_t noff = cur->off >> hdb->apow; int step; uint64_t llnum = noff - base; TCSETVNUMBUF64(step, wp, llnum); wp += step; bsiz -= step; uint32_t lnum = cur->rsiz >> hdb->apow; TCSETVNUMBUF(step, wp, lnum); wp += step; bsiz -= step; base = noff; cur++; } *(wp++) = '\0'; *(wp++) = '\0'; if(!tchdbseekwrite(hdb, hdb->msiz, buf, wp - buf)){ TCFREE(buf); return false; } TCFREE(buf); return true; } /* Save the free block pool into the file. The return value is true if successful, else, it is false. */ static bool tchdbloadfbp(TCHDB *hdb){ int bsiz = hdb->frec - hdb->msiz; char *buf; TCMALLOC(buf, bsiz); if(!tchdbseekread(hdb, hdb->msiz, buf, bsiz)){ TCFREE(buf); return false; } const char *rp = buf; HDBFB *cur = hdb->fbpool; HDBFB *end = cur + hdb->fbpmax * HDBFBPALWRAT; uint64_t base = 0; while(cur < end && *rp != '\0'){ int step; uint64_t llnum; TCREADVNUMBUF64(rp, llnum, step); base += llnum << hdb->apow; cur->off = base; rp += step; uint32_t lnum; TCREADVNUMBUF(rp, lnum, step); cur->rsiz = lnum << hdb->apow; rp += step; cur++; } hdb->fbpnum = cur - (HDBFB *)hdb->fbpool; TCFREE(buf); tcfbpsortbyrsiz(hdb->fbpool, hdb->fbpnum); return true; } /* Sort the free block pool by offset. `fbpool' specifies the free block pool. `fbpnum' specifies the number of blocks. */ static void tcfbpsortbyoff(HDBFB *fbpool, int fbpnum){ assert(fbpool && fbpnum >= 0); fbpnum--; int bottom = fbpnum / 2 + 1; int top = fbpnum; while(bottom > 0){ bottom--; int mybot = bottom; int i = mybot * 2; while(i <= top){ if(i < top && fbpool[i+1].off > fbpool[i].off) i++; if(fbpool[mybot].off >= fbpool[i].off) break; HDBFB swap = fbpool[mybot]; fbpool[mybot] = fbpool[i]; fbpool[i] = swap; mybot = i; i = mybot * 2; } } while(top > 0){ HDBFB swap = fbpool[0]; fbpool[0] = fbpool[top]; fbpool[top] = swap; top--; int mybot = bottom; int i = mybot * 2; while(i <= top){ if(i < top && fbpool[i+1].off > fbpool[i].off) i++; if(fbpool[mybot].off >= fbpool[i].off) break; swap = fbpool[mybot]; fbpool[mybot] = fbpool[i]; fbpool[i] = swap; mybot = i; i = mybot * 2; } } } /* Sort the free block pool by record size. `fbpool' specifies the free block pool. `fbpnum' specifies the number of blocks. */ static void tcfbpsortbyrsiz(HDBFB *fbpool, int fbpnum){ assert(fbpool && fbpnum >= 0); fbpnum--; int bottom = fbpnum / 2 + 1; int top = fbpnum; while(bottom > 0){ bottom--; int mybot = bottom; int i = mybot * 2; while(i <= top){ if(i < top && fbpool[i+1].rsiz > fbpool[i].rsiz) i++; if(fbpool[mybot].rsiz >= fbpool[i].rsiz) break; HDBFB swap = fbpool[mybot]; fbpool[mybot] = fbpool[i]; fbpool[i] = swap; mybot = i; i = mybot * 2; } } while(top > 0){ HDBFB swap = fbpool[0]; fbpool[0] = fbpool[top]; fbpool[top] = swap; top--; int mybot = bottom; int i = mybot * 2; while(i <= top){ if(i < top && fbpool[i+1].rsiz > fbpool[i].rsiz) i++; if(fbpool[mybot].rsiz >= fbpool[i].rsiz) break; swap = fbpool[mybot]; fbpool[mybot] = fbpool[i]; fbpool[i] = swap; mybot = i; i = mybot * 2; } } } /* Merge successive records in the free block pool. `hdb' specifies the hash database object. */ static void tchdbfbpmerge(TCHDB *hdb){ assert(hdb); TCDODEBUG(hdb->cnt_mergefbp++); tcfbpsortbyoff(hdb->fbpool, hdb->fbpnum); HDBFB *wp = hdb->fbpool; HDBFB *cur = wp; HDBFB *end = wp + hdb->fbpnum - 1; while(cur < end){ if(cur->off > 0){ HDBFB *next = cur + 1; if(cur->off + cur->rsiz == next->off && cur->rsiz + next->rsiz <= HDBFBMAXSIZ){ if(hdb->dfcur == next->off) hdb->dfcur += next->rsiz; if(hdb->iter == next->off) hdb->iter += next->rsiz; cur->rsiz += next->rsiz; next->off = 0; } *(wp++) = *cur; } cur++; } if(end->off > 0) *(wp++) = *end; hdb->fbpnum = wp - (HDBFB *)hdb->fbpool; hdb->fbpmis = hdb->fbpnum * -1; } /* Insert a block into the free block pool. `hdb' specifies the hash database object. `off' specifies the offset of the block. `rsiz' specifies the size of the block. */ static void tchdbfbpinsert(TCHDB *hdb, uint64_t off, uint32_t rsiz){ assert(hdb && off > 0 && rsiz > 0); TCDODEBUG(hdb->cnt_insertfbp++); hdb->dfcnt++; if(hdb->fpow < 1) return; HDBFB *pv = hdb->fbpool; if(hdb->fbpnum >= hdb->fbpmax * HDBFBPALWRAT){ tchdbfbpmerge(hdb); tcfbpsortbyrsiz(hdb->fbpool, hdb->fbpnum); int diff = hdb->fbpnum - hdb->fbpmax; if(diff > 0){ TCDODEBUG(hdb->cnt_reducefbp++); memmove(pv, pv + diff, (hdb->fbpnum - diff) * sizeof(*pv)); hdb->fbpnum -= diff; } hdb->fbpmis = 0; } int num = hdb->fbpnum; int left = 0; int right = num; int i = (left + right) / 2; int cand = -1; while(right >= left && i < num){ int rv = (int)rsiz - (int)pv[i].rsiz; if(rv == 0){ cand = i; break; } else if(rv <= 0){ cand = i; right = i - 1; } else { left = i + 1; } i = (left + right) / 2; } if(cand >= 0){ pv += cand; memmove(pv + 1, pv, sizeof(*pv) * (num - cand)); } else { pv += num; } pv->off = off; pv->rsiz = rsiz; hdb->fbpnum++; } /* Search the free block pool for the minimum region. `hdb' specifies the hash database object. `rec' specifies the record object to be stored. The return value is true if successful, else, it is false. */ static bool tchdbfbpsearch(TCHDB *hdb, TCHREC *rec){ assert(hdb && rec); TCDODEBUG(hdb->cnt_searchfbp++); if(hdb->fbpnum < 1){ rec->off = hdb->fsiz; rec->rsiz = 0; return true; } uint32_t rsiz = rec->rsiz; HDBFB *pv = hdb->fbpool; int num = hdb->fbpnum; int left = 0; int right = num; int i = (left + right) / 2; int cand = -1; while(right >= left && i < num){ int rv = (int)rsiz - (int)pv[i].rsiz; if(rv == 0){ cand = i; break; } else if(rv <= 0){ cand = i; right = i - 1; } else { left = i + 1; } i = (left + right) / 2; } if(cand >= 0){ pv += cand; if(pv->rsiz > rsiz * 2){ uint32_t psiz = tchdbpadsize(hdb, pv->off + rsiz); uint64_t noff = pv->off + rsiz + psiz; if(pv->rsiz >= (noff - pv->off) * 2){ TCDODEBUG(hdb->cnt_dividefbp++); rec->off = pv->off; rec->rsiz = noff - pv->off; pv->off = noff; pv->rsiz -= rec->rsiz; return tchdbwritefb(hdb, pv->off, pv->rsiz); } } rec->off = pv->off; rec->rsiz = pv->rsiz; memmove(pv, pv + 1, sizeof(*pv) * (num - cand - 1)); hdb->fbpnum--; return true; } rec->off = hdb->fsiz; rec->rsiz = 0; hdb->fbpmis++; if(hdb->fbpmis >= HDBFBPMGFREQ){ tchdbfbpmerge(hdb); tcfbpsortbyrsiz(hdb->fbpool, hdb->fbpnum); } return true; } /* Splice the trailing free block `hdb' specifies the hash database object. `rec' specifies the record object to be stored. `nsiz' specifies the needed size. The return value is whether splicing succeeded or not. */ static bool tchdbfbpsplice(TCHDB *hdb, TCHREC *rec, uint32_t nsiz){ assert(hdb && rec && nsiz > 0); if(hdb->mmtx){ if(hdb->fbpnum < 1) return false; uint64_t off = rec->off + rec->rsiz; uint32_t rsiz = rec->rsiz; uint8_t magic; if(tchdbseekreadtry(hdb, off, &magic, sizeof(magic)) && magic != HDBMAGICFB) return false; HDBFB *pv = hdb->fbpool; HDBFB *ep = pv + hdb->fbpnum; while(pv < ep){ if(pv->off == off && rsiz + pv->rsiz >= nsiz){ if(hdb->dfcur == pv->off) hdb->dfcur += pv->rsiz; if(hdb->iter == pv->off) hdb->iter += pv->rsiz; rec->rsiz += pv->rsiz; memmove(pv, pv + 1, sizeof(*pv) * ((ep - pv) - 1)); hdb->fbpnum--; return true; } pv++; } return false; } uint64_t off = rec->off + rec->rsiz; TCHREC nrec; char nbuf[HDBIOBUFSIZ]; while(off < hdb->fsiz){ nrec.off = off; if(!tchdbreadrec(hdb, &nrec, nbuf)) return false; if(nrec.magic != HDBMAGICFB) break; if(hdb->dfcur == off) hdb->dfcur += nrec.rsiz; if(hdb->iter == off) hdb->iter += nrec.rsiz; off += nrec.rsiz; } uint32_t jsiz = off - rec->off; if(jsiz < nsiz) return false; rec->rsiz = jsiz; uint64_t base = rec->off; HDBFB *wp = hdb->fbpool; HDBFB *cur = wp; HDBFB *end = wp + hdb->fbpnum; while(cur < end){ if(cur->off < base || cur->off > off) *(wp++) = *cur; cur++; } hdb->fbpnum = wp - (HDBFB *)hdb->fbpool; if(jsiz > nsiz * 2){ uint32_t psiz = tchdbpadsize(hdb, rec->off + nsiz); uint64_t noff = rec->off + nsiz + psiz; if(jsiz >= (noff - rec->off) * 2){ TCDODEBUG(hdb->cnt_dividefbp++); nsiz = off - noff; if(!tchdbwritefb(hdb, noff, nsiz)) return false; rec->rsiz = noff - rec->off; tchdbfbpinsert(hdb, noff, nsiz); } } return true; } /* Remove blocks of a region from the free block pool. `hdb' specifies the hash database object. `base' specifies the base offset of the region. `next' specifies the offset of the next region. `off' specifies the offset of the block. `rsiz' specifies the size of the block. */ static void tchdbfbptrim(TCHDB *hdb, uint64_t base, uint64_t next, uint64_t off, uint32_t rsiz){ assert(hdb && base > 0 && next > 0); if(hdb->fpow < 1) return; if(hdb->fbpnum < 1){ if(off > 0){ HDBFB *fbpool = hdb->fbpool; fbpool->off = off; fbpool->rsiz = rsiz; hdb->fbpnum = 1; } return; } HDBFB *wp = hdb->fbpool; HDBFB *cur = wp; HDBFB *end = wp + hdb->fbpnum; if(hdb->fbpnum >= hdb->fbpmax * HDBFBPALWRAT) cur++; while(cur < end){ if(cur->rsiz >= rsiz && off > 0){ TCDODEBUG(hdb->cnt_insertfbp++); wp->off = off; wp->rsiz = rsiz; wp++; off = 0; } else if(cur->off < base || cur->off >= next){ *(wp++) = *cur; } cur++; } if(off > 0){ TCDODEBUG(hdb->cnt_insertfbp++); wp->off = off; wp->rsiz = rsiz; wp++; off = 0; } hdb->fbpnum = wp - (HDBFB *)hdb->fbpool; } /* Write a free block into the file. `hdb' specifies the hash database object. `off' specifies the offset of the block. `rsiz' specifies the size of the block. The return value is true if successful, else, it is false. */ static bool tchdbwritefb(TCHDB *hdb, uint64_t off, uint32_t rsiz){ assert(hdb && off > 0 && rsiz > 0); char rbuf[HDBMAXHSIZ]; char *wp = rbuf; *(uint8_t *)(wp++) = HDBMAGICFB; uint32_t lnum = TCHTOIL(rsiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); if(!tchdbseekwrite(hdb, off, rbuf, wp - rbuf)) return false; return true; } /* Write a record into the file. `hdb' specifies the hash database object. `rec' specifies the record object. `bidx' specifies the index of the bucket. `entoff' specifies the offset of the tree entry. The return value is true if successful, else, it is false. */ static bool tchdbwriterec(TCHDB *hdb, TCHREC *rec, uint64_t bidx, off_t entoff){ assert(hdb && rec); TCDODEBUG(hdb->cnt_writerec++); char stack[HDBIOBUFSIZ]; int bsiz = (rec->rsiz > 0) ? rec->rsiz : HDBMAXHSIZ + rec->ksiz + rec->vsiz + hdb->align; char *rbuf; if(bsiz <= HDBIOBUFSIZ){ rbuf = stack; } else { TCMALLOC(rbuf, bsiz); } char *wp = rbuf; *(uint8_t *)(wp++) = HDBMAGICREC; *(uint8_t *)(wp++) = rec->hash; if(hdb->ba64){ uint64_t llnum; llnum = rec->left >> hdb->apow; llnum = TCHTOILL(llnum); memcpy(wp, &llnum, sizeof(llnum)); wp += sizeof(llnum); llnum = rec->right >> hdb->apow; llnum = TCHTOILL(llnum); memcpy(wp, &llnum, sizeof(llnum)); wp += sizeof(llnum); } else { uint32_t lnum; lnum = rec->left >> hdb->apow; lnum = TCHTOIL(lnum); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); lnum = rec->right >> hdb->apow; lnum = TCHTOIL(lnum); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); } uint16_t snum; char *pwp = wp; wp += sizeof(snum); int step; TCSETVNUMBUF(step, wp, rec->ksiz); wp += step; TCSETVNUMBUF(step, wp, rec->vsiz); wp += step; int32_t hsiz = wp - rbuf; int32_t rsiz = hsiz + rec->ksiz + rec->vsiz; int32_t finc = 0; if(rec->rsiz < 1){ uint16_t psiz = tchdbpadsize(hdb, hdb->fsiz + rsiz); rec->rsiz = rsiz + psiz; rec->psiz = psiz; finc = rec->rsiz; } else if(rsiz > rec->rsiz){ if(rbuf != stack) TCFREE(rbuf); if(!HDBLOCKDB(hdb)) return false; if(tchdbfbpsplice(hdb, rec, rsiz)){ TCDODEBUG(hdb->cnt_splicefbp++); bool rv = tchdbwriterec(hdb, rec, bidx, entoff); HDBUNLOCKDB(hdb); return rv; } TCDODEBUG(hdb->cnt_moverec++); if(!tchdbwritefb(hdb, rec->off, rec->rsiz)){ HDBUNLOCKDB(hdb); return false; } tchdbfbpinsert(hdb, rec->off, rec->rsiz); rec->rsiz = rsiz; if(!tchdbfbpsearch(hdb, rec)){ HDBUNLOCKDB(hdb); return false; } bool rv = tchdbwriterec(hdb, rec, bidx, entoff); HDBUNLOCKDB(hdb); return rv; } else { TCDODEBUG(hdb->cnt_reuserec++); uint32_t psiz = rec->rsiz - rsiz; if(psiz > UINT16_MAX){ TCDODEBUG(hdb->cnt_dividefbp++); psiz = tchdbpadsize(hdb, rec->off + rsiz); uint64_t noff = rec->off + rsiz + psiz; uint32_t nsiz = rec->rsiz - rsiz - psiz; rec->rsiz = noff - rec->off; rec->psiz = psiz; if(!tchdbwritefb(hdb, noff, nsiz)){ if(rbuf != stack) TCFREE(rbuf); return false; } if(!HDBLOCKDB(hdb)){ if(rbuf != stack) TCFREE(rbuf); return false; } tchdbfbpinsert(hdb, noff, nsiz); HDBUNLOCKDB(hdb); } rec->psiz = psiz; } snum = rec->psiz; snum = TCHTOIS(snum); memcpy(pwp, &snum, sizeof(snum)); rsiz = rec->rsiz; rsiz -= hsiz; memcpy(wp, rec->kbuf, rec->ksiz); wp += rec->ksiz; rsiz -= rec->ksiz; memcpy(wp, rec->vbuf, rec->vsiz); wp += rec->vsiz; rsiz -= rec->vsiz; memset(wp, 0, rsiz); if(!tchdbseekwrite(hdb, rec->off, rbuf, rec->rsiz)){ if(rbuf != stack) TCFREE(rbuf); return false; } if(finc != 0){ hdb->fsiz += finc; uint64_t llnum = hdb->fsiz; llnum = TCHTOILL(llnum); memcpy(hdb->map + HDBFSIZOFF, &llnum, sizeof(llnum)); } if(rbuf != stack) TCFREE(rbuf); if(entoff > 0){ if(hdb->ba64){ uint64_t llnum = rec->off >> hdb->apow; llnum = TCHTOILL(llnum); if(!tchdbseekwrite(hdb, entoff, &llnum, sizeof(uint64_t))) return false; } else { uint32_t lnum = rec->off >> hdb->apow; lnum = TCHTOIL(lnum); if(!tchdbseekwrite(hdb, entoff, &lnum, sizeof(uint32_t))) return false; } } else { tchdbsetbucket(hdb, bidx, rec->off); } return true; } /* Read a record from the file. `hdb' specifies the hash database object. `rec' specifies the record object. `rbuf' specifies the buffer for reading. The return value is true if successful, else, it is false. */ static bool tchdbreadrec(TCHDB *hdb, TCHREC *rec, char *rbuf){ assert(hdb && rec && rbuf); TCDODEBUG(hdb->cnt_readrec++); int rsiz = hdb->runit; if(!tchdbseekreadtry(hdb, rec->off, rbuf, rsiz)){ if(!HDBLOCKDB(hdb)) return false; rsiz = hdb->fsiz - rec->off; if(rsiz > hdb->runit){ rsiz = hdb->runit; } else if(rsiz < (int)(sizeof(uint8_t) + sizeof(uint32_t))){ tchdbsetecode(hdb, TCERHEAD, __FILE__, __LINE__, __func__); HDBUNLOCKDB(hdb); return false; } if(!tchdbseekread(hdb, rec->off, rbuf, rsiz)){ HDBUNLOCKDB(hdb); return false; } HDBUNLOCKDB(hdb); } const char *rp = rbuf; rec->magic = *(uint8_t *)(rp++); if(rec->magic == HDBMAGICFB){ uint32_t lnum; memcpy(&lnum, rp, sizeof(lnum)); rec->rsiz = TCITOHL(lnum); return true; } else if(rec->magic != HDBMAGICREC){ tchdbsetecode(hdb, TCERHEAD, __FILE__, __LINE__, __func__); return false; } rec->hash = *(uint8_t *)(rp++); if(hdb->ba64){ uint64_t llnum; memcpy(&llnum, rp, sizeof(llnum)); rec->left = TCITOHLL(llnum) << hdb->apow; rp += sizeof(llnum); memcpy(&llnum, rp, sizeof(llnum)); rec->right = TCITOHLL(llnum) << hdb->apow; rp += sizeof(llnum); } else { uint32_t lnum; memcpy(&lnum, rp, sizeof(lnum)); rec->left = (uint64_t)TCITOHL(lnum) << hdb->apow; rp += sizeof(lnum); memcpy(&lnum, rp, sizeof(lnum)); rec->right = (uint64_t)TCITOHL(lnum) << hdb->apow; rp += sizeof(lnum); } uint16_t snum; memcpy(&snum, rp, sizeof(snum)); rec->psiz = TCITOHS(snum); rp += sizeof(snum); uint32_t lnum; int step; TCREADVNUMBUF(rp, lnum, step); rec->ksiz = lnum; rp += step; TCREADVNUMBUF(rp, lnum, step); rec->vsiz = lnum; rp += step; int32_t hsiz = rp - rbuf; rec->rsiz = hsiz + rec->ksiz + rec->vsiz + rec->psiz; rec->kbuf = NULL; rec->vbuf = NULL; rec->boff = rec->off + hsiz; rec->bbuf = NULL; rsiz -= hsiz; if(rsiz >= rec->ksiz){ rec->kbuf = rp; rsiz -= rec->ksiz; rp += rec->ksiz; if(rsiz >= rec->vsiz) rec->vbuf = rp; } return true; } /* Read the body of a record from the file. `hdb' specifies the hash database object. `rec' specifies the record object. The return value is true if successful, else, it is false. */ static bool tchdbreadrecbody(TCHDB *hdb, TCHREC *rec){ assert(hdb && rec); int32_t bsiz = rec->ksiz + rec->vsiz; TCMALLOC(rec->bbuf, bsiz + 1); if(!tchdbseekread(hdb, rec->boff, rec->bbuf, bsiz)) return false; rec->kbuf = rec->bbuf; rec->vbuf = rec->bbuf + rec->ksiz; return true; } /* Remove a record from the file. `hdb' specifies the hash database object. `rec' specifies the record object. `rbuf' specifies the buffer for reading. `bidx' specifies the index of the bucket. `entoff' specifies the offset of the tree entry. The return value is true if successful, else, it is false. */ static bool tchdbremoverec(TCHDB *hdb, TCHREC *rec, char *rbuf, uint64_t bidx, off_t entoff){ assert(hdb && rec); if(!tchdbwritefb(hdb, rec->off, rec->rsiz)) return false; if(!HDBLOCKDB(hdb)) return false; tchdbfbpinsert(hdb, rec->off, rec->rsiz); HDBUNLOCKDB(hdb); uint64_t child; if(rec->left > 0 && rec->right < 1){ child = rec->left; } else if(rec->left < 1 && rec->right > 0){ child = rec->right; } else if(rec->left < 1){ child = 0; } else { child = rec->left; uint64_t right = rec->right; rec->right = child; while(rec->right > 0){ rec->off = rec->right; if(!tchdbreadrec(hdb, rec, rbuf)) return false; } if(hdb->ba64){ off_t toff = rec->off + (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint64_t)); uint64_t llnum = right >> hdb->apow; llnum = TCHTOILL(llnum); if(!tchdbseekwrite(hdb, toff, &llnum, sizeof(uint64_t))) return false; } else { off_t toff = rec->off + (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t)); uint32_t lnum = right >> hdb->apow; lnum = TCHTOIL(lnum); if(!tchdbseekwrite(hdb, toff, &lnum, sizeof(uint32_t))) return false; } } if(entoff > 0){ if(hdb->ba64){ uint64_t llnum = child >> hdb->apow; llnum = TCHTOILL(llnum); if(!tchdbseekwrite(hdb, entoff, &llnum, sizeof(uint64_t))) return false; } else { uint32_t lnum = child >> hdb->apow; lnum = TCHTOIL(lnum); if(!tchdbseekwrite(hdb, entoff, &lnum, sizeof(uint32_t))) return false; } } else { tchdbsetbucket(hdb, bidx, child); } if(!HDBLOCKDB(hdb)) return false; hdb->rnum--; uint64_t llnum = hdb->rnum; llnum = TCHTOILL(llnum); memcpy(hdb->map + HDBRNUMOFF, &llnum, sizeof(llnum)); HDBUNLOCKDB(hdb); return true; } /* Remove a record from the file. `hdb' specifies the hash database object. `rec' specifies the record object. `rbuf' specifies the buffer for reading. `destoff' specifies the offset of the destination. The return value is true if successful, else, it is false. */ static bool tchdbshiftrec(TCHDB *hdb, TCHREC *rec, char *rbuf, off_t destoff){ assert(hdb && rec && rbuf && destoff >= 0); TCDODEBUG(hdb->cnt_shiftrec++); if(!rec->vbuf && !tchdbreadrecbody(hdb, rec)) return false; uint8_t hash; uint64_t bidx = tchdbbidx(hdb, rec->kbuf, rec->ksiz, &hash); off_t off = tchdbgetbucket(hdb, bidx); if(rec->off == off){ bool err = false; rec->off = destoff; if(!tchdbwriterec(hdb, rec, bidx, 0)) err = true; TCFREE(rec->bbuf); rec->kbuf = NULL; rec->vbuf = NULL; rec->bbuf = NULL; return !err; } TCHREC trec; char tbuf[HDBIOBUFSIZ]; char *bbuf = rec->bbuf; const char *kbuf = rec->kbuf; int ksiz = rec->ksiz; const char *vbuf = rec->vbuf; int vsiz = rec->vsiz; rec->kbuf = NULL; rec->vbuf = NULL; rec->bbuf = NULL; off_t entoff = 0; while(off > 0){ trec.off = off; if(!tchdbreadrec(hdb, &trec, tbuf)){ TCFREE(bbuf); return false; } if(hash > trec.hash){ off = trec.left; entoff = trec.off + (sizeof(uint8_t) + sizeof(uint8_t)); } else if(hash < trec.hash){ off = trec.right; entoff = trec.off + (sizeof(uint8_t) + sizeof(uint8_t)) + (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t)); } else { if(!trec.kbuf && !tchdbreadrecbody(hdb, &trec)){ TCFREE(bbuf); return false; } int kcmp = tcreckeycmp(kbuf, ksiz, trec.kbuf, trec.ksiz); if(kcmp > 0){ off = trec.left; TCFREE(trec.bbuf); trec.kbuf = NULL; trec.bbuf = NULL; entoff = trec.off + (sizeof(uint8_t) + sizeof(uint8_t)); } else if(kcmp < 0){ off = trec.right; TCFREE(trec.bbuf); trec.kbuf = NULL; trec.bbuf = NULL; entoff = trec.off + (sizeof(uint8_t) + sizeof(uint8_t)) + (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t)); } else { TCFREE(trec.bbuf); trec.bbuf = NULL; bool err = false; rec->off = destoff; rec->kbuf = kbuf; rec->ksiz = ksiz; rec->vbuf = vbuf; rec->vsiz = vsiz; if(!tchdbwriterec(hdb, rec, bidx, entoff)) err = true; TCFREE(bbuf); return !err; } } } TCFREE(bbuf); tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } /* Compare keys of two records. `abuf' specifies the pointer to the region of the former. `asiz' specifies the size of the region. `bbuf' specifies the pointer to the region of the latter. `bsiz' specifies the size of the region. The return value is 0 if two equals, positive if the formar is big, else, negative. */ static int tcreckeycmp(const char *abuf, int asiz, const char *bbuf, int bsiz){ assert(abuf && asiz >= 0 && bbuf && bsiz >= 0); if(asiz > bsiz) return 1; if(asiz < bsiz) return -1; return memcmp(abuf, bbuf, asiz); } /* Flush the delayed record pool. `hdb' specifies the hash database object. The return value is true if successful, else, it is false. */ static bool tchdbflushdrp(TCHDB *hdb){ assert(hdb); if(!HDBLOCKDB(hdb)) return false; if(!hdb->drpool){ HDBUNLOCKDB(hdb); return true; } TCDODEBUG(hdb->cnt_flushdrp++); if(!tchdbseekwrite(hdb, hdb->drpoff, TCXSTRPTR(hdb->drpool), TCXSTRSIZE(hdb->drpool))){ HDBUNLOCKDB(hdb); return false; } const char *rp = TCXSTRPTR(hdb->drpdef); int size = TCXSTRSIZE(hdb->drpdef); while(size > 0){ int ksiz, vsiz; memcpy(&ksiz, rp, sizeof(int)); rp += sizeof(int); memcpy(&vsiz, rp, sizeof(int)); rp += sizeof(int); const char *kbuf = rp; rp += ksiz; const char *vbuf = rp; rp += vsiz; uint8_t hash; uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash); if(!tchdbputimpl(hdb, kbuf, ksiz, bidx, hash, vbuf, vsiz, HDBPDOVER)){ tcxstrdel(hdb->drpdef); tcxstrdel(hdb->drpool); hdb->drpool = NULL; hdb->drpdef = NULL; hdb->drpoff = 0; HDBUNLOCKDB(hdb); return false; } size -= sizeof(int) * 2 + ksiz + vsiz; } tcxstrdel(hdb->drpdef); tcxstrdel(hdb->drpool); hdb->drpool = NULL; hdb->drpdef = NULL; hdb->drpoff = 0; uint64_t llnum; llnum = hdb->rnum; llnum = TCHTOILL(llnum); memcpy(hdb->map + HDBRNUMOFF, &llnum, sizeof(llnum)); llnum = hdb->fsiz; llnum = TCHTOILL(llnum); memcpy(hdb->map + HDBFSIZOFF, &llnum, sizeof(llnum)); HDBUNLOCKDB(hdb); return true; } /* Adjust the caches for leaves and nodes. `hdb' specifies the hash tree database object. */ static void tchdbcacheadjust(TCHDB *hdb){ assert(hdb); TCDODEBUG(hdb->cnt_adjrecc++); tcmdbcutfront(hdb->recc, HDBCACHEOUT); } /* Initialize the write ahead logging file. `hdb' specifies the hash database object. If successful, the return value is true, else, it is false. */ static bool tchdbwalinit(TCHDB *hdb){ assert(hdb); if(lseek(hdb->walfd, 0, SEEK_SET) == -1){ tchdbsetecode(hdb, TCESEEK, __FILE__, __LINE__, __func__); return false; } if(ftruncate(hdb->walfd, 0) == -1){ tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__); return false; } uint64_t llnum = hdb->fsiz; llnum = TCHTOILL(llnum); if(!tcwrite(hdb->walfd, &llnum, sizeof(llnum))){ tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__); return false; } hdb->walend = hdb->fsiz; if(!tchdbwalwrite(hdb, 0, HDBHEADSIZ)) return false; return true; } /* Write an event into the write ahead logging file. `hdb' specifies the hash database object. `off' specifies the offset of the region to be updated. `size' specifies the size of the region. If successful, the return value is true, else, it is false. */ static bool tchdbwalwrite(TCHDB *hdb, uint64_t off, int64_t size){ assert(hdb && off >= 0 && size >= 0); if(off + size > hdb->walend) size = hdb->walend - off; if(size < 1) return true; char stack[HDBIOBUFSIZ]; char *buf; if(size + sizeof(off) + sizeof(size) <= HDBIOBUFSIZ){ buf = stack; } else { TCMALLOC(buf, size + sizeof(off) + sizeof(size)); } char *wp = buf; uint64_t llnum = TCHTOILL(off); memcpy(wp, &llnum, sizeof(llnum)); wp += sizeof(llnum); uint32_t lnum = TCHTOIL(size); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); if(!tchdbseekread(hdb, off, wp, size)){ if(buf != stack) TCFREE(buf); return false; } wp += size; if(!HDBLOCKWAL(hdb)) return false; if(!tcwrite(hdb->walfd, buf, wp - buf)){ tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__); if(buf != stack) TCFREE(buf); HDBUNLOCKWAL(hdb); return false; } if(buf != stack) TCFREE(buf); if((hdb->omode & HDBOTSYNC) && fsync(hdb->walfd) == -1){ tchdbsetecode(hdb, TCESYNC, __FILE__, __LINE__, __func__); HDBUNLOCKWAL(hdb); return false; } HDBUNLOCKWAL(hdb); return true; } /* Restore the database from the write ahead logging file. `hdb' specifies the hash database object. `path' specifies the path of the database file. If successful, the return value is true, else, it is false. */ static int tchdbwalrestore(TCHDB *hdb, const char *path){ assert(hdb && path); char *tpath = tcsprintf("%s%c%s", path, MYEXTCHR, HDBWALSUFFIX); int walfd = open(tpath, O_RDONLY, HDBFILEMODE); TCFREE(tpath); if(walfd < 0) return false; bool err = false; uint64_t walsiz = 0; struct stat sbuf; if(fstat(walfd, &sbuf) == 0){ walsiz = sbuf.st_size; } else { tchdbsetecode(hdb, TCESTAT, __FILE__, __LINE__, __func__); err = true; } if(walsiz >= sizeof(walsiz) + HDBHEADSIZ){ int dbfd = hdb->fd; int tfd = -1; if(!(hdb->omode & HDBOWRITER)){ tfd = open(path, O_WRONLY, HDBFILEMODE); if(tfd >= 0){ dbfd = tfd; } else { int ecode = TCEOPEN; switch(errno){ case EACCES: ecode = TCENOPERM; break; case ENOENT: ecode = TCENOFILE; break; case ENOTDIR: ecode = TCENOFILE; break; } tchdbsetecode(hdb, ecode, __FILE__, __LINE__, __func__); err = true; } } uint64_t fsiz = 0; if(tcread(walfd, &fsiz, sizeof(fsiz))){ fsiz = TCITOHLL(fsiz); } else { tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__); err = true; } TCLIST *list = tclistnew(); uint64_t waloff = sizeof(fsiz); char stack[HDBIOBUFSIZ]; while(waloff < walsiz){ uint64_t off; uint32_t size; if(!tcread(walfd, stack, sizeof(off) + sizeof(size))){ tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__); err = true; break; } memcpy(&off, stack, sizeof(off)); off = TCITOHLL(off); memcpy(&size, stack + sizeof(off), sizeof(size)); size = TCITOHL(size); char *buf; if(sizeof(off) + size <= HDBIOBUFSIZ){ buf = stack; } else { TCMALLOC(buf, sizeof(off) + size); } *(uint64_t *)buf = off; if(!tcread(walfd, buf + sizeof(off), size)){ tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__); err = true; if(buf != stack) TCFREE(buf); break; } TCLISTPUSH(list, buf, sizeof(off) + size); if(buf != stack) TCFREE(buf); waloff += sizeof(off) + sizeof(size) + size; } size_t xmsiz = 0; if(hdb->fd >= 0 && hdb->map) xmsiz = (hdb->xmsiz > hdb->msiz) ? hdb->xmsiz : hdb->msiz; for(int i = TCLISTNUM(list) - 1; i >= 0; i--){ const char *rec; int size; TCLISTVAL(rec, list, i, size); uint64_t off = *(uint64_t *)rec; rec += sizeof(off); size -= sizeof(off); if(lseek(dbfd, off, SEEK_SET) == -1){ tchdbsetecode(hdb, TCESEEK, __FILE__, __LINE__, __func__); err = true; break; } if(!tcwrite(dbfd, rec, size)){ tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__); err = true; break; } if(!TCUBCACHE && off < xmsiz){ size = (size <= xmsiz - off) ? size : xmsiz - off; memcpy(hdb->map + off, rec, size); } } tclistdel(list); if(ftruncate(dbfd, fsiz) == -1){ tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__); err = true; } if((hdb->omode & HDBOTSYNC) && fsync(dbfd) == -1){ tchdbsetecode(hdb, TCESYNC, __FILE__, __LINE__, __func__); err = true; } if(tfd >= 0 && close(tfd) == -1){ tchdbsetecode(hdb, TCECLOSE, __FILE__, __LINE__, __func__); err = true; } } else { err = true; } if(close(walfd) == -1){ tchdbsetecode(hdb, TCECLOSE, __FILE__, __LINE__, __func__); err = true; } return !err; } /* Remove the write ahead logging file. `hdb' specifies the hash database object. `path' specifies the path of the database file. If successful, the return value is true, else, it is false. */ static bool tchdbwalremove(TCHDB *hdb, const char *path){ assert(hdb && path); char *tpath = tcsprintf("%s%c%s", path, MYEXTCHR, HDBWALSUFFIX); bool err = false; if(unlink(tpath) == -1 && errno != ENOENT){ tchdbsetecode(hdb, TCEUNLINK, __FILE__, __LINE__, __func__); err = true; } TCFREE(tpath); return !err; } /* Open a database file and connect a hash database object. `hdb' specifies the hash database object. `path' specifies the path of the database file. `omode' specifies the connection mode. If successful, the return value is true, else, it is false. */ static bool tchdbopenimpl(TCHDB *hdb, const char *path, int omode){ assert(hdb && path); int mode = O_RDONLY; if(omode & HDBOWRITER){ mode = O_RDWR; if(omode & HDBOCREAT) mode |= O_CREAT; } int fd = open(path, mode, HDBFILEMODE); if(fd < 0){ int ecode = TCEOPEN; switch(errno){ case EACCES: ecode = TCENOPERM; break; case ENOENT: ecode = TCENOFILE; break; case ENOTDIR: ecode = TCENOFILE; break; } tchdbsetecode(hdb, ecode, __FILE__, __LINE__, __func__); return false; } if(!(omode & HDBONOLCK)){ if(!tclock(fd, omode & HDBOWRITER, omode & HDBOLCKNB)){ tchdbsetecode(hdb, TCELOCK, __FILE__, __LINE__, __func__); close(fd); return false; } } if((omode & HDBOWRITER) && (omode & HDBOTRUNC)){ if(ftruncate(fd, 0) == -1){ tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__); close(fd); return false; } if(!tchdbwalremove(hdb, path)){ close(fd); return false; } } struct stat sbuf; if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){ tchdbsetecode(hdb, TCESTAT, __FILE__, __LINE__, __func__); close(fd); return false; } char hbuf[HDBHEADSIZ]; if((omode & HDBOWRITER) && sbuf.st_size < 1){ hdb->flags = 0; hdb->rnum = 0; uint32_t fbpmax = 1 << hdb->fpow; uint32_t fbpsiz = HDBFBPBSIZ + fbpmax * HDBFBPESIZ; int besiz = (hdb->opts & HDBTLARGE) ? sizeof(int64_t) : sizeof(int32_t); hdb->align = 1 << hdb->apow; hdb->fsiz = HDBHEADSIZ + besiz * hdb->bnum + fbpsiz; hdb->fsiz += tchdbpadsize(hdb, hdb->fsiz); hdb->frec = hdb->fsiz; tchdbdumpmeta(hdb, hbuf); bool err = false; if(!tcwrite(fd, hbuf, HDBHEADSIZ)) err = true; char pbuf[HDBIOBUFSIZ]; memset(pbuf, 0, HDBIOBUFSIZ); uint64_t psiz = hdb->fsiz - HDBHEADSIZ; while(psiz > 0){ if(psiz > HDBIOBUFSIZ){ if(!tcwrite(fd, pbuf, HDBIOBUFSIZ)) err = true; psiz -= HDBIOBUFSIZ; } else { if(!tcwrite(fd, pbuf, psiz)) err = true; psiz = 0; } } if(err){ tchdbsetecode(hdb, TCEWRITE, __FILE__, __LINE__, __func__); close(fd); return false; } sbuf.st_size = hdb->fsiz; } if(lseek(fd, 0, SEEK_SET) == -1){ tchdbsetecode(hdb, TCESEEK, __FILE__, __LINE__, __func__); close(fd); return false; } if(!tcread(fd, hbuf, HDBHEADSIZ)){ tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__); close(fd); return false; } int type = hdb->type; tchdbloadmeta(hdb, hbuf); if((hdb->flags & HDBFOPEN) && tchdbwalrestore(hdb, path)){ if(lseek(fd, 0, SEEK_SET) == -1){ tchdbsetecode(hdb, TCESEEK, __FILE__, __LINE__, __func__); close(fd); return false; } if(!tcread(fd, hbuf, HDBHEADSIZ)){ tchdbsetecode(hdb, TCEREAD, __FILE__, __LINE__, __func__); close(fd); return false; } tchdbloadmeta(hdb, hbuf); if(!tchdbwalremove(hdb, path)){ close(fd); return false; } } int besiz = (hdb->opts & HDBTLARGE) ? sizeof(int64_t) : sizeof(int32_t); size_t msiz = HDBHEADSIZ + hdb->bnum * besiz; if(!(omode & HDBONOLCK)){ if(memcmp(hbuf, HDBMAGICDATA, strlen(HDBMAGICDATA)) || hdb->type != type || hdb->frec < msiz + HDBFBPBSIZ || hdb->frec > hdb->fsiz || sbuf.st_size < hdb->fsiz){ tchdbsetecode(hdb, TCEMETA, __FILE__, __LINE__, __func__); close(fd); return false; } } if(((hdb->opts & HDBTDEFLATE) && !_tc_deflate) || ((hdb->opts & HDBTBZIP) && !_tc_bzcompress) || ((hdb->opts & HDBTEXCODEC) && !hdb->enc)){ tchdbsetecode(hdb, TCEINVALID, __FILE__, __LINE__, __func__); close(fd); return false; } size_t xmsiz = (hdb->xmsiz > msiz) ? hdb->xmsiz : msiz; if(!(omode & HDBOWRITER) && xmsiz > hdb->fsiz) xmsiz = hdb->fsiz; void *map = mmap(0, xmsiz, PROT_READ | ((omode & HDBOWRITER) ? PROT_WRITE : 0), MAP_SHARED, fd, 0); if(map == MAP_FAILED){ tchdbsetecode(hdb, TCEMMAP, __FILE__, __LINE__, __func__); close(fd); return false; } hdb->fbpmax = 1 << hdb->fpow; if(omode & HDBOWRITER){ TCMALLOC(hdb->fbpool, hdb->fbpmax * HDBFBPALWRAT * sizeof(HDBFB)); } else { hdb->fbpool = NULL; } hdb->fbpnum = 0; hdb->fbpmis = 0; hdb->async = false; hdb->drpool = NULL; hdb->drpdef = NULL; hdb->drpoff = 0; hdb->recc = (hdb->rcnum > 0) ? tcmdbnew2(hdb->rcnum * 2 + 1) : NULL; hdb->path = tcstrdup(path); hdb->fd = fd; hdb->omode = omode; hdb->dfcur = hdb->frec; hdb->iter = 0; hdb->map = map; hdb->msiz = msiz; hdb->xfsiz = 0; if(hdb->opts & HDBTLARGE){ hdb->ba32 = NULL; hdb->ba64 = (uint64_t *)((char *)map + HDBHEADSIZ); } else { hdb->ba32 = (uint32_t *)((char *)map + HDBHEADSIZ); hdb->ba64 = NULL; } hdb->align = 1 << hdb->apow; hdb->runit = tclmin(tclmax(hdb->align, HDBMINRUNIT), HDBIOBUFSIZ); hdb->zmode = (hdb->opts & HDBTDEFLATE) || (hdb->opts & HDBTBZIP) || (hdb->opts & HDBTTCBS) || (hdb->opts & HDBTEXCODEC); hdb->ecode = TCESUCCESS; hdb->fatal = false; hdb->inode = (uint64_t)sbuf.st_ino; hdb->mtime = sbuf.st_mtime; hdb->dfcnt = 0; hdb->tran = false; hdb->walfd = -1; hdb->walend = 0; if(hdb->omode & HDBOWRITER){ bool err = false; if(!(hdb->flags & HDBFOPEN) && !tchdbloadfbp(hdb)) err = true; memset(hbuf, 0, 2); if(!tchdbseekwrite(hdb, hdb->msiz, hbuf, 2)) err = true; if(err){ TCFREE(hdb->path); TCFREE(hdb->fbpool); munmap(hdb->map, xmsiz); close(fd); hdb->fd = -1; return false; } tchdbsetflag(hdb, HDBFOPEN, true); } return true; } /* Close a hash database object. `hdb' specifies the hash database object. If successful, the return value is true, else, it is false. */ static bool tchdbcloseimpl(TCHDB *hdb){ assert(hdb); bool err = false; if(hdb->recc){ tcmdbdel(hdb->recc); hdb->recc = NULL; } if(hdb->omode & HDBOWRITER){ if(!tchdbflushdrp(hdb)) err = true; if(hdb->tran) hdb->fbpnum = 0; if(!tchdbsavefbp(hdb)) err = true; TCFREE(hdb->fbpool); tchdbsetflag(hdb, HDBFOPEN, false); } if((hdb->omode & HDBOWRITER) && !tchdbmemsync(hdb, false)) err = true; size_t xmsiz = (hdb->xmsiz > hdb->msiz) ? hdb->xmsiz : hdb->msiz; if(!(hdb->omode & HDBOWRITER) && xmsiz > hdb->fsiz) xmsiz = hdb->fsiz; if(munmap(hdb->map, xmsiz) == -1){ tchdbsetecode(hdb, TCEMMAP, __FILE__, __LINE__, __func__); err = true; } hdb->map = NULL; if((hdb->omode & HDBOWRITER) && ftruncate(hdb->fd, hdb->fsiz) == -1){ tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__); err = true; } if(hdb->tran){ if(!tchdbwalrestore(hdb, hdb->path)) err = true; hdb->tran = false; } if(hdb->walfd >= 0){ if(close(hdb->walfd) == -1){ tchdbsetecode(hdb, TCECLOSE, __FILE__, __LINE__, __func__); err = true; } if(!hdb->fatal && !tchdbwalremove(hdb, hdb->path)) err = true; } if(close(hdb->fd) == -1){ tchdbsetecode(hdb, TCECLOSE, __FILE__, __LINE__, __func__); err = true; } TCFREE(hdb->path); hdb->path = NULL; hdb->fd = -1; return !err; } /* Store a record. `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. `bidx' specifies the index of the bucket array. `hash' specifies the hash value for the collision tree. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. `dmode' specifies behavior when the key overlaps. If successful, the return value is true, else, it is false. */ static bool tchdbputimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash, const char *vbuf, int vsiz, int dmode){ assert(hdb && kbuf && ksiz >= 0); if(hdb->recc) tcmdbout(hdb->recc, kbuf, ksiz); off_t off = tchdbgetbucket(hdb, bidx); off_t entoff = 0; TCHREC rec; char rbuf[HDBIOBUFSIZ]; while(off > 0){ rec.off = off; if(!tchdbreadrec(hdb, &rec, rbuf)) return false; if(hash > rec.hash){ off = rec.left; entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)); } else if(hash < rec.hash){ off = rec.right; entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) + (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t)); } else { if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return false; int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz); if(kcmp > 0){ off = rec.left; TCFREE(rec.bbuf); rec.kbuf = NULL; rec.bbuf = NULL; entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)); } else if(kcmp < 0){ off = rec.right; TCFREE(rec.bbuf); rec.kbuf = NULL; rec.bbuf = NULL; entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) + (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t)); } else { bool rv; int nvsiz; char *nvbuf; HDBPDPROCOP *procptr; switch(dmode){ case HDBPDKEEP: tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__); TCFREE(rec.bbuf); return false; case HDBPDCAT: if(vsiz < 1){ TCFREE(rec.bbuf); return true; } if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)){ TCFREE(rec.bbuf); return false; } nvsiz = rec.vsiz + vsiz; if(rec.bbuf){ TCREALLOC(rec.bbuf, rec.bbuf, rec.ksiz + nvsiz); memcpy(rec.bbuf + rec.ksiz + rec.vsiz, vbuf, vsiz); rec.kbuf = rec.bbuf; rec.vbuf = rec.kbuf + rec.ksiz; rec.vsiz = nvsiz; } else { TCMALLOC(rec.bbuf, nvsiz + 1); memcpy(rec.bbuf, rec.vbuf, rec.vsiz); memcpy(rec.bbuf + rec.vsiz, vbuf, vsiz); rec.vbuf = rec.bbuf; rec.vsiz = nvsiz; } rv = tchdbwriterec(hdb, &rec, bidx, entoff); TCFREE(rec.bbuf); return rv; case HDBPDADDINT: if(rec.vsiz != sizeof(int)){ tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__); TCFREE(rec.bbuf); return false; } if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)){ TCFREE(rec.bbuf); return false; } int lnum; memcpy(&lnum, rec.vbuf, sizeof(lnum)); if(*(int *)vbuf == 0){ TCFREE(rec.bbuf); *(int *)vbuf = lnum; return true; } lnum += *(int *)vbuf; rec.vbuf = (char *)&lnum; *(int *)vbuf = lnum; rv = tchdbwriterec(hdb, &rec, bidx, entoff); TCFREE(rec.bbuf); return rv; case HDBPDADDDBL: if(rec.vsiz != sizeof(double)){ tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__); TCFREE(rec.bbuf); return false; } if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)){ TCFREE(rec.bbuf); return false; } double dnum; memcpy(&dnum, rec.vbuf, sizeof(dnum)); if(*(double *)vbuf == 0.0){ TCFREE(rec.bbuf); *(double *)vbuf = dnum; return true; } dnum += *(double *)vbuf; rec.vbuf = (char *)&dnum; *(double *)vbuf = dnum; rv = tchdbwriterec(hdb, &rec, bidx, entoff); TCFREE(rec.bbuf); return rv; case HDBPDPROC: if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)){ TCFREE(rec.bbuf); return false; } procptr = *(HDBPDPROCOP **)((char *)kbuf - sizeof(procptr)); nvbuf = procptr->proc(rec.vbuf, rec.vsiz, &nvsiz, procptr->op); TCFREE(rec.bbuf); if(nvbuf == (void *)-1){ return tchdbremoverec(hdb, &rec, rbuf, bidx, entoff); } else if(nvbuf){ rec.kbuf = kbuf; rec.ksiz = ksiz; rec.vbuf = nvbuf; rec.vsiz = nvsiz; rv = tchdbwriterec(hdb, &rec, bidx, entoff); TCFREE(nvbuf); return rv; } tchdbsetecode(hdb, TCEKEEP, __FILE__, __LINE__, __func__); return false; default: break; } TCFREE(rec.bbuf); rec.ksiz = ksiz; rec.vsiz = vsiz; rec.kbuf = kbuf; rec.vbuf = vbuf; return tchdbwriterec(hdb, &rec, bidx, entoff); } } } if(!vbuf){ tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } if(!HDBLOCKDB(hdb)) return false; rec.rsiz = hdb->ba64 ? sizeof(uint8_t) * 2 + sizeof(uint64_t) * 2 + sizeof(uint16_t) : sizeof(uint8_t) * 2 + sizeof(uint32_t) * 2 + sizeof(uint16_t); if(ksiz < (1U << 7)){ rec.rsiz += 1; } else if(ksiz < (1U << 14)){ rec.rsiz += 2; } else if(ksiz < (1U << 21)){ rec.rsiz += 3; } else if(ksiz < (1U << 28)){ rec.rsiz += 4; } else { rec.rsiz += 5; } if(vsiz < (1U << 7)){ rec.rsiz += 1; } else if(vsiz < (1U << 14)){ rec.rsiz += 2; } else if(vsiz < (1U << 21)){ rec.rsiz += 3; } else if(vsiz < (1U << 28)){ rec.rsiz += 4; } else { rec.rsiz += 5; } if(!tchdbfbpsearch(hdb, &rec)){ HDBUNLOCKDB(hdb); return false; } rec.hash = hash; rec.left = 0; rec.right = 0; rec.ksiz = ksiz; rec.vsiz = vsiz; rec.psiz = 0; rec.kbuf = kbuf; rec.vbuf = vbuf; if(!tchdbwriterec(hdb, &rec, bidx, entoff)){ HDBUNLOCKDB(hdb); return false; } hdb->rnum++; uint64_t llnum = hdb->rnum; llnum = TCHTOILL(llnum); memcpy(hdb->map + HDBRNUMOFF, &llnum, sizeof(llnum)); HDBUNLOCKDB(hdb); return true; } /* Append a record to the delayed record pool. `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 region of the value. `vsiz' specifies the size of the region of the value. `hash' specifies the second hash value. */ static void tchdbdrpappend(TCHDB *hdb, const char *kbuf, int ksiz, const char *vbuf, int vsiz, uint8_t hash){ assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); TCDODEBUG(hdb->cnt_appenddrp++); char rbuf[HDBIOBUFSIZ]; char *wp = rbuf; *(uint8_t *)(wp++) = HDBMAGICREC; *(uint8_t *)(wp++) = hash; if(hdb->ba64){ memset(wp, 0, sizeof(uint64_t) * 2); wp += sizeof(uint64_t) * 2; } else { memset(wp, 0, sizeof(uint32_t) * 2); wp += sizeof(uint32_t) * 2; } uint16_t snum; char *pwp = wp; wp += sizeof(snum); int step; TCSETVNUMBUF(step, wp, ksiz); wp += step; TCSETVNUMBUF(step, wp, vsiz); wp += step; int32_t hsiz = wp - rbuf; int32_t rsiz = hsiz + ksiz + vsiz; uint16_t psiz = tchdbpadsize(hdb, hdb->fsiz + rsiz); hdb->fsiz += rsiz + psiz; snum = TCHTOIS(psiz); memcpy(pwp, &snum, sizeof(snum)); TCXSTR *drpool = hdb->drpool; TCXSTRCAT(drpool, rbuf, hsiz); TCXSTRCAT(drpool, kbuf, ksiz); TCXSTRCAT(drpool, vbuf, vsiz); if(psiz > 0){ char pbuf[psiz]; memset(pbuf, 0, psiz); TCXSTRCAT(drpool, pbuf, psiz); } } /* Store a record in asynchronus fashion. `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. `bidx' specifies the index of the bucket array. `hash' specifies the hash value for the collision tree. `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. */ static bool tchdbputasyncimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash, const char *vbuf, int vsiz){ assert(hdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(hdb->recc) tcmdbout(hdb->recc, kbuf, ksiz); if(!hdb->drpool){ hdb->drpool = tcxstrnew3(HDBDRPUNIT + HDBDRPLAT); hdb->drpdef = tcxstrnew3(HDBDRPUNIT); hdb->drpoff = hdb->fsiz; } off_t off = tchdbgetbucket(hdb, bidx); off_t entoff = 0; TCHREC rec; char rbuf[HDBIOBUFSIZ]; while(off > 0){ if(off >= hdb->drpoff - hdb->runit){ TCDODEBUG(hdb->cnt_deferdrp++); TCXSTR *drpdef = hdb->drpdef; TCXSTRCAT(drpdef, &ksiz, sizeof(ksiz)); TCXSTRCAT(drpdef, &vsiz, sizeof(vsiz)); TCXSTRCAT(drpdef, kbuf, ksiz); TCXSTRCAT(drpdef, vbuf, vsiz); if(TCXSTRSIZE(hdb->drpdef) > HDBDRPUNIT && !tchdbflushdrp(hdb)) return false; return true; } rec.off = off; if(!tchdbreadrec(hdb, &rec, rbuf)) return false; if(hash > rec.hash){ off = rec.left; entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)); } else if(hash < rec.hash){ off = rec.right; entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) + (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t)); } else { TCDODEBUG(hdb->cnt_deferdrp++); TCXSTR *drpdef = hdb->drpdef; TCXSTRCAT(drpdef, &ksiz, sizeof(ksiz)); TCXSTRCAT(drpdef, &vsiz, sizeof(vsiz)); TCXSTRCAT(drpdef, kbuf, ksiz); TCXSTRCAT(drpdef, vbuf, vsiz); if(TCXSTRSIZE(hdb->drpdef) > HDBDRPUNIT && !tchdbflushdrp(hdb)) return false; return true; } } if(entoff > 0){ if(hdb->ba64){ uint64_t llnum = hdb->fsiz >> hdb->apow; llnum = TCHTOILL(llnum); if(!tchdbseekwrite(hdb, entoff, &llnum, sizeof(uint64_t))) return false; } else { uint32_t lnum = hdb->fsiz >> hdb->apow; lnum = TCHTOIL(lnum); if(!tchdbseekwrite(hdb, entoff, &lnum, sizeof(uint32_t))) return false; } } else { tchdbsetbucket(hdb, bidx, hdb->fsiz); } tchdbdrpappend(hdb, kbuf, ksiz, vbuf, vsiz, hash); hdb->rnum++; if(TCXSTRSIZE(hdb->drpool) > HDBDRPUNIT && !tchdbflushdrp(hdb)) return false; return true; } /* Remove a record of a hash database object. `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. `bidx' specifies the index of the bucket array. `hash' specifies the hash value for the collision tree. If successful, the return value is true, else, it is false. */ static bool tchdboutimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash){ assert(hdb && kbuf && ksiz >= 0); if(hdb->recc) tcmdbout(hdb->recc, kbuf, ksiz); off_t off = tchdbgetbucket(hdb, bidx); off_t entoff = 0; TCHREC rec; char rbuf[HDBIOBUFSIZ]; while(off > 0){ rec.off = off; if(!tchdbreadrec(hdb, &rec, rbuf)) return false; if(hash > rec.hash){ off = rec.left; entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)); } else if(hash < rec.hash){ off = rec.right; entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) + (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t)); } else { if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return false; int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz); if(kcmp > 0){ off = rec.left; TCFREE(rec.bbuf); rec.kbuf = NULL; rec.bbuf = NULL; entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)); } else if(kcmp < 0){ off = rec.right; TCFREE(rec.bbuf); rec.kbuf = NULL; rec.bbuf = NULL; entoff = rec.off + (sizeof(uint8_t) + sizeof(uint8_t)) + (hdb->ba64 ? sizeof(uint64_t) : sizeof(uint32_t)); } else { TCFREE(rec.bbuf); rec.bbuf = NULL; return tchdbremoverec(hdb, &rec, rbuf, bidx, entoff); } } } tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } /* Retrieve a record in a hash database object. `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. `bidx' specifies the index of the bucket array. `hash' specifies the hash value for the collision tree. `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. */ static char *tchdbgetimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash, int *sp){ assert(hdb && kbuf && ksiz >= 0 && sp); if(hdb->recc){ int tvsiz; char *tvbuf = tcmdbget(hdb->recc, kbuf, ksiz, &tvsiz); if(tvbuf){ if(*tvbuf == '*'){ tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); TCFREE(tvbuf); return NULL; } *sp = tvsiz - 1; memmove(tvbuf, tvbuf + 1, tvsiz); return tvbuf; } } off_t off = tchdbgetbucket(hdb, bidx); TCHREC rec; char rbuf[HDBIOBUFSIZ]; while(off > 0){ rec.off = off; if(!tchdbreadrec(hdb, &rec, rbuf)) return NULL; if(hash > rec.hash){ off = rec.left; } else if(hash < rec.hash){ off = rec.right; } else { if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return NULL; int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz); if(kcmp > 0){ off = rec.left; TCFREE(rec.bbuf); rec.kbuf = NULL; rec.bbuf = NULL; } else if(kcmp < 0){ off = rec.right; TCFREE(rec.bbuf); rec.kbuf = NULL; rec.bbuf = NULL; } else { if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return NULL; if(hdb->zmode){ int zsiz; char *zbuf; if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz); } else { zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop); } TCFREE(rec.bbuf); if(!zbuf){ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__); return NULL; } if(hdb->recc){ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb); tcmdbput4(hdb->recc, kbuf, ksiz, "=", 1, zbuf, zsiz); } *sp = zsiz; return zbuf; } if(hdb->recc){ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb); tcmdbput4(hdb->recc, kbuf, ksiz, "=", 1, rec.vbuf, rec.vsiz); } if(rec.bbuf){ memmove(rec.bbuf, rec.vbuf, rec.vsiz); rec.bbuf[rec.vsiz] = '\0'; *sp = rec.vsiz; return rec.bbuf; } *sp = rec.vsiz; char *rv; TCMEMDUP(rv, rec.vbuf, rec.vsiz); return rv; } } } if(hdb->recc){ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb); tcmdbput(hdb->recc, kbuf, ksiz, "*", 1); } tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); return NULL; } /* Retrieve a record in a hash database object and write the value into a buffer. `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. `bidx' specifies the index of the bucket array. `hash' specifies the hash value for the collision tree. `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. */ static int tchdbgetintobuf(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash, char *vbuf, int max){ assert(hdb && kbuf && ksiz >= 0 && vbuf && max >= 0); if(hdb->recc){ int tvsiz; char *tvbuf = tcmdbget(hdb->recc, kbuf, ksiz, &tvsiz); if(tvbuf){ if(*tvbuf == '*'){ tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); TCFREE(tvbuf); return -1; } tvsiz = tclmin(tvsiz - 1, max); memcpy(vbuf, tvbuf + 1, tvsiz); TCFREE(tvbuf); return tvsiz; } } off_t off = tchdbgetbucket(hdb, bidx); TCHREC rec; char rbuf[HDBIOBUFSIZ]; while(off > 0){ rec.off = off; if(!tchdbreadrec(hdb, &rec, rbuf)) return -1; if(hash > rec.hash){ off = rec.left; } else if(hash < rec.hash){ off = rec.right; } else { if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return -1; int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz); if(kcmp > 0){ off = rec.left; TCFREE(rec.bbuf); rec.kbuf = NULL; rec.bbuf = NULL; } else if(kcmp < 0){ off = rec.right; TCFREE(rec.bbuf); rec.kbuf = NULL; rec.bbuf = NULL; } else { if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return -1; if(hdb->zmode){ int zsiz; char *zbuf; if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz); } else { zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop); } TCFREE(rec.bbuf); if(!zbuf){ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__); return -1; } if(hdb->recc){ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb); tcmdbput4(hdb->recc, kbuf, ksiz, "=", 1, zbuf, zsiz); } zsiz = tclmin(zsiz, max); memcpy(vbuf, zbuf, zsiz); TCFREE(zbuf); return zsiz; } if(hdb->recc){ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb); tcmdbput4(hdb->recc, kbuf, ksiz, "=", 1, rec.vbuf, rec.vsiz); } int vsiz = tclmin(rec.vsiz, max); memcpy(vbuf, rec.vbuf, vsiz); TCFREE(rec.bbuf); return vsiz; } } } if(hdb->recc){ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb); tcmdbput(hdb->recc, kbuf, ksiz, "*", 1); } tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); return -1; } /* Retrieve the next record of a record in a hash database object. `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. `vbp' specifies the pointer to the variable into which the pointer to the value is assigned. `vsp' specifies the pointer to the variable into which the size of the value is assigned. If successful, the return value is the pointer to the region of the value of the next record. */ static char *tchdbgetnextimpl(TCHDB *hdb, const char *kbuf, int ksiz, int *sp, const char **vbp, int *vsp){ assert(hdb && sp); if(!kbuf){ uint64_t iter = hdb->frec; TCHREC rec; char rbuf[HDBIOBUFSIZ]; while(iter < hdb->fsiz){ rec.off = iter; if(!tchdbreadrec(hdb, &rec, rbuf)) return NULL; iter += rec.rsiz; if(rec.magic == HDBMAGICREC){ if(vbp){ if(hdb->zmode){ if(!tchdbreadrecbody(hdb, &rec)) return NULL; int zsiz; char *zbuf; if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz); } else { zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop); } if(!zbuf){ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__); TCFREE(rec.bbuf); return NULL; } char *rv; TCMALLOC(rv, rec.ksiz + zsiz + 1); memcpy(rv, rec.kbuf, rec.ksiz); memcpy(rv + rec.ksiz, zbuf, zsiz); *sp = rec.ksiz; *vbp = rv + rec.ksiz; *vsp = zsiz; TCFREE(zbuf); TCFREE(rec.bbuf); return rv; } if(rec.vbuf){ char *rv; TCMALLOC(rv, rec.ksiz + rec.vsiz + 1); memcpy(rv, rec.kbuf, rec.ksiz); memcpy(rv + rec.ksiz, rec.vbuf, rec.vsiz); *sp = rec.ksiz; *vbp = rv + rec.ksiz; *vsp = rec.vsiz; return rv; } if(!tchdbreadrecbody(hdb, &rec)) return NULL; *sp = rec.ksiz; *vbp = rec.vbuf; *vsp = rec.vsiz; return rec.bbuf; } if(rec.kbuf){ *sp = rec.ksiz; char *rv; TCMEMDUP(rv, rec.kbuf, rec.ksiz); return rv; } if(!tchdbreadrecbody(hdb, &rec)) return NULL; rec.bbuf[rec.ksiz] = '\0'; *sp = rec.ksiz; return rec.bbuf; } } tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); return NULL; } uint8_t hash; uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash); off_t off = tchdbgetbucket(hdb, bidx); TCHREC rec; char rbuf[HDBIOBUFSIZ]; while(off > 0){ rec.off = off; if(!tchdbreadrec(hdb, &rec, rbuf)) return NULL; if(hash > rec.hash){ off = rec.left; } else if(hash < rec.hash){ off = rec.right; } else { if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return NULL; int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz); if(kcmp > 0){ off = rec.left; TCFREE(rec.bbuf); rec.kbuf = NULL; rec.bbuf = NULL; } else if(kcmp < 0){ off = rec.right; TCFREE(rec.bbuf); rec.kbuf = NULL; rec.bbuf = NULL; } else { uint64_t iter = rec.off + rec.rsiz; TCFREE(rec.bbuf); rec.kbuf = NULL; rec.bbuf = NULL; while(iter < hdb->fsiz){ rec.off = iter; if(!tchdbreadrec(hdb, &rec, rbuf)) return NULL; iter += rec.rsiz; if(rec.magic == HDBMAGICREC){ if(vbp){ if(hdb->zmode){ if(!tchdbreadrecbody(hdb, &rec)) return NULL; int zsiz; char *zbuf; if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz); } else { zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop); } if(!zbuf){ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__); TCFREE(rec.bbuf); return NULL; } char *rv; TCMALLOC(rv, rec.ksiz + zsiz + 1); memcpy(rv, rec.kbuf, rec.ksiz); memcpy(rv + rec.ksiz, zbuf, zsiz); *sp = rec.ksiz; *vbp = rv + rec.ksiz; *vsp = zsiz; TCFREE(zbuf); TCFREE(rec.bbuf); return rv; } if(rec.vbuf){ char *rv; TCMALLOC(rv, rec.ksiz + rec.vsiz + 1); memcpy(rv, rec.kbuf, rec.ksiz); memcpy(rv + rec.ksiz, rec.vbuf, rec.vsiz); *sp = rec.ksiz; *vbp = rv + rec.ksiz; *vsp = rec.vsiz; return rv; } if(!tchdbreadrecbody(hdb, &rec)) return NULL; *sp = rec.ksiz; *vbp = rec.vbuf; *vsp = rec.vsiz; return rec.bbuf; } if(rec.kbuf){ *sp = rec.ksiz; char *rv; TCMEMDUP(rv, rec.kbuf, rec.ksiz); return rv; } if(!tchdbreadrecbody(hdb, &rec)) return NULL; rec.bbuf[rec.ksiz] = '\0'; *sp = rec.ksiz; return rec.bbuf; } } tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); return NULL; } } } tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); return NULL; } /* Get the size of the value of a record in a hash database object. `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. `bidx' specifies the index of the bucket array. `hash' specifies the hash value for the collision tree. If successful, the return value is the size of the value of the corresponding record, else, it is -1. */ static int tchdbvsizimpl(TCHDB *hdb, const char *kbuf, int ksiz, uint64_t bidx, uint8_t hash){ assert(hdb && kbuf && ksiz >= 0); if(hdb->recc){ int tvsiz; char *tvbuf = tcmdbget(hdb->recc, kbuf, ksiz, &tvsiz); if(tvbuf){ if(*tvbuf == '*'){ tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); TCFREE(tvbuf); return -1; } TCFREE(tvbuf); return tvsiz - 1; } } off_t off = tchdbgetbucket(hdb, bidx); TCHREC rec; char rbuf[HDBIOBUFSIZ]; while(off > 0){ rec.off = off; if(!tchdbreadrec(hdb, &rec, rbuf)) return -1; if(hash > rec.hash){ off = rec.left; } else if(hash < rec.hash){ off = rec.right; } else { if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return -1; int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz); if(kcmp > 0){ off = rec.left; TCFREE(rec.bbuf); rec.kbuf = NULL; rec.bbuf = NULL; } else if(kcmp < 0){ off = rec.right; TCFREE(rec.bbuf); rec.kbuf = NULL; rec.bbuf = NULL; } else { if(hdb->zmode){ if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return -1; int zsiz; char *zbuf; if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz); } else { zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop); } TCFREE(rec.bbuf); if(!zbuf){ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__); return -1; } if(hdb->recc){ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb); tcmdbput4(hdb->recc, kbuf, ksiz, "=", 1, zbuf, zsiz); } TCFREE(zbuf); return zsiz; } if(hdb->recc && rec.vbuf){ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb); tcmdbput4(hdb->recc, kbuf, ksiz, "=", 1, rec.vbuf, rec.vsiz); } TCFREE(rec.bbuf); return rec.vsiz; } } } if(hdb->recc){ if(tcmdbrnum(hdb->recc) >= hdb->rcnum) tchdbcacheadjust(hdb); tcmdbput(hdb->recc, kbuf, ksiz, "*", 1); } tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); return -1; } /* Initialize the iterator of a hash database object. `hdb' specifies the hash database object. If successful, the return value is true, else, it is false. */ static bool tchdbiterinitimpl(TCHDB *hdb){ assert(hdb); hdb->iter = hdb->frec; return true; } /* Get the next key of the iterator of a hash database object. `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'. */ static char *tchdbiternextimpl(TCHDB *hdb, int *sp){ assert(hdb && sp); TCHREC rec; char rbuf[HDBIOBUFSIZ]; while(hdb->iter < hdb->fsiz){ rec.off = hdb->iter; if(!tchdbreadrec(hdb, &rec, rbuf)) return NULL; hdb->iter += rec.rsiz; if(rec.magic == HDBMAGICREC){ if(rec.kbuf){ *sp = rec.ksiz; char *rv; TCMEMDUP(rv, rec.kbuf, rec.ksiz); return rv; } if(!tchdbreadrecbody(hdb, &rec)) return NULL; rec.bbuf[rec.ksiz] = '\0'; *sp = rec.ksiz; return rec.bbuf; } } tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); return NULL; } /* Get the next extensible objects of the iterator of a hash database object. */ static bool tchdbiternextintoxstr(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr){ assert(hdb && kxstr && vxstr); TCHREC rec; char rbuf[HDBIOBUFSIZ]; while(hdb->iter < hdb->fsiz){ rec.off = hdb->iter; if(!tchdbreadrec(hdb, &rec, rbuf)) return false; hdb->iter += rec.rsiz; if(rec.magic == HDBMAGICREC){ if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)) return false; tcxstrclear(kxstr); TCXSTRCAT(kxstr, rec.kbuf, rec.ksiz); tcxstrclear(vxstr); if(hdb->zmode){ int zsiz; char *zbuf; if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz); } else { zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop); } if(!zbuf){ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__); TCFREE(rec.bbuf); return false; } TCXSTRCAT(vxstr, zbuf, zsiz); TCFREE(zbuf); } else { TCXSTRCAT(vxstr, rec.vbuf, rec.vsiz); } TCFREE(rec.bbuf); return true; } } tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } /* Optimize the file of a hash database object. `hdb' specifies the hash database object. `bnum' specifies the number of elements of the bucket array. `apow' specifies the size of record alignment by power of 2. `fpow' specifies the maximum number of elements of the free block pool by power of 2. `opts' specifies options by bitwise-or. If successful, the return value is true, else, it is false. */ static bool tchdboptimizeimpl(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){ assert(hdb); char *tpath = tcsprintf("%s%ctmp%c%llu", hdb->path, MYEXTCHR, MYEXTCHR, hdb->inode); TCHDB *thdb = tchdbnew(); thdb->dbgfd = hdb->dbgfd; thdb->enc = hdb->enc; thdb->encop = hdb->encop; thdb->dec = hdb->dec; thdb->decop = hdb->decop; if(bnum < 1){ bnum = hdb->rnum * 2 + 1; if(bnum < HDBDEFBNUM) bnum = HDBDEFBNUM; } if(apow < 0) apow = hdb->apow; if(fpow < 0) fpow = hdb->fpow; if(opts == UINT8_MAX) opts = hdb->opts; tchdbtune(thdb, bnum, apow, fpow, opts); if(!tchdbopen(thdb, tpath, HDBOWRITER | HDBOCREAT | HDBOTRUNC)){ tchdbsetecode(hdb, thdb->ecode, __FILE__, __LINE__, __func__); tchdbdel(thdb); TCFREE(tpath); return false; } memcpy(tchdbopaque(thdb), tchdbopaque(hdb), HDBHEADSIZ - HDBOPAQUEOFF); bool err = false; uint64_t off = hdb->frec; TCHREC rec; char rbuf[HDBIOBUFSIZ]; while(off < hdb->fsiz){ rec.off = off; if(!tchdbreadrec(hdb, &rec, rbuf)){ err = true; break; } off += rec.rsiz; if(rec.magic == HDBMAGICREC){ if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)){ TCFREE(rec.bbuf); err = true; } else { if(hdb->zmode){ int zsiz; char *zbuf; if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz); } else { zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop); } if(zbuf){ if(!tchdbput(thdb, rec.kbuf, rec.ksiz, zbuf, zsiz)){ tchdbsetecode(hdb, thdb->ecode, __FILE__, __LINE__, __func__); err = true; } TCFREE(zbuf); } else { tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__); err = true; } } else { if(!tchdbput(thdb, rec.kbuf, rec.ksiz, rec.vbuf, rec.vsiz)){ tchdbsetecode(hdb, thdb->ecode, __FILE__, __LINE__, __func__); err = true; } } } TCFREE(rec.bbuf); } } if(!tchdbclose(thdb)){ tchdbsetecode(hdb, thdb->ecode, __FILE__, __LINE__, __func__); err = true; } bool esc = false; if(err && (hdb->omode & HDBONOLCK) && !thdb->fatal){ err = false; esc = true; } tchdbdel(thdb); if(err){ TCFREE(tpath); return false; } if(esc){ char *bpath = tcsprintf("%s%cbroken", tpath, MYEXTCHR); if(rename(hdb->path, bpath) == -1){ tchdbsetecode(hdb, TCEUNLINK, __FILE__, __LINE__, __func__); err = true; } TCFREE(bpath); } else { if(unlink(hdb->path) == -1){ tchdbsetecode(hdb, TCEUNLINK, __FILE__, __LINE__, __func__); err = true; } } if(rename(tpath, hdb->path) == -1){ tchdbsetecode(hdb, TCERENAME, __FILE__, __LINE__, __func__); err = true; } TCFREE(tpath); if(err) return false; tpath = tcstrdup(hdb->path); int omode = (hdb->omode & ~HDBOCREAT) & ~HDBOTRUNC; if(!tchdbcloseimpl(hdb)){ TCFREE(tpath); return false; } bool rv = tchdbopenimpl(hdb, tpath, omode); TCFREE(tpath); return rv; } /* Remove all records of a hash database object. `hdb' specifies the hash database object. If successful, the return value is true, else, it is false. */ static bool tchdbvanishimpl(TCHDB *hdb){ assert(hdb); char *path = tcstrdup(hdb->path); int omode = hdb->omode; bool err = false; if(!tchdbcloseimpl(hdb)) err = true; if(!tchdbopenimpl(hdb, path, HDBOTRUNC | omode)){ tcpathunlock(hdb->rpath); TCFREE(hdb->rpath); err = true; } TCFREE(path); return !err; } /* Copy the database file of a hash database object. `hdb' specifies the hash database object. `path' specifies the path of the destination file. If successful, the return value is true, else, it is false. */ static bool tchdbcopyimpl(TCHDB *hdb, const char *path){ assert(hdb && path); bool err = false; if(hdb->omode & HDBOWRITER){ if(!tchdbsavefbp(hdb)) err = true; if(!tchdbmemsync(hdb, false)) err = true; tchdbsetflag(hdb, HDBFOPEN, false); } if(*path == '@'){ char tsbuf[TCNUMBUFSIZ]; sprintf(tsbuf, "%llu", (unsigned long long)(tctime() * 1000000)); const char *args[3]; args[0] = path + 1; args[1] = hdb->path; args[2] = tsbuf; if(tcsystem(args, sizeof(args) / sizeof(*args)) != 0) err = true; } else { if(!tccopyfile(hdb->path, path)){ tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__); err = true; } } if(hdb->omode & HDBOWRITER) tchdbsetflag(hdb, HDBFOPEN, true); return !err; } /* Perform dynamic defragmentation of a hash database object. `hdb' specifies the hash database object connected. `step' specifie the number of steps. If successful, the return value is true, else, it is false. */ static bool tchdbdefragimpl(TCHDB *hdb, int64_t step){ assert(hdb && step >= 0); TCDODEBUG(hdb->cnt_defrag++); hdb->dfcnt = 0; TCHREC rec; char rbuf[HDBIOBUFSIZ]; while(true){ if(hdb->dfcur >= hdb->fsiz){ hdb->dfcur = hdb->frec; return true; } if(step-- < 1) return true; rec.off = hdb->dfcur; if(!tchdbreadrec(hdb, &rec, rbuf)) return false; if(rec.magic == HDBMAGICFB) break; hdb->dfcur += rec.rsiz; } uint32_t align = hdb->align; uint64_t base = hdb->dfcur; uint64_t dest = base; uint64_t cur = base; if(hdb->iter == cur) hdb->iter += rec.rsiz; cur += rec.rsiz; uint64_t fbsiz = cur - dest; step++; while(step > 0 && cur < hdb->fsiz){ rec.off = cur; if(!tchdbreadrec(hdb, &rec, rbuf)) return false; uint32_t rsiz = rec.rsiz; if(rec.magic == HDBMAGICREC){ if(rec.psiz >= align){ int diff = rec.psiz - rec.psiz % align; rec.psiz -= diff; rec.rsiz -= diff; fbsiz += diff; } if(!tchdbshiftrec(hdb, &rec, rbuf, dest)) return false; if(hdb->iter == cur) hdb->iter = dest; dest += rec.rsiz; step--; } else { if(hdb->iter == cur) hdb->iter += rec.rsiz; fbsiz += rec.rsiz; } cur += rsiz; } if(cur < hdb->fsiz){ if(fbsiz > HDBFBMAXSIZ){ tchdbfbptrim(hdb, base, cur, 0, 0); uint64_t off = dest; uint64_t size = fbsiz; while(size > 0){ uint32_t rsiz = (size > HDBFBMAXSIZ) ? HDBFBMAXSIZ : size; if(size - rsiz < HDBMINRUNIT) rsiz = size; tchdbfbpinsert(hdb, off, rsiz); if(!tchdbwritefb(hdb, off, rsiz)) return false; off += rsiz; size -= rsiz; } } else { tchdbfbptrim(hdb, base, cur, dest, fbsiz); if(!tchdbwritefb(hdb, dest, fbsiz)) return false; } hdb->dfcur = cur - fbsiz; } else { TCDODEBUG(hdb->cnt_trunc++); if(hdb->tran && !tchdbwalwrite(hdb, dest, fbsiz)) return false; tchdbfbptrim(hdb, base, cur, 0, 0); hdb->dfcur = hdb->frec; hdb->fsiz = dest; uint64_t llnum = hdb->fsiz; llnum = TCHTOILL(llnum); memcpy(hdb->map + HDBFSIZOFF, &llnum, sizeof(llnum)); if(hdb->iter >= hdb->fsiz) hdb->iter = UINT64_MAX; if(!hdb->tran){ if(ftruncate(hdb->fd, hdb->fsiz) == -1){ tchdbsetecode(hdb, TCETRUNC, __FILE__, __LINE__, __func__); return false; } hdb->xfsiz = 0; } } return true; } /* Move the iterator to the record corresponding a key of a hash database object. `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 true, else, it is false. */ static bool tchdbiterjumpimpl(TCHDB *hdb, const char *kbuf, int ksiz){ assert(hdb && kbuf && ksiz); uint8_t hash; uint64_t bidx = tchdbbidx(hdb, kbuf, ksiz, &hash); off_t off = tchdbgetbucket(hdb, bidx); TCHREC rec; char rbuf[HDBIOBUFSIZ]; while(off > 0){ rec.off = off; if(!tchdbreadrec(hdb, &rec, rbuf)) return false; if(hash > rec.hash){ off = rec.left; } else if(hash < rec.hash){ off = rec.right; } else { if(!rec.kbuf && !tchdbreadrecbody(hdb, &rec)) return false; int kcmp = tcreckeycmp(kbuf, ksiz, rec.kbuf, rec.ksiz); if(kcmp > 0){ off = rec.left; TCFREE(rec.bbuf); rec.kbuf = NULL; rec.bbuf = NULL; } else if(kcmp < 0){ off = rec.right; TCFREE(rec.bbuf); rec.kbuf = NULL; rec.bbuf = NULL; } else { hdb->iter = off; return true; } } } tchdbsetecode(hdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } /* Process each record atomically of a hash database object. `hdb' specifies the hash database object. `func' specifies the pointer to the iterator function called for each record. `op' specifies an arbitrary pointer to be given as a parameter of the iterator function. If successful, the return value is true, else, it is false. */ static bool tchdbforeachimpl(TCHDB *hdb, TCITER iter, void *op){ assert(hdb && iter); bool err = false; uint64_t off = hdb->frec; TCHREC rec; char rbuf[HDBIOBUFSIZ]; bool cont = true; while(cont && off < hdb->fsiz){ rec.off = off; if(!tchdbreadrec(hdb, &rec, rbuf)){ err = true; break; } off += rec.rsiz; if(rec.magic == HDBMAGICREC){ if(!rec.vbuf && !tchdbreadrecbody(hdb, &rec)){ TCFREE(rec.bbuf); err = true; } else { if(hdb->zmode){ int zsiz; char *zbuf; if(hdb->opts & HDBTDEFLATE){ zbuf = _tc_inflate(rec.vbuf, rec.vsiz, &zsiz, _TCZMRAW); } else if(hdb->opts & HDBTBZIP){ zbuf = _tc_bzdecompress(rec.vbuf, rec.vsiz, &zsiz); } else if(hdb->opts & HDBTTCBS){ zbuf = tcbsdecode(rec.vbuf, rec.vsiz, &zsiz); } else { zbuf = hdb->dec(rec.vbuf, rec.vsiz, &zsiz, hdb->decop); } if(zbuf){ cont = iter(rec.kbuf, rec.ksiz, zbuf, zsiz, op); TCFREE(zbuf); } else { tchdbsetecode(hdb, TCEMISC, __FILE__, __LINE__, __func__); err = true; } } else { cont = iter(rec.kbuf, rec.ksiz, rec.vbuf, rec.vsiz, op); } } TCFREE(rec.bbuf); } } return !err; } /* Lock a method of the hash database object. `hdb' specifies the hash database object. `wr' specifies whether the lock is writer or not. If successful, the return value is true, else, it is false. */ static bool tchdblockmethod(TCHDB *hdb, bool wr){ assert(hdb); if(wr ? pthread_rwlock_wrlock(hdb->mmtx) != 0 : pthread_rwlock_rdlock(hdb->mmtx) != 0){ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Unlock a method of the hash database object. `hdb' specifies the hash database object. If successful, the return value is true, else, it is false. */ static bool tchdbunlockmethod(TCHDB *hdb){ assert(hdb); if(pthread_rwlock_unlock(hdb->mmtx) != 0){ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Lock a record of the hash database object. `hdb' specifies the hash database object. `bidx' specifies the bucket index of the record. `wr' specifies whether the lock is writer or not. If successful, the return value is true, else, it is false. */ static bool tchdblockrecord(TCHDB *hdb, uint8_t bidx, bool wr){ assert(hdb); if(wr ? pthread_rwlock_wrlock((pthread_rwlock_t *)hdb->rmtxs + bidx) != 0 : pthread_rwlock_rdlock((pthread_rwlock_t *)hdb->rmtxs + bidx) != 0){ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Unlock a record of the hash database object. `hdb' specifies the hash database object. `bidx' specifies the bucket index of the record. If successful, the return value is true, else, it is false. */ static bool tchdbunlockrecord(TCHDB *hdb, uint8_t bidx){ assert(hdb); if(pthread_rwlock_unlock((pthread_rwlock_t *)hdb->rmtxs + bidx) != 0){ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Lock all records of the hash database object. `hdb' specifies the hash database object. `wr' specifies whether the lock is writer or not. If successful, the return value is true, else, it is false. */ static bool tchdblockallrecords(TCHDB *hdb, bool wr){ assert(hdb); for(int i = 0; i <= UINT8_MAX; i++){ if(wr ? pthread_rwlock_wrlock((pthread_rwlock_t *)hdb->rmtxs + i) != 0 : pthread_rwlock_rdlock((pthread_rwlock_t *)hdb->rmtxs + i) != 0){ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__); while(--i >= 0){ pthread_rwlock_unlock((pthread_rwlock_t *)hdb->rmtxs + i); } return false; } } TCTESTYIELD(); return true; } /* Unlock all records of the hash database object. `hdb' specifies the hash database object. If successful, the return value is true, else, it is false. */ static bool tchdbunlockallrecords(TCHDB *hdb){ assert(hdb); bool err = false; for(int i = UINT8_MAX; i >= 0; i--){ if(pthread_rwlock_unlock((pthread_rwlock_t *)hdb->rmtxs + i)) err = true; } TCTESTYIELD(); if(err){ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } return true; } /* Lock the whole database of the hash database object. `hdb' specifies the hash database object. If successful, the return value is true, else, it is false. */ static bool tchdblockdb(TCHDB *hdb){ assert(hdb); if(pthread_mutex_lock(hdb->dmtx) != 0){ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Unlock the whole database of the hash database object. `hdb' specifies the hash database object. If successful, the return value is true, else, it is false. */ static bool tchdbunlockdb(TCHDB *hdb){ assert(hdb); if(pthread_mutex_unlock(hdb->dmtx) != 0){ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Lock the write ahead logging file of the hash database object. `hdb' specifies the hash database object. If successful, the return value is true, else, it is false. */ static bool tchdblockwal(TCHDB *hdb){ assert(hdb); if(pthread_mutex_lock(hdb->wmtx) != 0){ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Unlock the write ahead logging file of the hash database object. `hdb' specifies the hash database object. If successful, the return value is true, else, it is false. */ static bool tchdbunlockwal(TCHDB *hdb){ assert(hdb); if(pthread_mutex_unlock(hdb->wmtx) != 0){ tchdbsetecode(hdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /************************************************************************************************* * debugging functions *************************************************************************************************/ /* Print meta data of the header into the debugging output. `hdb' specifies the hash database object. */ void tchdbprintmeta(TCHDB *hdb){ assert(hdb); if(hdb->dbgfd < 0) return; int dbgfd = (hdb->dbgfd == UINT16_MAX) ? 1 : hdb->dbgfd; char buf[HDBIOBUFSIZ]; char *wp = buf; wp += sprintf(wp, "META:"); wp += sprintf(wp, " mmtx=%p", (void *)hdb->mmtx); wp += sprintf(wp, " rmtxs=%p", (void *)hdb->rmtxs); wp += sprintf(wp, " dmtx=%p", (void *)hdb->dmtx); wp += sprintf(wp, " wmtx=%p", (void *)hdb->wmtx); wp += sprintf(wp, " eckey=%p", (void *)hdb->eckey); wp += sprintf(wp, " rpath=%s", hdb->rpath ? hdb->rpath : "-"); wp += sprintf(wp, " type=%02X", hdb->type); wp += sprintf(wp, " flags=%02X", hdb->flags); wp += sprintf(wp, " bnum=%llu", (unsigned long long)hdb->bnum); wp += sprintf(wp, " apow=%u", hdb->apow); wp += sprintf(wp, " fpow=%u", hdb->fpow); wp += sprintf(wp, " opts=%u", hdb->opts); wp += sprintf(wp, " path=%s", hdb->path ? hdb->path : "-"); wp += sprintf(wp, " fd=%d", hdb->fd); wp += sprintf(wp, " omode=%u", hdb->omode); wp += sprintf(wp, " rnum=%llu", (unsigned long long)hdb->rnum); wp += sprintf(wp, " fsiz=%llu", (unsigned long long)hdb->fsiz); wp += sprintf(wp, " frec=%llu", (unsigned long long)hdb->frec); wp += sprintf(wp, " dfcur=%llu", (unsigned long long)hdb->dfcur); wp += sprintf(wp, " iter=%llu", (unsigned long long)hdb->iter); wp += sprintf(wp, " map=%p", (void *)hdb->map); wp += sprintf(wp, " msiz=%llu", (unsigned long long)hdb->msiz); wp += sprintf(wp, " ba32=%p", (void *)hdb->ba32); wp += sprintf(wp, " ba64=%p", (void *)hdb->ba64); wp += sprintf(wp, " align=%u", hdb->align); wp += sprintf(wp, " runit=%u", hdb->runit); wp += sprintf(wp, " zmode=%u", hdb->zmode); wp += sprintf(wp, " fbpmax=%d", hdb->fbpmax); wp += sprintf(wp, " fbpool=%p", (void *)hdb->fbpool); wp += sprintf(wp, " fbpnum=%d", hdb->fbpnum); wp += sprintf(wp, " fbpmis=%d", hdb->fbpmis); wp += sprintf(wp, " drpool=%p", (void *)hdb->drpool); wp += sprintf(wp, " drpdef=%p", (void *)hdb->drpdef); wp += sprintf(wp, " drpoff=%llu", (unsigned long long)hdb->drpoff); wp += sprintf(wp, " recc=%p", (void *)hdb->recc); wp += sprintf(wp, " rcnum=%u", hdb->rcnum); wp += sprintf(wp, " ecode=%d", hdb->ecode); wp += sprintf(wp, " fatal=%u", hdb->fatal); wp += sprintf(wp, " inode=%llu", (unsigned long long)(uint64_t)hdb->inode); wp += sprintf(wp, " mtime=%llu", (unsigned long long)(uint64_t)hdb->mtime); wp += sprintf(wp, " dfunit=%u", hdb->dfunit); wp += sprintf(wp, " dfcnt=%u", hdb->dfcnt); wp += sprintf(wp, " tran=%d", hdb->tran); wp += sprintf(wp, " walfd=%d", hdb->walfd); wp += sprintf(wp, " walend=%llu", (unsigned long long)hdb->walend); wp += sprintf(wp, " dbgfd=%d", hdb->dbgfd); wp += sprintf(wp, " cnt_writerec=%lld", (long long)hdb->cnt_writerec); wp += sprintf(wp, " cnt_reuserec=%lld", (long long)hdb->cnt_reuserec); wp += sprintf(wp, " cnt_moverec=%lld", (long long)hdb->cnt_moverec); wp += sprintf(wp, " cnt_readrec=%lld", (long long)hdb->cnt_readrec); wp += sprintf(wp, " cnt_searchfbp=%lld", (long long)hdb->cnt_searchfbp); wp += sprintf(wp, " cnt_insertfbp=%lld", (long long)hdb->cnt_insertfbp); wp += sprintf(wp, " cnt_splicefbp=%lld", (long long)hdb->cnt_splicefbp); wp += sprintf(wp, " cnt_dividefbp=%lld", (long long)hdb->cnt_dividefbp); wp += sprintf(wp, " cnt_mergefbp=%lld", (long long)hdb->cnt_mergefbp); wp += sprintf(wp, " cnt_reducefbp=%lld", (long long)hdb->cnt_reducefbp); wp += sprintf(wp, " cnt_appenddrp=%lld", (long long)hdb->cnt_appenddrp); wp += sprintf(wp, " cnt_deferdrp=%lld", (long long)hdb->cnt_deferdrp); wp += sprintf(wp, " cnt_flushdrp=%lld", (long long)hdb->cnt_flushdrp); wp += sprintf(wp, " cnt_adjrecc=%lld", (long long)hdb->cnt_adjrecc); wp += sprintf(wp, " cnt_defrag=%lld", (long long)hdb->cnt_defrag); wp += sprintf(wp, " cnt_shiftrec=%lld", (long long)hdb->cnt_shiftrec); wp += sprintf(wp, " cnt_trunc=%lld", (long long)hdb->cnt_trunc); *(wp++) = '\n'; tcwrite(dbgfd, buf, wp - buf); } /* Print a record information into the debugging output. `hdb' specifies the hash database object. `rec' specifies the record. */ void tchdbprintrec(TCHDB *hdb, TCHREC *rec){ assert(hdb && rec); if(hdb->dbgfd < 0) return; int dbgfd = (hdb->dbgfd == UINT16_MAX) ? 1 : hdb->dbgfd; char buf[HDBIOBUFSIZ]; char *wp = buf; wp += sprintf(wp, "REC:"); wp += sprintf(wp, " off=%llu", (unsigned long long)rec->off); wp += sprintf(wp, " rsiz=%u", rec->rsiz); wp += sprintf(wp, " magic=%02X", rec->magic); wp += sprintf(wp, " hash=%02X", rec->hash); wp += sprintf(wp, " left=%llu", (unsigned long long)rec->left); wp += sprintf(wp, " right=%llu", (unsigned long long)rec->right); wp += sprintf(wp, " ksiz=%u", rec->ksiz); wp += sprintf(wp, " vsiz=%u", rec->vsiz); wp += sprintf(wp, " psiz=%u", rec->psiz); wp += sprintf(wp, " kbuf=%p", (void *)rec->kbuf); wp += sprintf(wp, " vbuf=%p", (void *)rec->vbuf); wp += sprintf(wp, " boff=%llu", (unsigned long long)rec->boff); wp += sprintf(wp, " bbuf=%p", (void *)rec->bbuf); *(wp++) = '\n'; tcwrite(dbgfd, buf, wp - buf); } // END OF FILE tokyocabinet-1.4.48/configure0000755000175000017500000045434112013574135015230 0ustar mikiomikio#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.68 for tokyocabinet 1.4.48. # # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 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 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" 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 : # 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 export CONFIG_SHELL 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+"$@"} 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_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; } # 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 -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi else as_ln_s='cp -p' 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 if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in #( -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # 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='tokyocabinet' PACKAGE_TARNAME='tokyocabinet' PACKAGE_VERSION='1.4.48' PACKAGE_STRING='tokyocabinet 1.4.48' 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 $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used" >&2 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 tokyocabinet 1.4.48 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/tokyocabinet] --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 tokyocabinet 1.4.48:";; 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 tokyocabinet configure 1.4.48 generated by GNU Autoconf 2.68 Copyright (C) 2010 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 || $as_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 tokyocabinet $as_me 1.4.48, which was generated by GNU Autoconf 2.68. 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" MYLIBRARYFILES="libtokyocabinet.a" MYLIBOBJFILES="tcutil.o tchdb.o tcbdb.o tcfdb.o tctdb.o tcadb.o myconf.o md5.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" MYPCFILES="tokyocabinet.pc" # Building flags 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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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 #include #include /* 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 test -n "$CFLAGS" && MYCFLAGS="$CFLAGS $MYCFLAGS" test -n "$CPPFLAGS" && MYCPPFLAGS="$CPPFLAGS $MYCPPFLAGS" test -n "$LDFLAGS" && MYLDFLAGS="$LDFLAGS $MYLDFLAGS" # 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" { test -f "$ac_path_GREP" && $as_test_x "$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" { test -f "$ac_path_EGREP" && $as_test_x "$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 -ltokyocabinet" >&5 $as_echo_n "checking for main in -ltokyocabinet... " >&6; } if ${ac_cv_lib_tokyocabinet_main+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ltokyocabinet $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_tokyocabinet_main=yes else ac_cv_lib_tokyocabinet_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_tokyocabinet_main" >&5 $as_echo "$ac_cv_lib_tokyocabinet_main" >&6; } if test "x$ac_cv_lib_tokyocabinet_main" = xyes; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: old version of Tokyo Cabinet was detected" >&5 $as_echo "$as_me: WARNING: old version of Tokyo Cabinet was detected" >&2;} 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 libtokyocabinet.$MYLIBVER.$MYLIBREV.0.dylib" MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.$MYLIBVER.dylib" MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.dylib" MYLDLIBPATHENV="DYLD_LIBRARY_PATH" else MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so.$MYLIBVER.$MYLIBREV.0" MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so.$MYLIBVER" MYLIBRARYFILES="$MYLIBRARYFILES libtokyocabinet.so" fi fi #================================================================ # Generic Settings #================================================================ # Export variables # Targets ac_config_files="$ac_config_files Makefile tokyocabinet.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 -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi else as_ln_s='cp -p' 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 if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in #( -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # 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 tokyocabinet $as_me 1.4.48, which was generated by GNU Autoconf 2.68. 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="\\ tokyocabinet config.status 1.4.48 configured by $0, generated by GNU Autoconf 2.68, with options \\"\$ac_cs_config\\" Copyright (C) 2010 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" ;; "tokyocabinet.pc") CONFIG_FILES="$CONFIG_FILES tokyocabinet.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 tokyocabinet-1.4.48/tcamttest.c0000644000175000017500000003626612013574446015504 0ustar mikiomikio/************************************************************************************************* * The test cases of the abstract database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" #define MULDIVNUM 8 // division number of multiple database #define RECBUFSIZ 48 // buffer for records typedef struct { // type of structure for write thread TCADB *adb; int rnum; int id; } TARGWRITE; typedef struct { // type of structure for read thread TCADB *adb; int rnum; int id; } TARGREAD; typedef struct { // type of structure for remove thread TCADB *adb; int rnum; int id; } TARGREMOVE; /* global variables */ const char *g_progname; // program name unsigned int g_randseed; // random seed int g_dbgfd; // debugging output /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void iprintf(const char *format, ...); static void iputchar(int c); static void eprint(TCADB *adb, int line, const char *func); static void sysprint(void); static void setskeltran(ADBSKEL *skel); static int runwrite(int argc, char **argv); static int runread(int argc, char **argv); static int runremove(int argc, char **argv); static int procwrite(const char *name, int tnum, int rnum); static int procread(const char *name, int tnum); static int procremove(const char *name, int tnum); static void *threadwrite(void *targ); static void *threadread(void *targ); static void *threadremove(void *targ); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; const char *ebuf = getenv("TCRNDSEED"); g_randseed = ebuf ? tcatoix(ebuf) : tctime() * 1000; srand(g_randseed); ebuf = getenv("TCDBGFD"); g_dbgfd = ebuf ? tcatoix(ebuf) : UINT16_MAX; if(argc < 2) usage(); int 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], "remove")){ rv = runremove(argc, argv); } else { usage(); } if(rv != 0){ printf("FAILED: TCRNDSEED=%u PID=%d", g_randseed, (int)getpid()); for(int i = 0; i < argc; i++){ printf(" %s", argv[i]); } printf("\n\n"); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: test cases of the abstract database API of Tokyo Cabinet\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s write name tnum rnum\n", g_progname); fprintf(stderr, " %s read name tnum\n", g_progname); fprintf(stderr, " %s remove name tnum\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); } /* print a character and flush the buffer */ static void iputchar(int c){ putchar(c); fflush(stdout); } /* print error message of abstract database */ static void eprint(TCADB *adb, int line, const char *func){ const char *path = adb ? tcadbpath(adb) : NULL; fprintf(stderr, "%s: %s: %d: %s: error\n", g_progname, path ? path : "-", line, func); } /* print system information */ static void sysprint(void){ TCMAP *info = tcsysinfo(); if(info){ tcmapiterinit(info); const char *kbuf; while((kbuf = tcmapiternext2(info)) != NULL){ iprintf("sys_%s: %s\n", kbuf, tcmapiterval2(kbuf)); } tcmapdel(info); } } /* set the transparent skeleton database */ static void setskeltran(ADBSKEL *skel){ memset(skel, 0, sizeof(*skel)); skel->opq = tcadbnew(); skel->del = (void (*)(void *))tcadbdel; skel->open = (bool (*)(void *, const char *))tcadbopen; skel->close = (bool (*)(void *))tcadbclose; skel->put = (bool (*)(void *, const void *, int, const void *, int))tcadbput; skel->putkeep = (bool (*)(void *, const void *, int, const void *, int))tcadbputkeep; skel->putcat = (bool (*)(void *, const void *, int, const void *, int))tcadbputcat; skel->out = (bool (*)(void *, const void *, int))tcadbout; skel->get = (void *(*)(void *, const void *, int, int *))tcadbget; skel->vsiz = (int (*)(void *, const void *, int))tcadbvsiz; skel->iterinit = (bool (*)(void *))tcadbiterinit; skel->iternext = (void *(*)(void *, int *))tcadbiternext; skel->fwmkeys = (TCLIST *(*)(void *, const void *, int, int))tcadbfwmkeys; skel->addint = (int (*)(void *, const void *, int, int))tcadbaddint; skel->adddouble = (double (*)(void *, const void *, int, double))tcadbadddouble; skel->sync = (bool (*)(void *))tcadbsync; skel->optimize = (bool (*)(void *, const char *))tcadboptimize; skel->vanish = (bool (*)(void *))tcadbvanish; skel->copy = (bool (*)(void *, const char *))tcadbcopy; skel->tranbegin = (bool (*)(void *))tcadbtranbegin; skel->trancommit = (bool (*)(void *))tcadbtrancommit; skel->tranabort = (bool (*)(void *))tcadbtranabort; skel->path = (const char *(*)(void *))tcadbpath; skel->rnum = (uint64_t (*)(void *))tcadbrnum; skel->size = (uint64_t (*)(void *))tcadbsize; skel->misc = (TCLIST *(*)(void *, const char *, const TCLIST *))tcadbmisc; skel->putproc = (bool (*)(void *, const void *, int, const void *, int, TCPDPROC, void *))tcadbputproc; skel->foreach = (bool (*)(void *, TCITER, void *))tcadbforeach; } /* parse arguments of write command */ static int runwrite(int argc, char **argv){ char *name = NULL; char *tstr = NULL; char *rstr = NULL; for(int i = 2; i < argc; i++){ if(!name && argv[i][0] == '-'){ usage(); } else if(!name){ name = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!name || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int rv = procwrite(name, tnum, rnum); return rv; } /* parse arguments of read command */ static int runread(int argc, char **argv){ char *name = NULL; char *tstr = NULL; for(int i = 2; i < argc; i++){ if(!name && argv[i][0] == '-'){ usage(); } else if(!name){ name = argv[i]; } else if(!tstr){ tstr = argv[i]; } else { usage(); } } if(!name || !tstr) usage(); int tnum = tcatoix(tstr); if(tnum < 1) usage(); int rv = procread(name, tnum); return rv; } /* parse arguments of remove command */ static int runremove(int argc, char **argv){ char *name = NULL; char *tstr = NULL; for(int i = 2; i < argc; i++){ if(!name && argv[i][0] == '-'){ usage(); } else if(!name){ name = argv[i]; } else if(!tstr){ tstr = argv[i]; } else { usage(); } } if(!name || !tstr) usage(); int tnum = tcatoix(tstr); if(tnum < 1) usage(); int rv = procremove(name, tnum); return rv; } /* perform write command */ static int procwrite(const char *name, int tnum, int rnum){ iprintf("\n seed=%u name=%s tnum=%d rnum=%d\n\n", g_randseed, name, tnum, rnum); bool err = false; double stime = tctime(); TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ eprint(adb, __LINE__, "tcadbsetskel"); err = true; skel.del(skel.opq); } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, MULDIVNUM)){ eprint(adb, __LINE__, "tcadbsetskelmulti"); err = true; } name++; } if(!tcadbopen(adb, name)){ eprint(adb, __LINE__, "tcadbopen"); err = true; } TARGWRITE targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].adb = adb; targs[0].rnum = rnum; targs[0].id = 0; if(threadwrite(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].adb = adb; targs[i].rnum = rnum; targs[i].id = i; if(pthread_create(threads + i, NULL, threadwrite, targs + i) != 0){ eprint(adb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(adb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb)); iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb)); sysprint(); if(!tcadbclose(adb)){ eprint(adb, __LINE__, "tcadbclose"); err = true; } tcadbdel(adb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform read command */ static int procread(const char *name, int tnum){ iprintf("\n seed=%u name=%s tnum=%d\n\n", g_randseed, name, tnum); bool err = false; double stime = tctime(); TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ eprint(adb, __LINE__, "tcadbsetskel"); err = true; skel.del(skel.opq); } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, MULDIVNUM)){ eprint(adb, __LINE__, "tcadbsetskelmulti"); err = true; } name++; } if(!tcadbopen(adb, name)){ eprint(adb, __LINE__, "tcadbopen"); err = true; } int rnum = tcadbrnum(adb) / tnum; TARGREAD targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].adb = adb; targs[0].rnum = rnum; targs[0].id = 0; if(threadread(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].adb = adb; targs[i].rnum = rnum; targs[i].id = i; if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){ eprint(adb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(adb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb)); iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb)); sysprint(); if(!tcadbclose(adb)){ eprint(adb, __LINE__, "tcadbclose"); err = true; } tcadbdel(adb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform remove command */ static int procremove(const char *name, int tnum){ iprintf("\n seed=%u name=%s tnum=%d\n\n", g_randseed, name, tnum); bool err = false; double stime = tctime(); TCADB *adb = tcadbnew(); ADBSKEL skel; if(*name == '@'){ setskeltran(&skel); if(!tcadbsetskel(adb, &skel)){ eprint(adb, __LINE__, "tcadbsetskel"); err = true; skel.del(skel.opq); } name++; } else if(*name == '%'){ if(!tcadbsetskelmulti(adb, MULDIVNUM)){ eprint(adb, __LINE__, "tcadbsetskelmulti"); err = true; } name++; } if(!tcadbopen(adb, name)){ eprint(adb, __LINE__, "tcadbopen"); err = true; } int rnum = tcadbrnum(adb) / tnum; TARGREMOVE targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].adb = adb; targs[0].rnum = rnum; targs[0].id = 0; if(threadremove(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].adb = adb; targs[i].rnum = rnum; targs[i].id = i; if(pthread_create(threads + i, NULL, threadremove, targs + i) != 0){ eprint(adb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(adb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcadbrnum(adb)); iprintf("size: %llu\n", (unsigned long long)tcadbsize(adb)); sysprint(); if(!tcadbclose(adb)){ eprint(adb, __LINE__, "tcadbclose"); err = true; } tcadbdel(adb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* thread the write function */ static void *threadwrite(void *targ){ TCADB *adb = ((TARGWRITE *)targ)->adb; int rnum = ((TARGWRITE *)targ)->rnum; int id = ((TARGWRITE *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", base + i + 1); if(!tcadbput(adb, buf, len, buf, len)){ eprint(adb, __LINE__, "tcadbput"); err = true; break; } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the read function */ static void *threadread(void *targ){ TCADB *adb = ((TARGREAD *)targ)->adb; int rnum = ((TARGREAD *)targ)->rnum; int id = ((TARGREAD *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", base + i + 1); int vsiz; char *vbuf = tcadbget(adb, kbuf, ksiz, &vsiz); if(!vbuf){ eprint(adb, __LINE__, "tcadbget"); err = true; } tcfree(vbuf); if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the remove function */ static void *threadremove(void *targ){ TCADB *adb = ((TARGREMOVE *)targ)->adb; int rnum = ((TARGREMOVE *)targ)->rnum; int id = ((TARGREMOVE *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", base + i + 1); if(!tcadbout(adb, kbuf, ksiz)){ eprint(adb, __LINE__, "tcadbout"); err = true; break; } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } // END OF FILE tokyocabinet-1.4.48/tcbmttest.c0000644000175000017500000015341012013574446015474 0ustar mikiomikio/************************************************************************************************* * The test cases of the B+ tree database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" #define RECBUFSIZ 48 // buffer for records typedef struct { // type of structure for write thread TCBDB *bdb; int rnum; bool rnd; int id; } TARGWRITE; typedef struct { // type of structure for read thread TCBDB *bdb; int rnum; bool wb; bool rnd; int id; } TARGREAD; typedef struct { // type of structure for remove thread TCBDB *bdb; int rnum; bool rnd; int id; } TARGREMOVE; typedef struct { // type of structure for wicked thread TCBDB *bdb; int rnum; bool nc; int id; TCMAP *map; } TARGWICKED; typedef struct { // type of structure for typical thread TCBDB *bdb; int rnum; bool nc; int rratio; int id; } TARGTYPICAL; typedef struct { // type of structure for race thread TCBDB *bdb; int rnum; int id; } TARGRACE; /* global variables */ const char *g_progname; // program name unsigned int g_randseed; // random seed int g_dbgfd; // debugging output /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void iprintf(const char *format, ...); static void iputchar(int c); static void eprint(TCBDB *bdb, int line, const char *func); static void mprint(TCBDB *bdb); static void sysprint(void); static int myrand(int range); static int myrandnd(int range); static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op); static int runwrite(int argc, char **argv); static int runread(int argc, char **argv); static int runremove(int argc, char **argv); static int runwicked(int argc, char **argv); static int runtypical(int argc, char **argv); static int runrace(int argc, char **argv); static int procwrite(const char *path, int tnum, int rnum, int lmemb, int nmemb, int bnum, int apow, int fpow, int opts, int xmsiz, int dfunit, int omode, bool rnd); static int procread(const char *path, int tnum, int xmsiz, int dfunit, int omode, bool wb, bool rnd); static int procremove(const char *path, int tnum, int xmsiz, int dfunit, int omode, bool rnd); static int procwicked(const char *path, int tnum, int rnum, int opts, int omode, bool nc); static int proctypical(const char *path, int tnum, int rnum, int lmemb, int nmemb, int bnum, int apow, int fpow, int opts, int xmsiz, int dfunit, int omode, bool nc, int rratio); static int procrace(const char *path, int tnum, int rnum, int lmemb, int nmemb, int bnum, int apow, int fpow, int opts, int xmsiz, int dfunit, int omode); static void *threadwrite(void *targ); static void *threadread(void *targ); static void *threadremove(void *targ); static void *threadwicked(void *targ); static void *threadtypical(void *targ); static void *threadrace(void *targ); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; const char *ebuf = getenv("TCRNDSEED"); g_randseed = ebuf ? tcatoix(ebuf) : tctime() * 1000; srand(g_randseed); ebuf = getenv("TCDBGFD"); g_dbgfd = ebuf ? tcatoix(ebuf) : UINT16_MAX; if(argc < 2) usage(); int 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], "remove")){ rv = runremove(argc, argv); } else if(!strcmp(argv[1], "wicked")){ rv = runwicked(argc, argv); } else if(!strcmp(argv[1], "typical")){ rv = runtypical(argc, argv); } else if(!strcmp(argv[1], "race")){ rv = runrace(argc, argv); } else { usage(); } if(rv != 0){ printf("FAILED: TCRNDSEED=%u PID=%d", g_randseed, (int)getpid()); for(int i = 0; i < argc; i++){ printf(" %s", argv[i]); } printf("\n\n"); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: test cases of the B+ tree database API of Tokyo Cabinet\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s write [-tl] [-td|-tb|-tt|-tx] [-xm num] [-df num] [-nl|-nb] [-rnd]" " path tnum rnum [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname); fprintf(stderr, " %s read [-xm num] [-df num] [-nl|-nb] [-wb] [-rnd] path tnum\n", g_progname); fprintf(stderr, " %s remove [-xm num] [-df num] [-nl|-nb] [-rnd] path tnum\n", g_progname); fprintf(stderr, " %s wicked [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] [-nc] path tnum rnum\n", g_progname); fprintf(stderr, " %s typical [-tl] [-td|-tb|-tt|-tx] [-xm num] [-df num] [-nl|-nb]" " [-nc] [-rr num] path tnum rnum [lmemb [nmemb [bnum [apow [fpow]]]]]\n", g_progname); fprintf(stderr, " %s race [-tl] [-td|-tb|-tt|-tx] [-xm num] [-df num] [-nl|-nb]" " path tnum rnum [lmemb [nmemb [bnum [apow [fpow]]]]]\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); } /* print a character and flush the buffer */ static void iputchar(int c){ putchar(c); fflush(stdout); } /* print error message of hash database */ static void eprint(TCBDB *bdb, int line, const char *func){ const char *path = tcbdbpath(bdb); int ecode = tcbdbecode(bdb); fprintf(stderr, "%s: %s: %d: %s: error: %d: %s\n", g_progname, path ? path : "-", line, func, ecode, tcbdberrmsg(ecode)); } /* print members of hash database */ static void mprint(TCBDB *bdb){ if(bdb->hdb->cnt_writerec < 0) return; iprintf("max leaf member: %d\n", tcbdblmemb(bdb)); iprintf("max node member: %d\n", tcbdbnmemb(bdb)); iprintf("leaf number: %lld\n", (long long)tcbdblnum(bdb)); iprintf("node number: %lld\n", (long long)tcbdbnnum(bdb)); iprintf("bucket number: %lld\n", (long long)tcbdbbnum(bdb)); iprintf("used bucket number: %lld\n", (long long)tcbdbbnumused(bdb)); iprintf("cnt_saveleaf: %lld\n", (long long)bdb->cnt_saveleaf); iprintf("cnt_loadleaf: %lld\n", (long long)bdb->cnt_loadleaf); iprintf("cnt_killleaf: %lld\n", (long long)bdb->cnt_killleaf); iprintf("cnt_adjleafc: %lld\n", (long long)bdb->cnt_adjleafc); iprintf("cnt_savenode: %lld\n", (long long)bdb->cnt_savenode); iprintf("cnt_loadnode: %lld\n", (long long)bdb->cnt_loadnode); iprintf("cnt_adjnodec: %lld\n", (long long)bdb->cnt_adjnodec); iprintf("cnt_writerec: %lld\n", (long long)bdb->hdb->cnt_writerec); iprintf("cnt_reuserec: %lld\n", (long long)bdb->hdb->cnt_reuserec); iprintf("cnt_moverec: %lld\n", (long long)bdb->hdb->cnt_moverec); iprintf("cnt_readrec: %lld\n", (long long)bdb->hdb->cnt_readrec); iprintf("cnt_searchfbp: %lld\n", (long long)bdb->hdb->cnt_searchfbp); iprintf("cnt_insertfbp: %lld\n", (long long)bdb->hdb->cnt_insertfbp); iprintf("cnt_splicefbp: %lld\n", (long long)bdb->hdb->cnt_splicefbp); iprintf("cnt_dividefbp: %lld\n", (long long)bdb->hdb->cnt_dividefbp); iprintf("cnt_mergefbp: %lld\n", (long long)bdb->hdb->cnt_mergefbp); iprintf("cnt_reducefbp: %lld\n", (long long)bdb->hdb->cnt_reducefbp); iprintf("cnt_appenddrp: %lld\n", (long long)bdb->hdb->cnt_appenddrp); iprintf("cnt_deferdrp: %lld\n", (long long)bdb->hdb->cnt_deferdrp); iprintf("cnt_flushdrp: %lld\n", (long long)bdb->hdb->cnt_flushdrp); iprintf("cnt_adjrecc: %lld\n", (long long)bdb->hdb->cnt_adjrecc); iprintf("cnt_defrag: %lld\n", (long long)bdb->hdb->cnt_defrag); iprintf("cnt_shiftrec: %lld\n", (long long)bdb->hdb->cnt_shiftrec); iprintf("cnt_trunc: %lld\n", (long long)bdb->hdb->cnt_trunc); } /* print system information */ static void sysprint(void){ TCMAP *info = tcsysinfo(); if(info){ tcmapiterinit(info); const char *kbuf; while((kbuf = tcmapiternext2(info)) != NULL){ iprintf("sys_%s: %s\n", kbuf, tcmapiterval2(kbuf)); } tcmapdel(info); } } /* get a random number */ static int myrand(int range){ if(range < 2) return 0; int high = (unsigned int)rand() >> 4; int low = range * (rand() / (RAND_MAX + 1.0)); low &= (unsigned int)INT_MAX >> 4; return (high + low) % range; } /* get a random number based on normal distribution */ static int myrandnd(int range){ int num = (int)tcdrandnd(range >> 1, range / 10); return (num < 0 || num >= range) ? 0 : num; } /* iterator function */ static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op){ unsigned int sum = 0; while(--ksiz >= 0){ sum += ((char *)kbuf)[ksiz]; } while(--vsiz >= 0){ sum += ((char *)vbuf)[vsiz]; } return myrand(100 + (sum & 0xff)) > 0; } /* parse arguments of write command */ static int runwrite(int argc, char **argv){ char *path = NULL; char *tstr = NULL; char *rstr = NULL; char *lmstr = NULL; char *nmstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; int opts = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tl")){ opts |= BDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= BDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= BDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= BDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= BDBTEXCODEC; } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!lmstr){ lmstr = argv[i]; } else if(!nmstr){ nmstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int lmemb = lmstr ? tcatoix(lmstr) : -1; int nmemb = nmstr ? tcatoix(nmstr) : -1; int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = procwrite(path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, xmsiz, dfunit, omode, rnd); return rv; } /* parse arguments of read command */ static int runread(int argc, char **argv){ char *path = NULL; char *tstr = NULL; int xmsiz = -1; int dfunit = 0; int omode = 0; bool rnd = false; bool wb = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else if(!strcmp(argv[i], "-wb")){ wb = true; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else { usage(); } } if(!path || !tstr) usage(); int tnum = tcatoix(tstr); if(tnum < 1) usage(); int rv = procread(path, tnum, xmsiz, dfunit, omode, wb, rnd); return rv; } /* parse arguments of remove command */ static int runremove(int argc, char **argv){ char *path = NULL; char *tstr = NULL; int xmsiz = -1; int dfunit = 0; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else { usage(); } } if(!path || !tstr) usage(); int tnum = tcatoix(tstr); if(tnum < 1) usage(); int rv = procremove(path, tnum, xmsiz, dfunit, omode, rnd); return rv; } /* parse arguments of wicked command */ static int runwicked(int argc, char **argv){ char *path = NULL; char *tstr = NULL; char *rstr = NULL; int opts = 0; int omode = 0; bool nc = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tl")){ opts |= BDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= BDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= BDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= BDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= BDBTEXCODEC; } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else if(!strcmp(argv[i], "-nc")){ nc = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!path || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int rv = procwicked(path, tnum, rnum, opts, omode, nc); return rv; } /* parse arguments of typical command */ static int runtypical(int argc, char **argv){ char *path = NULL; char *tstr = NULL; char *rstr = NULL; char *lmstr = NULL; char *nmstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; int opts = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; int rratio = -1; bool nc = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tl")){ opts |= BDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= BDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= BDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= BDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= BDBTEXCODEC; } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else if(!strcmp(argv[i], "-nc")){ nc = true; } else if(!strcmp(argv[i], "-rr")){ if(++i >= argc) usage(); rratio = tcatoix(argv[i]); } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!lmstr){ lmstr = argv[i]; } else if(!nmstr){ nmstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int lmemb = lmstr ? tcatoix(lmstr) : -1; int nmemb = nmstr ? tcatoix(nmstr) : -1; int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = proctypical(path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, xmsiz, dfunit, omode, nc, rratio); return rv; } /* parse arguments of race command */ static int runrace(int argc, char **argv){ char *path = NULL; char *tstr = NULL; char *rstr = NULL; char *lmstr = NULL; char *nmstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; int opts = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tl")){ opts |= BDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= BDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= BDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= BDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= BDBTEXCODEC; } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!lmstr){ lmstr = argv[i]; } else if(!nmstr){ nmstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int lmemb = lmstr ? tcatoix(lmstr) : -1; int nmemb = nmstr ? tcatoix(nmstr) : -1; int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = procrace(path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, xmsiz, dfunit, omode); return rv; } /* perform write command */ static int procwrite(const char *path, int tnum, int rnum, int lmemb, int nmemb, int bnum, int apow, int fpow, int opts, int xmsiz, int dfunit, int omode, bool rnd){ iprintf("\n seed=%u path=%s tnum=%d rnum=%d lmemb=%d nmemb=%d" " bnum=%d apow=%d fpow=%d opts=%d xmsiz=%d dfunit=%d omode=%d rnd=%d\n\n", g_randseed, path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, xmsiz, dfunit, omode, rnd); bool err = false; double stime = tctime(); TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(!tcbdbsetmutex(bdb)){ eprint(bdb, __LINE__, "tcbdbsetmutex"); err = true; } if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcodecfunc"); err = true; } if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){ eprint(bdb, __LINE__, "tcbdbtune"); err = true; } if(xmsiz >= 0 && !tcbdbsetxmsiz(bdb, xmsiz)){ eprint(bdb, __LINE__, "tcbdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tcbdbsetdfunit(bdb, dfunit)){ eprint(bdb, __LINE__, "tcbdbsetdfunit"); err = true; } if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){ eprint(bdb, __LINE__, "tcbdbopen"); err = true; } TARGWRITE targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].bdb = bdb; targs[0].rnum = rnum; targs[0].rnd = rnd; targs[0].id = 0; if(threadwrite(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].bdb = bdb; targs[i].rnum = rnum; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadwrite, targs + i) != 0){ eprint(bdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(bdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb)); iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb)); mprint(bdb); sysprint(); if(!tcbdbclose(bdb)){ eprint(bdb, __LINE__, "tcbdbclose"); err = true; } tcbdbdel(bdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform read command */ static int procread(const char *path, int tnum, int xmsiz, int dfunit, int omode, bool wb, bool rnd){ iprintf("\n seed=%u path=%s tnum=%d xmsiz=%d dfunit=%d omode=%d" " wb=%d rnd=%d\n\n", g_randseed, path, tnum, xmsiz, dfunit, omode, wb, rnd); bool err = false; double stime = tctime(); TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(!tcbdbsetmutex(bdb)){ eprint(bdb, __LINE__, "tcbdbsetmutex"); err = true; } if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcodecfunc"); err = true; } if(xmsiz >= 0 && !tcbdbsetxmsiz(bdb, xmsiz)){ eprint(bdb, __LINE__, "tcbdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tcbdbsetdfunit(bdb, dfunit)){ eprint(bdb, __LINE__, "tcbdbsetdfunit"); err = true; } if(!tcbdbopen(bdb, path, BDBOREADER | omode)){ eprint(bdb, __LINE__, "tcbdbopen"); err = true; } int rnum = tcbdbrnum(bdb) / tnum; TARGREAD targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].bdb = bdb; targs[0].rnum = rnum; targs[0].wb = wb; targs[0].rnd = rnd; targs[0].id = 0; if(threadread(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].bdb = bdb; targs[i].rnum = rnum; targs[i].wb = wb; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){ eprint(bdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(bdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb)); iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb)); mprint(bdb); sysprint(); if(!tcbdbclose(bdb)){ eprint(bdb, __LINE__, "tcbdbclose"); err = true; } tcbdbdel(bdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform remove command */ static int procremove(const char *path, int tnum, int xmsiz, int dfunit, int omode, bool rnd){ iprintf("\n seed=%u path=%s tnum=%d xmsiz=%d dfunit=%d omode=%d" " rnd=%d\n\n", g_randseed, path, tnum, xmsiz, dfunit, omode, rnd); bool err = false; double stime = tctime(); TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(!tcbdbsetmutex(bdb)){ eprint(bdb, __LINE__, "tcbdbsetmutex"); err = true; } if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcodecfunc"); err = true; } if(xmsiz >= 0 && !tcbdbsetxmsiz(bdb, xmsiz)){ eprint(bdb, __LINE__, "tcbdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tcbdbsetdfunit(bdb, dfunit)){ eprint(bdb, __LINE__, "tcbdbsetdfunit"); err = true; } if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){ eprint(bdb, __LINE__, "tcbdbopen"); err = true; } int rnum = tcbdbrnum(bdb) / tnum; TARGREMOVE targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].bdb = bdb; targs[0].rnum = rnum; targs[0].rnd = rnd; targs[0].id = 0; if(threadremove(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].bdb = bdb; targs[i].rnum = rnum; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadremove, targs + i) != 0){ eprint(bdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(bdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb)); iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb)); mprint(bdb); sysprint(); if(!tcbdbclose(bdb)){ eprint(bdb, __LINE__, "tcbdbclose"); err = true; } tcbdbdel(bdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform wicked command */ static int procwicked(const char *path, int tnum, int rnum, int opts, int omode, bool nc){ iprintf("\n seed=%u path=%s tnum=%d rnum=%d opts=%d omode=%d nc=%d\n\n", g_randseed, path, tnum, rnum, opts, omode, nc); bool err = false; double stime = tctime(); TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(!tcbdbsetmutex(bdb)){ eprint(bdb, __LINE__, "tcbdbsetmutex"); err = true; } if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcodecfunc"); err = true; } if(!tcbdbtune(bdb, 10, 10, rnum / 50, 10, -1, opts)){ eprint(bdb, __LINE__, "tcbdbtune"); err = true; } if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){ eprint(bdb, __LINE__, "tcbdbopen"); err = true; } TARGWICKED targs[tnum]; pthread_t threads[tnum]; TCMAP *map = tcmapnew(); if(tnum == 1){ targs[0].bdb = bdb; targs[0].rnum = rnum; targs[0].nc = nc; targs[0].id = 0; targs[0].map = map; if(threadwicked(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].bdb = bdb; targs[i].rnum = rnum; targs[i].nc = nc; targs[i].id = i; targs[i].map = map; if(pthread_create(threads + i, NULL, threadwicked, targs + i) != 0){ eprint(bdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(bdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } if(!nc){ if(!tcbdbsync(bdb)){ eprint(bdb, __LINE__, "tcbdbsync"); err = true; } if(tcbdbrnum(bdb) != tcmaprnum(map)){ eprint(bdb, __LINE__, "(validation)"); err = true; } int end = rnum * tnum; for(int i = 1; i <= end && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", i - 1); int vsiz; const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz); int rsiz; char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz); if(vbuf){ iputchar('.'); if(!rbuf){ eprint(bdb, __LINE__, "tcbdbget"); err = true; } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(bdb, __LINE__, "(validation)"); err = true; } } else { iputchar('*'); if(rbuf || tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "(validation)"); err = true; } } tcfree(rbuf); if(i % 50 == 0) iprintf(" (%08d)\n", i); } if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum); } tcmapdel(map); iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb)); iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb)); mprint(bdb); sysprint(); if(!tcbdbclose(bdb)){ eprint(bdb, __LINE__, "tcbdbclose"); err = true; } tcbdbdel(bdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform typical command */ static int proctypical(const char *path, int tnum, int rnum, int lmemb, int nmemb, int bnum, int apow, int fpow, int opts, int xmsiz, int dfunit, int omode, bool nc, int rratio){ iprintf("\n seed=%u path=%s tnum=%d rnum=%d lmemb=%d nmemb=%d" " bnum=%d apow=%d fpow=%d opts=%d xmsiz=%d dfunit=%d omode=%d" " nc=%d rratio=%d\n\n", g_randseed, path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, xmsiz, dfunit, omode, nc, rratio); bool err = false; double stime = tctime(); TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(!tcbdbsetmutex(bdb)){ eprint(bdb, __LINE__, "tcbdbsetmutex"); err = true; } if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcodecfunc"); err = true; } if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){ eprint(bdb, __LINE__, "tcbdbtune"); err = true; } if(xmsiz >= 0 && !tcbdbsetxmsiz(bdb, xmsiz)){ eprint(bdb, __LINE__, "tcbdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tcbdbsetdfunit(bdb, dfunit)){ eprint(bdb, __LINE__, "tcbdbsetdfunit"); err = true; } if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){ eprint(bdb, __LINE__, "tcbdbopen"); err = true; } TARGTYPICAL targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].bdb = bdb; targs[0].rnum = rnum; targs[0].nc = nc; targs[0].rratio = rratio; targs[0].id = 0; if(threadtypical(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].bdb = bdb; targs[i].rnum = rnum; targs[i].nc = nc; targs[i].rratio = rratio; targs[i].id = i; if(pthread_create(threads + i, NULL, threadtypical, targs + i) != 0){ eprint(bdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(bdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb)); iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb)); mprint(bdb); sysprint(); if(!tcbdbclose(bdb)){ eprint(bdb, __LINE__, "tcbdbclose"); err = true; } tcbdbdel(bdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform race command */ static int procrace(const char *path, int tnum, int rnum, int lmemb, int nmemb, int bnum, int apow, int fpow, int opts, int xmsiz, int dfunit, int omode){ iprintf("\n seed=%u path=%s tnum=%d rnum=%d lmemb=%d nmemb=%d" " bnum=%d apow=%d fpow=%d opts=%d xmsiz=%d dfunit=%d omode=%d\n\n", g_randseed, path, tnum, rnum, lmemb, nmemb, bnum, apow, fpow, opts, xmsiz, dfunit, omode); bool err = false; double stime = tctime(); TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(!tcbdbsetmutex(bdb)){ eprint(bdb, __LINE__, "tcbdbsetmutex"); err = true; } if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcodecfunc"); err = true; } if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){ eprint(bdb, __LINE__, "tcbdbtune"); err = true; } if(xmsiz >= 0 && !tcbdbsetxmsiz(bdb, xmsiz)){ eprint(bdb, __LINE__, "tcbdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tcbdbsetdfunit(bdb, dfunit)){ eprint(bdb, __LINE__, "tcbdbsetdfunit"); err = true; } if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){ eprint(bdb, __LINE__, "tcbdbopen"); err = true; } TARGRACE targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].bdb = bdb; targs[0].rnum = rnum; targs[0].id = 0; if(threadrace(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].bdb = bdb; targs[i].rnum = rnum; targs[i].id = i; if(pthread_create(threads + i, NULL, threadrace, targs + i) != 0){ eprint(bdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(bdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb)); iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb)); mprint(bdb); sysprint(); if(!tcbdbclose(bdb)){ eprint(bdb, __LINE__, "tcbdbclose"); err = true; } tcbdbdel(bdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* thread the write function */ static void *threadwrite(void *targ){ TCBDB *bdb = ((TARGWRITE *)targ)->bdb; int rnum = ((TARGWRITE *)targ)->rnum; bool rnd = ((TARGWRITE *)targ)->rnd; int id = ((TARGWRITE *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) : i)); if(!tcbdbput(bdb, buf, len, buf, len)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; break; } if(id <= 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the read function */ static void *threadread(void *targ){ TCBDB *bdb = ((TARGREAD *)targ)->bdb; int rnum = ((TARGREAD *)targ)->rnum; bool wb = ((TARGREAD *)targ)->wb; bool rnd = ((TARGREAD *)targ)->rnd; int id = ((TARGREAD *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrandnd(i) : i)); int vsiz; if(wb){ int vsiz; const char *vbuf = tcbdbget3(bdb, kbuf, ksiz, &vsiz); if(!vbuf && (!rnd || tcbdbecode(bdb) != TCENOREC)){ eprint(bdb, __LINE__, "tcbdbget3"); err = true; } } else { char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz); if(!vbuf && (!rnd || tcbdbecode(bdb) != TCENOREC)){ eprint(bdb, __LINE__, "tcbdbget"); err = true; } tcfree(vbuf); } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the remove function */ static void *threadremove(void *targ){ TCBDB *bdb = ((TARGREMOVE *)targ)->bdb; int rnum = ((TARGREMOVE *)targ)->rnum; bool rnd = ((TARGREMOVE *)targ)->rnd; int id = ((TARGREMOVE *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrand(i + 1) : i)); if(!tcbdbout(bdb, kbuf, ksiz) && (!rnd || tcbdbecode(bdb) != TCENOREC)){ eprint(bdb, __LINE__, "tcbdbout"); err = true; break; } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the wicked function */ static void *threadwicked(void *targ){ TCBDB *bdb = ((TARGWICKED *)targ)->bdb; int rnum = ((TARGWICKED *)targ)->rnum; bool nc = ((TARGWICKED *)targ)->nc; int id = ((TARGWICKED *)targ)->id; TCMAP *map = ((TARGWICKED *)targ)->map; BDBCUR *cur = tcbdbcurnew(bdb); bool err = false; for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum * (id + 1))); char vbuf[RECBUFSIZ]; int vsiz = myrand(RECBUFSIZ); memset(vbuf, '*', vsiz); vbuf[vsiz] = '\0'; char *rbuf; if(!nc) tcglobalmutexlock(); switch(myrand(16)){ case 0: if(id == 0) iputchar('0'); if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; } if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: if(id == 0) iputchar('1'); if(!tcbdbput2(bdb, kbuf, vbuf)){ eprint(bdb, __LINE__, "tcbdbput2"); err = true; } if(!nc) tcmapput2(map, kbuf, vbuf); break; case 2: if(id == 0) iputchar('2'); if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbputkeep"); err = true; } if(!nc) tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 3: if(id == 0) iputchar('3'); if(!tcbdbputkeep2(bdb, kbuf, vbuf) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbputkeep2"); err = true; } if(!nc) tcmapputkeep2(map, kbuf, vbuf); break; case 4: if(id == 0) iputchar('4'); if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbputcat"); err = true; } if(!nc) tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 5: if(id == 0) iputchar('5'); if(!tcbdbputcat2(bdb, kbuf, vbuf)){ eprint(bdb, __LINE__, "tcbdbputcat2"); err = true; } if(!nc) tcmapputcat2(map, kbuf, vbuf); break; case 6: if(id == 0) iputchar('6'); if(nc){ if(!tcbdbputdup(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbputdup"); err = true; } } break; case 7: if(id == 0) iputchar('7'); if(nc){ if(!tcbdbputdup2(bdb, kbuf, vbuf)){ eprint(bdb, __LINE__, "tcbdbputdup2"); err = true; } } break; case 8: if(id == 0) iputchar('8'); if(myrand(2) == 0){ if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout"); err = true; } if(!nc) tcmapout(map, kbuf, ksiz); } break; case 9: if(id == 0) iputchar('9'); if(myrand(2) == 0){ if(!tcbdbout2(bdb, kbuf) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout2"); err = true; } if(!nc) tcmapout2(map, kbuf); } break; case 10: if(id == 0) iputchar('A'); if(!(rbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz))){ if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbget"); err = true; } rbuf = tcsprintf("[%d]", myrand(i + 1)); vsiz = strlen(rbuf); } vsiz += myrand(vsiz); if(myrand(3) == 0) vsiz += PATH_MAX; rbuf = tcrealloc(rbuf, vsiz + 1); for(int j = 0; j < vsiz; j++){ rbuf[j] = myrand(0x100); } if(!tcbdbput(bdb, kbuf, ksiz, rbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; } if(!nc) tcmapput(map, kbuf, ksiz, rbuf, vsiz); tcfree(rbuf); break; case 11: if(id == 0) iputchar('B'); if(!(rbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz)) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbget"); err = true; } tcfree(rbuf); break; case 12: if(id == 0) iputchar('C'); if(!(rbuf = tcbdbget2(bdb, kbuf)) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbget2"); err = true; } tcfree(rbuf); break; case 13: if(id == 0) iputchar('D'); if(!tcbdbget3(bdb, kbuf, ksiz, &vsiz) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbget3"); err = true; } break; case 14: if(id == 0) iputchar('E'); if(myrand(rnum / 50) == 0){ switch(myrand(5)){ case 0: if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurfirst"); err = true; } break; case 1: if(!tcbdbcurlast(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurlast"); err = true; } break; default: if(!tcbdbcurjump(cur, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurjump"); err = true; } break; } } TCXSTR *ikey = tcxstrnew(); TCXSTR *ival = tcxstrnew(); for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){ if(j % 3 == 0){ if(!tcbdbcurrec(cur, ikey, ival)){ int ecode = tcbdbecode(bdb); if(ecode != TCEINVALID && ecode != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurrec"); err = true; } } } else { int iksiz; if(!tcbdbcurkey3(cur, &iksiz)){ int ecode = tcbdbecode(bdb); if(ecode != TCEINVALID && ecode != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurkey3"); err = true; } } } if(myrand(5) == 0){ if(!tcbdbcurprev(cur)){ int ecode = tcbdbecode(bdb); if(ecode != TCEINVALID && ecode != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurprev"); err = true; } } } else { if(!tcbdbcurnext(cur)){ int ecode = tcbdbecode(bdb); if(ecode != TCEINVALID && ecode != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurnext"); err = true; } } } } tcxstrdel(ival); tcxstrdel(ikey); break; default: if(id == 0) iputchar('@'); if(tcbdbtranbegin(bdb)){ if(myrand(2) == 0){ if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; } if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz); } else { if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout"); err = true; } if(!nc) tcmapout(map, kbuf, ksiz); } if(nc && myrand(2) == 0){ if(!tcbdbtranabort(bdb)){ eprint(bdb, __LINE__, "tcbdbtranabort"); err = true; } } else { if(!tcbdbtrancommit(bdb)){ eprint(bdb, __LINE__, "tcbdbtrancommit"); err = true; } } } else { eprint(bdb, __LINE__, "tcbdbtranbegin"); err = true; } if(myrand(1000) == 0){ if(!tcbdbforeach(bdb, iterfunc, NULL)){ eprint(bdb, __LINE__, "tcbdbforeach"); err = true; } } if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX); break; } if(!nc) tcglobalmutexunlock(); if(id == 0){ if(i % 50 == 0) iprintf(" (%08d)\n", i); if(id == 0 && i == rnum / 4){ if(!tcbdboptimize(bdb, -1, -1, -1, -1, -1, -1) && tcbdbecode(bdb) != TCEINVALID){ eprint(bdb, __LINE__, "tcbdboptimize"); err = true; } if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurfirst"); err = true; } } } } tcbdbcurdel(cur); return err ? "error" : NULL; } /* thread the typical function */ static void *threadtypical(void *targ){ TCBDB *bdb = ((TARGTYPICAL *)targ)->bdb; int rnum = ((TARGTYPICAL *)targ)->rnum; bool nc = ((TARGTYPICAL *)targ)->nc; int rratio = ((TARGTYPICAL *)targ)->rratio; int id = ((TARGTYPICAL *)targ)->id; bool err = false; TCMAP *map = (!nc && id == 0) ? tcmapnew2(rnum + 1) : NULL; int base = id * rnum; int mrange = tclmax(50 + rratio, 100); BDBCUR *cur = tcbdbcurnew(bdb); for(int i = 1; !err && i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", base + myrandnd(i)); int rnd = myrand(mrange); if(rnd < 10){ if(!tcbdbput(bdb, buf, len, buf, len)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; } if(map) tcmapput(map, buf, len, buf, len); } else if(rnd < 15){ if(!tcbdbputkeep(bdb, buf, len, buf, len) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbputkeep"); err = true; } if(map) tcmapputkeep(map, buf, len, buf, len); } else if(rnd < 20){ if(!tcbdbputcat(bdb, buf, len, buf, len)){ eprint(bdb, __LINE__, "tcbdbputcat"); err = true; } if(map) tcmapputcat(map, buf, len, buf, len); } else if(rnd < 25){ if(!tcbdbout(bdb, buf, len) && tcbdbecode(bdb) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout"); err = true; } if(map) tcmapout(map, buf, len); } else if(rnd < 27){ switch(myrand(3)){ case 0: if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurfirst"); err = true; } for(int j = 0; !err && j < 10; j++){ int ksiz; char *kbuf = tcbdbcurkey(cur, &ksiz); if(kbuf){ int vsiz; char *vbuf = tcbdbcurval(cur, &vsiz); if(vbuf){ tcfree(vbuf); } else if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurval"); err = true; } tcfree(kbuf); } else if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurkey"); err = true; } tcbdbcurnext(cur); } break; case 1: if(!tcbdbcurlast(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurlast"); err = true; } for(int j = 0; !err && j < 10; j++){ int ksiz; char *kbuf = tcbdbcurkey(cur, &ksiz); if(kbuf){ int vsiz; char *vbuf = tcbdbcurval(cur, &vsiz); if(vbuf){ tcfree(vbuf); } else if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurval"); err = true; } tcfree(kbuf); } else if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurkey"); err = true; } tcbdbcurprev(cur); } break; case 2: if(!tcbdbcurjump(cur, buf, len) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurjump"); err = true; } for(int j = 0; !err && j < 10; j++){ int ksiz; char *kbuf = tcbdbcurkey(cur, &ksiz); if(kbuf){ int vsiz; char *vbuf = tcbdbcurval(cur, &vsiz); if(vbuf){ tcfree(vbuf); } else if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurval"); err = true; } tcfree(kbuf); } else if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurkey"); err = true; } tcbdbcurnext(cur); } break; } } else { int vsiz; char *vbuf = tcbdbget(bdb, buf, len, &vsiz); if(vbuf){ if(map){ int msiz; const char *mbuf = tcmapget(map, buf, len, &msiz); if(!mbuf || msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){ eprint(bdb, __LINE__, "(validation)"); err = true; } } tcfree(vbuf); } else { if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbget"); err = true; } if(map && tcmapget(map, buf, len, &vsiz)){ eprint(bdb, __LINE__, "(validation)"); err = true; } } } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } tcbdbcurdel(cur); if(map){ tcmapiterinit(map); int ksiz; const char *kbuf; while(!err && (kbuf = tcmapiternext(map, &ksiz)) != NULL){ int vsiz; char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz); if(vbuf){ int msiz; const char *mbuf = tcmapget(map, kbuf, ksiz, &msiz); if(!mbuf || msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){ eprint(bdb, __LINE__, "(validation)"); err = true; } tcfree(vbuf); } else { eprint(bdb, __LINE__, "(validation)"); err = true; } } tcmapdel(map); } return err ? "error" : NULL; } /* thread the race function */ static void *threadrace(void *targ){ TCBDB *bdb = ((TARGRACE *)targ)->bdb; int rnum = ((TARGRACE *)targ)->rnum; int id = ((TARGRACE *)targ)->id; bool err = false; int mid = rnum * 2; for(int i = 1; !err && i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%d", myrandnd(i)); int rnd = myrand(100); if(rnd < 10){ if(!tcbdbputkeep(bdb, buf, len, buf, len) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbputkeep"); err = true; } } else if(rnd < 20){ if(!tcbdbputcat(bdb, buf, len, buf, len)){ eprint(bdb, __LINE__, "tcbdbputcat"); err = true; } } else if(rnd < 30){ if(!tcbdbputdup(bdb, buf, len, buf, len)){ eprint(bdb, __LINE__, "tcbdbputdup"); err = true; } } else if(rnd < 40){ if(!tcbdbputdupback(bdb, buf, len, buf, len)){ eprint(bdb, __LINE__, "tcbdbputdupback"); err = true; } } else if(rnd < 50){ if(!tcbdbout(bdb, buf, len) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout"); err = true; } } else { if(myrand(10) == 0){ int rsiz = myrand(1024); char *rbuf = tcmalloc(rsiz + 1); for(int j = 0; j < rsiz; j++){ rbuf[j] = myrand('z' - 'a') + 'a'; } if(myrand(2) == 0){ if(!tcbdbput(bdb, buf, len, rbuf, rsiz)){ eprint(bdb, __LINE__, "tcbdbputcat"); err = true; } } else { if(!tcbdbputcat(bdb, buf, len, rbuf, rsiz)){ eprint(bdb, __LINE__, "tcbdbputcat"); err = true; } } tcfree(rbuf); } else { if(!tcbdbput(bdb, buf, len, buf, len)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; } } } if(id == 0){ if(myrand(mid) == 0){ iprintf("[v]"); if(!tcbdbvanish(bdb)){ eprint(bdb, __LINE__, "tcbdbvanish"); err = true; } } if(myrand(mid) == 0){ iprintf("[o]"); if(!tcbdboptimize(bdb, -1, -1, myrand(rnum) + 1, myrand(10), myrand(10), 0)){ eprint(bdb, __LINE__, "tcbdbvanish"); err = true; } } if(myrand(mid) == 0){ iprintf("[d]"); if(!tcbdbdefrag(bdb, -1)){ eprint(bdb, __LINE__, "tcbdbdefrag"); err = true; } } if(myrand(mid) == 0){ iprintf("[i]"); BDBCUR *cur = tcbdbcurnew(bdb); if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurfirst"); err = true; } char *kbuf; int ksiz; while((kbuf = tcbdbcurkey(cur, &ksiz)) != NULL){ int vsiz; char *vbuf = tcbdbcurval(cur, &vsiz); if(vbuf){ tcfree(vbuf); } else if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbget"); err = true; } tcfree(kbuf); tcbdbcurnext(cur); } if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "(validation)"); err = true; } tcbdbcurdel(cur); } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } } return err ? "error" : NULL; } // END OF FILE tokyocabinet-1.4.48/tcucodec.c0000644000175000017500000011072512013574446015256 0ustar mikiomikio/************************************************************************************************* * Popular encoders and decoders of the utility API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include "myconf.h" /* global variables */ const char *g_progname; // program name /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void eprintf(const char *format, ...); static int runurl(int argc, char **argv); static int runbase(int argc, char **argv); static int runquote(int argc, char **argv); static int runmime(int argc, char **argv); static int runhex(int argc, char **argv); static int runpack(int argc, char **argv); static int runtcbs(int argc, char **argv); static int runzlib(int argc, char **argv); static int runbzip(int argc, char **argv); static int runxml(int argc, char **argv); static int runcstr(int argc, char **argv); static int runucs(int argc, char **argv); static int runhash(int argc, char **argv); static int runcipher(int argc, char **argv); static int rundate(int argc, char **argv); static int runtmpl(int argc, char **argv); static int runconf(int argc, char **argv); static int procurl(const char *ibuf, int isiz, bool dec, bool br, const char *base); static int procbase(const char *ibuf, int isiz, bool dec); static int procquote(const char *ibuf, int isiz, bool dec); static int procmime(const char *ibuf, int isiz, bool dec, const char *ename, bool qb, bool on, bool hd, bool bd, int part); static int prochex(const char *ibuf, int isiz, bool dec); static int procpack(const char *ibuf, int isiz, bool dec, bool bwt); static int proctcbs(const char *ibuf, int isiz, bool dec); static int proczlib(const char *ibuf, int isiz, bool dec, bool gz); static int procbzip(const char *ibuf, int isiz, bool dec); static int procxml(const char *ibuf, int isiz, bool dec, bool br); static int proccstr(const char *ibuf, int isiz, bool dec, bool js); static int procucs(const char *ibuf, int isiz, bool dec, bool un, const char *kw); static int prochash(const char *ibuf, int isiz, bool crc, int ch); static int proccipher(const char *ibuf, int isiz, const char *key); static int procdate(const char *str, int jl, bool wf, bool rf); static int proctmpl(const char *ibuf, int isiz, TCMAP *vars); static int procconf(int mode); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; if(argc < 2) usage(); int rv = 0; if(!strcmp(argv[1], "url")){ rv = runurl(argc, argv); } else if(!strcmp(argv[1], "base")){ rv = runbase(argc, argv); } else if(!strcmp(argv[1], "quote")){ rv = runquote(argc, argv); } else if(!strcmp(argv[1], "mime")){ rv = runmime(argc, argv); } else if(!strcmp(argv[1], "hex")){ rv = runhex(argc, argv); } else if(!strcmp(argv[1], "pack")){ rv = runpack(argc, argv); } else if(!strcmp(argv[1], "tcbs")){ rv = runtcbs(argc, argv); } else if(!strcmp(argv[1], "zlib")){ rv = runzlib(argc, argv); } else if(!strcmp(argv[1], "bzip")){ rv = runbzip(argc, argv); } else if(!strcmp(argv[1], "xml")){ rv = runxml(argc, argv); } else if(!strcmp(argv[1], "cstr")){ rv = runcstr(argc, argv); } else if(!strcmp(argv[1], "ucs")){ rv = runucs(argc, argv); } else if(!strcmp(argv[1], "hash")){ rv = runhash(argc, argv); } else if(!strcmp(argv[1], "cipher")){ rv = runcipher(argc, argv); } else if(!strcmp(argv[1], "date")){ rv = rundate(argc, argv); } else if(!strcmp(argv[1], "tmpl")){ rv = runtmpl(argc, argv); } else if(!strcmp(argv[1], "conf")){ rv = runconf(argc, argv); } else { usage(); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: popular encoders and decoders of Tokyo Cabinet\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s url [-d] [-br] [-rs base] [file]\n", g_progname); fprintf(stderr, " %s base [-d] [file]\n", g_progname); fprintf(stderr, " %s quote [-d] [file]\n", g_progname); fprintf(stderr, " %s mime [-d] [-en name] [-q] [-on] [-hd] [-bd] [-part num] [file]\n", g_progname); fprintf(stderr, " %s hex [-d] [file]\n", g_progname); fprintf(stderr, " %s pack [-d] [-bwt] [file]\n", g_progname); fprintf(stderr, " %s tcbs [-d] [file]\n", g_progname); fprintf(stderr, " %s zlib [-d] [-gz] [file]\n", g_progname); fprintf(stderr, " %s bzip [-d] [file]\n", g_progname); fprintf(stderr, " %s xml [-d] [-br] [file]\n", g_progname); fprintf(stderr, " %s cstr [-d] [-js] [file]\n", g_progname); fprintf(stderr, " %s ucs [-d] [-un] [file]\n", g_progname); fprintf(stderr, " %s hash [-crc] [-ch num] [file]\n", g_progname); fprintf(stderr, " %s cipher [-key str] [file]\n", g_progname); fprintf(stderr, " %s date [-ds str] [-jl num] [-wf] [-rf]\n", g_progname); fprintf(stderr, " %s tmpl [-var name val] [file]\n", g_progname); fprintf(stderr, " %s conf [-v|-i|-l|-p]\n", g_progname); fprintf(stderr, "\n"); exit(1); } /* print formatted error string */ static void eprintf(const char *format, ...){ va_list ap; va_start(ap, format); fprintf(stderr, "%s: ", g_progname); vfprintf(stderr, format, ap); fprintf(stderr, "\n"); va_end(ap); } /* parse arguments of url command */ static int runurl(int argc, char **argv){ char *path = NULL; bool dec = false; bool br = false; char *base = NULL; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-d")){ dec = true; } else if(!strcmp(argv[i], "-br")){ br = true; } else if(!strcmp(argv[i], "-rs")){ if(++i >= argc) usage(); base = argv[i]; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } char *ibuf; int isiz; if(path && path[0] == '@'){ isiz = strlen(path) - 1; ibuf = tcmemdup(path + 1, isiz); } else { ibuf = tcreadfile(path, -1, &isiz); } if(!ibuf){ eprintf("%s: cannot open", path ? path : "(stdin)"); return 1; } int rv = procurl(ibuf, isiz, dec, br, base); if(path && path[0] == '@' && !br) printf("\n"); tcfree(ibuf); return rv; } /* parse arguments of base command */ static int runbase(int argc, char **argv){ char *path = NULL; bool dec = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-d")){ dec = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } char *ibuf; int isiz; if(path && path[0] == '@'){ isiz = strlen(path) - 1; ibuf = tcmemdup(path + 1, isiz); } else { ibuf = tcreadfile(path, -1, &isiz); } if(!ibuf){ eprintf("%s: cannot open", path ? path : "(stdin)"); return 1; } int rv = procbase(ibuf, isiz, dec); if(path && path[0] == '@') printf("\n"); tcfree(ibuf); return rv; } /* parse arguments of quote command */ static int runquote(int argc, char **argv){ char *path = NULL; bool dec = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-d")){ dec = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } char *ibuf; int isiz; if(path && path[0] == '@'){ isiz = strlen(path) - 1; ibuf = tcmemdup(path + 1, isiz); } else { ibuf = tcreadfile(path, -1, &isiz); } if(!ibuf){ eprintf("%s: cannot open", path ? path : "(stdin)"); return 1; } int rv = procquote(ibuf, isiz, dec); if(path && path[0] == '@') printf("\n"); tcfree(ibuf); return rv; } /* parse arguments of mime command */ static int runmime(int argc, char **argv){ char *path = NULL; bool dec = false; char *ename = NULL; bool qb = false; bool on = false; bool hd = false; bool bd = false; int part = -1; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-d")){ dec = true; } else if(!strcmp(argv[i], "-en")){ if(++i >= argc) usage(); ename = argv[i]; } else if(!strcmp(argv[i], "-q")){ qb = true; } else if(!strcmp(argv[i], "-on")){ on = true; } else if(!strcmp(argv[i], "-hd")){ hd = true; } else if(!strcmp(argv[i], "-bd")){ bd = true; } else if(!strcmp(argv[i], "-part")){ if(++i >= argc) usage(); part = tcatoix(argv[i]); } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } char *ibuf; int isiz; if(path && path[0] == '@'){ isiz = strlen(path) - 1; ibuf = tcmemdup(path + 1, isiz); } else { ibuf = tcreadfile(path, -1, &isiz); } if(!ibuf){ eprintf("%s: cannot open", path ? path : "(stdin)"); return 1; } if(!ename) ename = "UTF-8"; int rv = procmime(ibuf, isiz, dec, ename, qb, on, hd, bd, part); if(path && path[0] == '@') printf("\n"); tcfree(ibuf); return rv; } /* parse arguments of hex command */ static int runhex(int argc, char **argv){ char *path = NULL; bool dec = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-d")){ dec = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } char *ibuf; int isiz; if(path && path[0] == '@'){ isiz = strlen(path) - 1; ibuf = tcmemdup(path + 1, isiz); } else { ibuf = tcreadfile(path, -1, &isiz); } if(!ibuf){ eprintf("%s: cannot open", path ? path : "(stdin)"); return 1; } int rv = prochex(ibuf, isiz, dec); if(path && path[0] == '@') printf("\n"); tcfree(ibuf); return rv; } /* parse arguments of pack command */ static int runpack(int argc, char **argv){ char *path = NULL; bool dec = false; bool bwt = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-d")){ dec = true; } else if(!strcmp(argv[i], "-bwt")){ bwt = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } char *ibuf; int isiz; if(path && path[0] == '@'){ isiz = strlen(path) - 1; ibuf = tcmemdup(path + 1, isiz); } else { ibuf = tcreadfile(path, -1, &isiz); } if(!ibuf){ eprintf("%s: cannot open", path ? path : "(stdin)"); return 1; } int rv = procpack(ibuf, isiz, dec, bwt); if(path && path[0] == '@') printf("\n"); tcfree(ibuf); return rv; } /* parse arguments of tcbs command */ static int runtcbs(int argc, char **argv){ char *path = NULL; bool dec = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-d")){ dec = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } char *ibuf; int isiz; if(path && path[0] == '@'){ isiz = strlen(path) - 1; ibuf = tcmemdup(path + 1, isiz); } else { ibuf = tcreadfile(path, -1, &isiz); } if(!ibuf){ eprintf("%s: cannot open", path ? path : "(stdin)"); return 1; } int rv = proctcbs(ibuf, isiz, dec); if(path && path[0] == '@') printf("\n"); tcfree(ibuf); return rv; } /* parse arguments of zlib command */ static int runzlib(int argc, char **argv){ char *path = NULL; bool dec = false; bool gz = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-d")){ dec = true; } else if(!strcmp(argv[i], "-gz")){ gz = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } char *ibuf; int isiz; if(path && path[0] == '@'){ isiz = strlen(path) - 1; ibuf = tcmemdup(path + 1, isiz); } else { ibuf = tcreadfile(path, -1, &isiz); } if(!ibuf){ eprintf("%s: cannot open", path ? path : "(stdin)"); return 1; } int rv = proczlib(ibuf, isiz, dec, gz); if(path && path[0] == '@') printf("\n"); tcfree(ibuf); return rv; } /* parse arguments of bzip command */ static int runbzip(int argc, char **argv){ char *path = NULL; bool dec = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-d")){ dec = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } char *ibuf; int isiz; if(path && path[0] == '@'){ isiz = strlen(path) - 1; ibuf = tcmemdup(path + 1, isiz); } else { ibuf = tcreadfile(path, -1, &isiz); } if(!ibuf){ eprintf("%s: cannot open", path ? path : "(stdin)"); return 1; } int rv = procbzip(ibuf, isiz, dec); if(path && path[0] == '@') printf("\n"); tcfree(ibuf); return rv; } /* parse arguments of xml command */ static int runxml(int argc, char **argv){ char *path = NULL; bool dec = false; bool br = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-d")){ dec = true; } else if(!strcmp(argv[i], "-br")){ br = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } char *ibuf; int isiz; if(path && path[0] == '@'){ isiz = strlen(path) - 1; ibuf = tcmemdup(path + 1, isiz); } else { ibuf = tcreadfile(path, -1, &isiz); } if(!ibuf){ eprintf("%s: cannot open", path ? path : "(stdin)"); return 1; } int rv = procxml(ibuf, isiz, dec, br); if(path && path[0] == '@') printf("\n"); tcfree(ibuf); return rv; } /* parse arguments of cstr command */ static int runcstr(int argc, char **argv){ char *path = NULL; bool dec = false; bool js = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-d")){ dec = true; } else if(!strcmp(argv[i], "-js")){ js = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } char *ibuf; int isiz; if(path && path[0] == '@'){ isiz = strlen(path) - 1; ibuf = tcmemdup(path + 1, isiz); } else { ibuf = tcreadfile(path, -1, &isiz); } if(!ibuf){ eprintf("%s: cannot open", path ? path : "(stdin)"); return 1; } int rv = proccstr(ibuf, isiz, dec, js); if(path && path[0] == '@') printf("\n"); tcfree(ibuf); return rv; } /* parse arguments of ucs command */ static int runucs(int argc, char **argv){ char *path = NULL; bool dec = false; bool un = false; char *kw = NULL; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-d")){ dec = true; } else if(!strcmp(argv[i], "-un")){ un = true; } else if(!strcmp(argv[i], "-kw")){ if(++i >= argc) usage(); kw = argv[i]; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } char *ibuf; int isiz; if(path && path[0] == '@'){ isiz = strlen(path) - 1; ibuf = tcmemdup(path + 1, isiz); } else { ibuf = tcreadfile(path, -1, &isiz); } if(!ibuf){ eprintf("%s: cannot open", path ? path : "(stdin)"); return 1; } int rv = procucs(ibuf, isiz, dec, un, kw); if(path && path[0] == '@') printf("\n"); tcfree(ibuf); return rv; } /* parse arguments of hash command */ static int runhash(int argc, char **argv){ char *path = NULL; bool crc = false; int ch = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-crc")){ crc = true; } else if(!strcmp(argv[i], "-ch")){ if(++i >= argc) usage(); ch = tcatoix(argv[i]); } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } char *ibuf; int isiz; if(path && path[0] == '@'){ isiz = strlen(path) - 1; ibuf = tcmemdup(path + 1, isiz); } else { ibuf = tcreadfile(path, -1, &isiz); } if(!ibuf){ eprintf("%s: cannot open", path ? path : "(stdin)"); return 1; } int rv = prochash(ibuf, isiz, crc, ch); tcfree(ibuf); return rv; } /* parse arguments of cipher command */ static int runcipher(int argc, char **argv){ char *path = NULL; char *key = NULL; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-key")){ if(++i >= argc) usage(); key = argv[i]; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } char *ibuf; int isiz; if(path && path[0] == '@'){ isiz = strlen(path) - 1; ibuf = tcmemdup(path + 1, isiz); } else { ibuf = tcreadfile(path, -1, &isiz); } if(!ibuf){ eprintf("%s: cannot open", path ? path : "(stdin)"); return 1; } int rv = proccipher(ibuf, isiz, key); if(path && path[0] == '@') printf("\n"); tcfree(ibuf); return rv; } /* parse arguments of date command */ static int rundate(int argc, char **argv){ char *str = NULL; int jl = INT_MAX; bool wf = false; bool rf = false; for(int i = 2; i < argc; i++){ if(argv[i][0] == '-'){ if(!strcmp(argv[i], "-ds")){ if(++i >= argc) usage(); str = argv[i]; } else if(!strcmp(argv[i], "-jl")){ if(++i >= argc) usage(); jl = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-wf")){ wf = true; } else if(!strcmp(argv[i], "-rf")){ rf = true; } else { usage(); } } else { usage(); } } int rv = procdate(str, jl, wf, rf); return rv; } /* parse arguments of tmpl command */ static int runtmpl(int argc, char **argv){ char *path = NULL; TCMAP *vars = tcmpoolmapnew(tcmpoolglobal()); for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-var")){ if(++i >= argc) usage(); const char *name = argv[i]; if(++i >= argc) usage(); const char *value = argv[i]; tcmapput2(vars, name, value); } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } char *ibuf; int isiz; if(path && path[0] == '@'){ isiz = strlen(path) - 1; ibuf = tcmemdup(path + 1, isiz); } else { ibuf = tcreadfile(path, -1, &isiz); } if(!ibuf){ eprintf("%s: cannot open", path ? path : "(stdin)"); return 1; } int rv = proctmpl(ibuf, isiz, vars); if(path && path[0] == '@') printf("\n"); tcfree(ibuf); return rv; } /* parse arguments of conf command */ static int runconf(int argc, char **argv){ int mode = 0; for(int i = 2; i < argc; i++){ if(argv[i][0] == '-'){ if(!strcmp(argv[i], "-v")){ mode = 'v'; } else if(!strcmp(argv[i], "-i")){ mode = 'i'; } else if(!strcmp(argv[i], "-l")){ mode = 'l'; } else if(!strcmp(argv[i], "-p")){ mode = 'p'; } else { usage(); } } else { usage(); } } int rv = procconf(mode); return rv; } /* perform url command */ static int procurl(const char *ibuf, int isiz, bool dec, bool br, const char *base){ if(base){ char *obuf = tcurlresolve(base, ibuf); printf("%s", obuf); tcfree(obuf); } else if(br){ TCMAP *elems = tcurlbreak(ibuf); const char *elem; if((elem = tcmapget2(elems, "self")) != NULL) printf("self: %s\n", elem); if((elem = tcmapget2(elems, "scheme")) != NULL) printf("scheme: %s\n", elem); if((elem = tcmapget2(elems, "host")) != NULL) printf("host: %s\n", elem); if((elem = tcmapget2(elems, "port")) != NULL) printf("port: %s\n", elem); if((elem = tcmapget2(elems, "authority")) != NULL) printf("authority: %s\n", elem); if((elem = tcmapget2(elems, "path")) != NULL) printf("path: %s\n", elem); if((elem = tcmapget2(elems, "file")) != NULL) printf("file: %s\n", elem); if((elem = tcmapget2(elems, "query")) != NULL) printf("query: %s\n", elem); if((elem = tcmapget2(elems, "fragment")) != NULL) printf("fragment: %s\n", elem); tcmapdel(elems); } else if(dec){ int osiz; char *obuf = tcurldecode(ibuf, &osiz); fwrite(obuf, 1, osiz, stdout); tcfree(obuf); } else { char *obuf = tcurlencode(ibuf, isiz); fwrite(obuf, 1, strlen(obuf), stdout); tcfree(obuf); } return 0; } /* perform base command */ static int procbase(const char *ibuf, int isiz, bool dec){ if(dec){ int osiz; char *obuf = tcbasedecode(ibuf, &osiz); fwrite(obuf, 1, osiz, stdout); tcfree(obuf); } else { char *obuf = tcbaseencode(ibuf, isiz); fwrite(obuf, 1, strlen(obuf), stdout); tcfree(obuf); } return 0; } /* perform quote command */ static int procquote(const char *ibuf, int isiz, bool dec){ if(dec){ int osiz; char *obuf = tcquotedecode(ibuf, &osiz); fwrite(obuf, 1, osiz, stdout); tcfree(obuf); } else { char *obuf = tcquoteencode(ibuf, isiz); fwrite(obuf, 1, strlen(obuf), stdout); tcfree(obuf); } return 0; } /* perform mime command */ static int procmime(const char *ibuf, int isiz, bool dec, const char *ename, bool qb, bool on, bool hd, bool bd, int part){ if(hd || bd || part > 0){ TCMAP *hmap = tcmapnew2(16); int osiz; char *obuf = tcmimebreak(ibuf, isiz, hmap, &osiz); if(part > 0){ const char *value; if(!(value = tcmapget2(hmap, "TYPE")) || !tcstrifwm(value, "multipart/") || !(value = tcmapget2(hmap, "BOUNDARY"))){ eprintf("not multipart"); } else { TCLIST *parts = tcmimeparts(obuf, osiz, value); if(part <= tclistnum(parts)){ int bsiz; const char *body = tclistval(parts, part - 1, &bsiz); fwrite(body, 1, bsiz, stdout); } else { eprintf("no such part"); } tclistdel(parts); } } else { if(hd){ TCLIST *names = tcmapkeys(hmap); tclistsort(names); int num = tclistnum(names); for(int i = 0; i < num; i++){ const char *name = tclistval2(names, i); printf("%s\t%s\n", name, tcmapget2(hmap, name)); } tclistdel(names); if(bd) putchar('\n'); } if(bd) fwrite(obuf, 1, osiz, stdout); } tcfree(obuf); tcmapdel(hmap); } else if(dec){ char ebuf[32]; char *obuf = tcmimedecode(ibuf, ebuf); if(on){ fwrite(ebuf, 1, strlen(ebuf), stdout); } else { fwrite(obuf, 1, strlen(obuf), stdout); } tcfree(obuf); } else { char *obuf = tcmimeencode(ibuf, ename, !qb); fwrite(obuf, 1, strlen(obuf), stdout); tcfree(obuf); } return 0; } /* perform hex command */ static int prochex(const char *ibuf, int isiz, bool dec){ if(dec){ int osiz; char *obuf = tchexdecode(ibuf, &osiz); fwrite(obuf, 1, osiz, stdout); tcfree(obuf); } else { char *obuf = tchexencode(ibuf, isiz); fwrite(obuf, 1, strlen(obuf), stdout); tcfree(obuf); } return 0; } /* perform pack command */ static int procpack(const char *ibuf, int isiz, bool dec, bool bwt){ if(dec){ int osiz; char *obuf = tcpackdecode(ibuf, isiz, &osiz); if(bwt && osiz > 0){ int idx, step; TCREADVNUMBUF(obuf, idx, step); char *tbuf = tcbwtdecode(obuf + step, osiz - step, idx); fwrite(tbuf, 1, osiz - step, stdout); tcfree(tbuf); } else { fwrite(obuf, 1, osiz, stdout); } tcfree(obuf); } else { char *tbuf = NULL; if(bwt){ int idx; tbuf = tcbwtencode(ibuf, isiz, &idx); char vnumbuf[sizeof(int)+1]; int step; TCSETVNUMBUF(step, vnumbuf, idx); tbuf = tcrealloc(tbuf, isiz + step + 1); memmove(tbuf + step, tbuf, isiz); memcpy(tbuf, vnumbuf, step); isiz += step; ibuf = tbuf; } int osiz; char *obuf = tcpackencode(ibuf, isiz, &osiz); fwrite(obuf, 1, osiz, stdout); tcfree(obuf); tcfree(tbuf); } return 0; } /* perform tcbs command */ static int proctcbs(const char *ibuf, int isiz, bool dec){ if(dec){ int osiz; char *obuf = tcbsdecode(ibuf, isiz, &osiz); fwrite(obuf, 1, osiz, stdout); tcfree(obuf); } else { int osiz; char *obuf = tcbsencode(ibuf, isiz, &osiz); fwrite(obuf, 1, osiz, stdout); tcfree(obuf); } return 0; } /* perform zlib command */ static int proczlib(const char *ibuf, int isiz, bool dec, bool gz){ if(dec){ int osiz; char *obuf = gz ? tcgzipdecode(ibuf, isiz, &osiz) : tcinflate(ibuf, isiz, &osiz); if(obuf){ fwrite(obuf, 1, osiz, stdout); tcfree(obuf); } else { eprintf("inflate failure"); } } else { int osiz; char *obuf = gz ? tcgzipencode(ibuf, isiz, &osiz) : tcdeflate(ibuf, isiz, &osiz); if(obuf){ fwrite(obuf, 1, osiz, stdout); tcfree(obuf); } else { eprintf("deflate failure"); } } return 0; } /* perform bzip command */ static int procbzip(const char *ibuf, int isiz, bool dec){ if(dec){ int osiz; char *obuf = tcbzipdecode(ibuf, isiz, &osiz); if(obuf){ fwrite(obuf, 1, osiz, stdout); tcfree(obuf); } else { eprintf("inflate failure"); } } else { int osiz; char *obuf = tcbzipencode(ibuf, isiz, &osiz); if(obuf){ fwrite(obuf, 1, osiz, stdout); tcfree(obuf); } else { eprintf("deflate failure"); } } return 0; } /* perform xml command */ static int procxml(const char *ibuf, int isiz, bool dec, bool br){ if(br){ TCLIST *elems = tcxmlbreak(ibuf); for(int i = 0; i < tclistnum(elems); i++){ int esiz; const char *elem = tclistval(elems, i, &esiz); char *estr = tcmemdup(elem, esiz); tcstrsubchr(estr, "\t\n\r", " "); tcstrtrim(estr); if(*estr != '\0'){ if(*elem == '<'){ if(tcstrfwm(estr, ""); for(int j = 0; j < tclistnum(list); j++){ const char *elem = tclistval2(list, j); TCMAP *attrs = tcxmlattrs(elem); tcmapdel(attrs); } tclistdel(list); } if(i % 100 == 1){ TCTMPL *tmpl = tctmplnew(); const char *str = "{{ title XML }}{{UNKNOWN COMMAND}}{{CONF author 'Mikio Hirabayashi'}}\n" "{{ FOREACH docs \\}}\n" "{{ IF void }}===={{void}}{{ ELSE }}----{{void.void}}{{ END }}\n" "{{ IF .id }}ID:{{ .id }}{{ END }}\n" "{{ IF .title }}Title:{{ .title }}{{ END }}\n" "{{ IF author }}Author:{{ author }}{{ END }}\n" "{{ IF .coms }}{{ SET addr 'Setagaya, Tokyo' }}--\n" "{{ FOREACH .coms com }}{{ com.author MD5 }}: {{ com.body }}\n" "{{ END \\}}\n" "{{ END \\}}\n" "{{ END \\}}\n"; tctmplsetsep(tmpl, "{{", "}}"); tctmplload(tmpl, str); const char *cval = tctmplconf(tmpl, "author"); if(!cval || strcmp(cval, "Mikio Hirabayashi")) err = true; TCMPOOL *mpool = tcmpoolnew(); TCMAP *vars = tcmpoolmapnew(mpool); tcmapput2(vars, "title", "I LOVE YOU"); TCLIST *docs = tcmpoollistnew(mpool); for(int j = 0; j < 3; j++){ TCMAP *doc = tcmpoolmapnew(mpool); char vbuf[TCNUMBUFSIZ]; sprintf(vbuf, "%d", i + j); tcmapput2(doc, "id", vbuf); sprintf(vbuf, "[%08d]", i + j); tcmapput2(doc, "title", vbuf); TCLIST *coms = tcmpoollistnew(mpool); for(int k = 0; k < 3; k++){ TCMAP *com = tcmpoolmapnew(mpool); sprintf(vbuf, "u%d", k); tcmapput2(com, "author", vbuf); sprintf(vbuf, "this is the %dth pen.", (j + 1) * (k + 1) + i); tcmapput2(com, "body", vbuf); tclistpushmap(coms, com); } tcmapputlist(doc, "coms", coms); tclistpushmap(docs, doc); } tcmapputlist(vars, "docs", docs); char *res = tctmpldump(tmpl, vars); tcfree(res); tcmpoolclear(mpool, true); tcmpoolmalloc(mpool, 1); tcmpoollistnew(mpool); tcmpooldel(mpool); tctmpldel(tmpl); } if(i % 10 == 1){ for(int16_t j = 1; j <= 0x2000; j *= 2){ for(int16_t num = j - 1; num <= j + 1; num++){ int16_t nnum = TCHTOIS(num); if(num != TCITOHS(nnum)) err = true; } } for(int32_t j = 1; j <= 0x20000000; j *= 2){ for(int32_t num = j - 1; num <= j + 1; num++){ int32_t nnum = TCHTOIL(num); if(num != TCITOHL(nnum)) err = true; char buf[TCNUMBUFSIZ]; int step, nstep; TCSETVNUMBUF(step, buf, num); TCREADVNUMBUF(buf, nnum, nstep); if(num != nnum || step != nstep) err = true; } } for(int64_t j = 1; j <= 0x2000000000000000; j *= 2){ for(int64_t num = j - 1; num <= j + 1; num++){ int64_t nnum = TCHTOILL(num); if(num != TCITOHLL(nnum)) err = true; char buf[TCNUMBUFSIZ]; int step, nstep; TCSETVNUMBUF64(step, buf, num); TCREADVNUMBUF64(buf, nnum, nstep); if(num != nnum || step != nstep) err = true; } } TCBITMAP *bitmap = TCBITMAPNEW(100); for(int j = 0; j < 100; j++){ if(j % 3 == 0) TCBITMAPON(bitmap, j); if(j % 5 == 0) TCBITMAPOFF(bitmap, j); } for(int j = 0; j < 100; j++){ if(j % 5 == 0){ if(TCBITMAPCHECK(bitmap, j)) err = true; } else if(j % 3 == 0){ if(!TCBITMAPCHECK(bitmap, j)) err = true; } } TCBITMAPDEL(bitmap); buf = tcmalloc(i / 8 + 2); TCBITSTRM strm; TCBITSTRMINITW(strm, buf); for(int j = 0; j < i; j++){ int sign = j % 3 == 0 || j % 7 == 0; TCBITSTRMCAT(strm, sign); } TCBITSTRMSETEND(strm); int bnum = TCBITSTRMNUM(strm); if(bnum != i) err = true; TCBITSTRMINITR(strm, buf, bsiz); for(int j = 0; j < i; j++){ int sign; TCBITSTRMREAD(strm, sign); if(sign != (j % 3 == 0 || j % 7 == 0)) err = true; } tcfree(buf); } if(i % 100 == 1){ char path[RECBUFSIZ]; sprintf(path, "%d", myrand(10)); if(tcpathlock(path)){ if(!tcpathunlock(path)) err = true; } else { err = true; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("time: %.3f\n", tctime() - stime); if(err){ iprintf("error\n\n"); return 1; } iprintf("ok\n\n"); return 0; } /* perform wicked command */ static int procwicked(int rnum){ iprintf("\n seed=%u rnum=%d\n\n", g_randseed, rnum); double stime = tctime(); TCMPOOL *mpool = tcmpoolglobal(); TCXSTR *xstr = myrand(2) > 0 ? tcxstrnew() : tcxstrnew2("hello world"); tcmpoolpushxstr(mpool, xstr); TCLIST *list = myrand(2) > 0 ? tclistnew() : tclistnew2(myrand(rnum) + rnum / 2); tcmpoolpushlist(mpool, list); TCPTRLIST *ptrlist = myrand(2) > 0 ? tcptrlistnew() : tcptrlistnew2(myrand(rnum) + rnum / 2); tcmpoolpush(mpool, ptrlist, (void (*)(void*))tcptrlistdel); TCMAP *map = myrand(2) > 0 ? tcmapnew() : tcmapnew2(myrand(rnum) + rnum / 2); tcmpoolpushmap(mpool, map); TCTREE *tree = myrand(2) > 0 ? tctreenew() : tctreenew2(tccmpdecimal, NULL); tcmpoolpushtree(mpool, tree); TCMDB *mdb = myrand(2) > 0 ? tcmdbnew() : tcmdbnew2(myrand(rnum) + rnum / 2); tcmpoolpush(mpool, mdb, (void (*)(void*))tcmdbdel); TCNDB *ndb = myrand(2) > 0 ? tcndbnew() : tcndbnew2(tccmpdecimal, NULL); tcmpoolpush(mpool, ndb, (void (*)(void*))tcndbdel); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(i)); char vbuf[RECBUFSIZ]; int vsiz = sprintf(vbuf, "%d", myrand(i)); char *tmp; switch(myrand(70)){ case 0: iputchar('0'); tcxstrcat(xstr, kbuf, ksiz); break; case 1: iputchar('1'); tcxstrcat2(xstr, kbuf); break; case 2: iputchar('2'); if(myrand(rnum / 100 + 1) == 0) tcxstrclear(xstr); break; case 3: iputchar('3'); tcxstrprintf(xstr, "[%s:%d:%llu:%b:%llb]\n", kbuf, i, (long long)i * 65521, i, (unsigned long long)i * 65521); break; case 4: iputchar('4'); tclistpush(list, kbuf, ksiz); tcptrlistpush(ptrlist, tcmemdup(kbuf, ksiz)); break; case 5: iputchar('5'); tclistpush2(list, kbuf); break; case 6: iputchar('6'); tmp = tcmemdup(kbuf, ksiz); tclistpushmalloc(list, tmp, strlen(tmp)); break; case 7: iputchar('7'); if(myrand(10) == 0){ tcfree(tclistpop(list, &ksiz)); tcfree(tcptrlistpop(ptrlist)); } break; case 8: iputchar('8'); if(myrand(10) == 0) tcfree(tclistpop2(list)); break; case 9: iputchar('9'); tclistunshift(list, kbuf, ksiz); tcptrlistunshift(ptrlist, tcmemdup(kbuf, ksiz)); break; case 10: iputchar('A'); tclistunshift2(list, kbuf); break; case 11: iputchar('B'); if(myrand(10) == 0){ tcfree(tclistshift(list, &ksiz)); tcfree(tcptrlistshift(ptrlist)); } break; case 12: iputchar('C'); if(myrand(10) == 0) tcfree(tclistshift2(list)); break; case 13: iputchar('D'); tclistinsert(list, i / 10, kbuf, ksiz); if(tcptrlistnum(ptrlist) > i / 10) tcptrlistinsert(ptrlist, i / 10, tcmemdup(kbuf, ksiz)); break; case 14: iputchar('E'); tclistinsert2(list, i / 10, kbuf); break; case 15: iputchar('F'); if(myrand(10) == 0){ tcfree(tclistremove(list, i / 10, &ksiz)); tcfree(tcptrlistremove(ptrlist, i / 10)); } break; case 16: iputchar('G'); if(myrand(10) == 0) tcfree(tclistremove2(list, i / 10)); break; case 17: iputchar('H'); tclistover(list, i / 10, kbuf, ksiz); if(tcptrlistnum(ptrlist) > i / 10){ tcfree(tcptrlistval(ptrlist, i / 10)); tcptrlistover(ptrlist, i / 10, tcmemdup(kbuf, ksiz)); } break; case 18: iputchar('I'); tclistover2(list, i / 10, kbuf); break; case 19: iputchar('J'); if(myrand(rnum / 1000 + 1) == 0) tclistsort(list); break; case 20: iputchar('K'); if(myrand(rnum / 1000 + 1) == 0) tclistsortci(list); break; case 21: iputchar('L'); if(myrand(rnum / 1000 + 1) == 0) tclistlsearch(list, kbuf, ksiz); break; case 22: iputchar('M'); if(myrand(rnum / 1000 + 1) == 0) tclistbsearch(list, kbuf, ksiz); break; case 23: iputchar('N'); if(myrand(rnum / 100 + 1) == 0){ tclistclear(list); for(int j = 0; j < tcptrlistnum(ptrlist); j++){ tcfree(tcptrlistval(ptrlist, j)); } tcptrlistclear(ptrlist); } break; case 24: iputchar('O'); if(myrand(rnum / 100 + 1) == 0){ int dsiz; char *dbuf = tclistdump(list, &dsiz); tclistdel(tclistload(dbuf, dsiz)); tcfree(dbuf); } break; case 25: iputchar('P'); if(myrand(100) == 0){ if(myrand(2) == 0){ for(int j = 0; j < tclistnum(list); j++){ int rsiz; tclistval(list, j, &rsiz); tcptrlistval(ptrlist, j); } } else { for(int j = 0; j < tclistnum(list); j++){ tclistval2(list, j); } } } break; case 26: iputchar('Q'); tcmapput(map, kbuf, ksiz, vbuf, vsiz); tctreeput(tree, kbuf, ksiz, vbuf, vsiz); break; case 27: iputchar('R'); tcmapput2(map, kbuf, vbuf); tctreeput2(tree, kbuf, vbuf); break; case 28: iputchar('S'); tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); tctreeputkeep(tree, kbuf, ksiz, vbuf, vsiz); break; case 29: iputchar('T'); tcmapputkeep2(map, kbuf, vbuf); tctreeputkeep2(tree, kbuf, vbuf); break; case 30: iputchar('U'); tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); tctreeputcat(tree, kbuf, ksiz, vbuf, vsiz); break; case 31: iputchar('V'); tcmapputcat2(map, kbuf, vbuf); tctreeputcat2(tree, kbuf, vbuf); break; case 32: iputchar('W'); if(myrand(2) == 0){ tcmapput3(map, kbuf, ksiz, vbuf, vsiz); tctreeput3(tree, kbuf, ksiz, vbuf, vsiz); } if(myrand(2) == 0){ tcmapput4(map, kbuf, ksiz, vbuf, vsiz, vbuf, vsiz); tctreeputkeep3(tree, kbuf, ksiz, vbuf, vsiz); } if(myrand(2) == 0){ tcmapputcat3(map, kbuf, ksiz, vbuf, vsiz); tctreeputcat3(tree, kbuf, ksiz, vbuf, vsiz); } break; case 33: iputchar('X'); if(myrand(10) == 0){ tcmapout(map, kbuf, ksiz); tctreeout(tree, kbuf, ksiz); } break; case 34: iputchar('Y'); if(myrand(10) == 0){ tcmapout2(map, kbuf); tctreeout2(tree, kbuf); } break; case 35: iputchar('Z'); tcmapget3(map, kbuf, ksiz, &vsiz); tctreeget3(tree, kbuf, ksiz, &vsiz); break; case 36: iputchar('a'); tcmapmove(map, kbuf, ksiz, true); break; case 37: iputchar('b'); tcmapmove(map, kbuf, ksiz, false); break; case 38: iputchar('c'); tcmapmove2(map, kbuf, true); break; case 39: iputchar('d'); if(myrand(100) == 0){ if(myrand(2) == 0){ tcmapiterinit(map); tctreeiterinit(tree); } else { tcmapiterinit2(map, kbuf, ksiz); tctreeiterinit2(tree, kbuf, ksiz); } } break; case 40: iputchar('e'); tcmapiternext(map, &vsiz); tctreeiternext(tree, &vsiz); break; case 41: iputchar('f'); tcmapiternext2(map); tctreeiternext2(tree); break; case 42: iputchar('g'); if(myrand(100) == 0){ int anum; switch(myrand(4)){ case 0: tclistdel(tcmapkeys(map)); tclistdel(tctreekeys(tree)); break; case 1: tcfree(tcmapkeys2(map, &anum)); tcfree(tctreekeys2(tree, &anum)); break; case 2: tclistdel(tcmapvals(map)); tclistdel(tctreevals(tree)); break; default: tcfree(tcmapvals2(map, &anum)); tcfree(tctreevals2(tree, &anum)); break; } } break; case 43: iputchar('h'); if(myrand(rnum / 100 + 1) == 0){ tcmapclear(map); tctreeclear(tree); } break; case 44: iputchar('i'); if(myrand(20) == 0){ tcmapcutfront(map, myrand(10)); tctreecutfringe(tree, myrand(10)); } break; case 45: iputchar('j'); if(myrand(rnum / 100 + 1) == 0){ int dsiz; char *dbuf = tcmapdump(map, &dsiz); tcfree(tcmaploadone(dbuf, dsiz, kbuf, ksiz, &vsiz)); tcmapdel(tcmapload(dbuf, dsiz)); tcfree(dbuf); dbuf = tctreedump(tree, &dsiz); tcfree(tctreeloadone(dbuf, dsiz, kbuf, ksiz, &vsiz)); tctreedel(tctreeload(dbuf, dsiz, tccmplexical, NULL)); tcfree(dbuf); } break; case 46: iputchar('k'); tcmdbput(mdb, kbuf, ksiz, vbuf, vsiz); tcndbput(ndb, kbuf, ksiz, vbuf, vsiz); break; case 47: iputchar('l'); tcmdbput2(mdb, kbuf, vbuf); tcndbput2(ndb, kbuf, vbuf); break; case 48: iputchar('m'); tcmdbputkeep(mdb, kbuf, ksiz, vbuf, vsiz); tcndbputkeep(ndb, kbuf, ksiz, vbuf, vsiz); break; case 49: iputchar('n'); tcmdbputkeep2(mdb, kbuf, vbuf); tcndbputkeep2(ndb, kbuf, vbuf); break; case 50: iputchar('o'); tcmdbputcat(mdb, kbuf, ksiz, vbuf, vsiz); tcndbputcat(ndb, kbuf, ksiz, vbuf, vsiz); break; case 51: iputchar('p'); tcmdbputcat2(mdb, kbuf, vbuf); tcndbputcat2(ndb, kbuf, vbuf); break; case 52: iputchar('q'); if(myrand(2) == 0){ tcmdbput3(mdb, kbuf, ksiz, vbuf, vsiz); tcndbput3(ndb, kbuf, ksiz, vbuf, vsiz); } if(myrand(2) == 0){ tcmdbput4(mdb, kbuf, ksiz, vbuf, vsiz, vbuf, vsiz); tcndbputkeep3(ndb, kbuf, ksiz, vbuf, vsiz); } if(myrand(2) == 0){ tcmdbputcat3(mdb, kbuf, ksiz, vbuf, vsiz); tcndbputcat3(ndb, kbuf, ksiz, vbuf, vsiz); } break; case 53: iputchar('r'); if(myrand(10) == 0){ tcmdbout(mdb, kbuf, ksiz); tcndbout(ndb, kbuf, ksiz); } break; case 54: iputchar('s'); if(myrand(10) == 0){ tcmdbout2(mdb, kbuf); tcndbout2(ndb, kbuf); } break; case 55: iputchar('t'); tcfree(tcmdbget(mdb, kbuf, ksiz, &vsiz)); tcfree(tcndbget(ndb, kbuf, ksiz, &vsiz)); break; case 56: iputchar('u'); tcfree(tcmdbget3(mdb, kbuf, ksiz, &vsiz)); tcfree(tcndbget3(ndb, kbuf, ksiz, &vsiz)); break; case 57: iputchar('v'); if(myrand(100) == 0){ if(myrand(2) == 0){ tcmdbiterinit(mdb); tcndbiterinit(ndb); } else { tcmdbiterinit2(mdb, kbuf, ksiz); tcndbiterinit2(ndb, kbuf, ksiz); } } break; case 58: iputchar('w'); tcfree(tcmdbiternext(mdb, &vsiz)); tcfree(tcndbiternext(ndb, &vsiz)); break; case 59: iputchar('x'); tcfree(tcmdbiternext2(mdb)); tcfree(tcndbiternext2(ndb)); break; case 60: iputchar('y'); if(myrand(rnum / 100 + 1) == 0){ tcmdbvanish(mdb); tcndbvanish(ndb); } break; case 61: iputchar('z'); if(myrand(200) == 0){ tcmdbcutfront(mdb, myrand(100)); tcndbcutfringe(ndb, myrand(100)); } break; case 62: iputchar('+'); if(myrand(200) == 0){ tcmdbforeach(mdb, iterfunc, NULL); tcndbforeach(ndb, iterfunc, NULL); } break; case 63: iputchar('+'); if(myrand(100) == 0){ char *tptr = tcmpoolmalloc(mpool, 1); switch(myrand(5)){ case 0: tcfree(tptr); tcmpoolpop(mpool, false); break; case 1: tcmpoolpop(mpool, true); break; } } break; case 64: iputchar('+'); if(myrand(100) == 0){ TCXSTR *txstr = tcmpoolxstrnew(mpool); switch(myrand(5)){ case 0: tcxstrdel(txstr); tcmpoolpop(mpool, false); break; case 1: tcmpoolpop(mpool, true); break; } } break; case 65: iputchar('+'); if(myrand(100) == 0){ TCLIST *tlist = tcmpoollistnew(mpool); switch(myrand(5)){ case 0: tclistdel(tlist); tcmpoolpop(mpool, false); break; case 1: tcmpoolpop(mpool, true); break; } } break; case 66: iputchar('+'); if(myrand(100) == 0){ TCMAP *tmap = tcmpoolmapnew(mpool); switch(myrand(5)){ case 0: tcmapdel(tmap); tcmpoolpop(mpool, false); break; case 1: tcmpoolpop(mpool, true); break; } } break; case 67: iputchar('+'); if(myrand(100) == 0){ TCTREE *ttree = tcmpooltreenew(mpool); switch(myrand(5)){ case 0: tctreedel(ttree); tcmpoolpop(mpool, false); break; case 1: tcmpoolpop(mpool, true); break; } } break; default: iputchar('@'); if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX); break; } if(i % 50 == 0) iprintf(" (%08d)\n", i); } if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum); for(int i = 0; i < tcptrlistnum(ptrlist); i++){ tcfree(tcptrlistval(ptrlist, i)); } iprintf("time: %.3f\n", tctime() - stime); iprintf("ok\n\n"); return 0; } // END OF FILE tokyocabinet-1.4.48/tcfdb.c0000644000175000017500000024163412013574446014553 0ustar mikiomikio/************************************************************************************************* * The fixed-length database API of Tokyo Cabinet * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include "tcutil.h" #include "tcfdb.h" #include "myconf.h" #define FDBFILEMODE 00644 // permission of created files #define FDBIOBUFSIZ 8192 // size of an I/O buffer #define FDBMAGICDATA "ToKyO CaBiNeT" // magic data for identification #define FDBHEADSIZ 256 // size of the reagion of the header #define FDBTYPEOFF 32 // offset of the region for the database type #define FDBFLAGSOFF 33 // offset of the region for the additional flags #define FDBRNUMOFF 48 // offset of the region for the record number #define FDBFSIZOFF 56 // offset of the region for the file size #define FDBWIDTHOFF 64 // offset of the region for the record width #define FDBLIMSIZOFF 72 // offset of the region for the limit size #define FDBMINOFF 80 // offset of the region for the minimum ID offset #define FDBMAXOFF 88 // offset of the region for the maximum ID offset #define FDBOPAQUEOFF 128 // offset of the region for the opaque field #define FDBDEFWIDTH 255 // default value width #define FDBDEFLIMSIZ (256LL<<20) // default limit size #define FDBRMTXNUM 127 // number of record mutexes #define FDBTRUNCALW 256 // number of record for truncate allowance #define FDBIDARYUNIT 2048 // size of ID array allocation unit #define FDBWALSUFFIX "wal" // suffix of write ahead logging file enum { // enumeration for duplication behavior FDBPDOVER, // overwrite an existing value FDBPDKEEP, // keep the existing value FDBPDCAT, // concatenate values FDBPDADDINT, // add an integer FDBPDADDDBL, // add a real number FDBPDPROC // process by a callback function }; typedef struct { // type of structure for a duplication callback TCPDPROC proc; // function pointer void *op; // opaque pointer } FDBPDPROCOP; /* private macros */ #define FDBLOCKMETHOD(TC_fdb, TC_wr) \ ((TC_fdb)->mmtx ? tcfdblockmethod((TC_fdb), (TC_wr)) : true) #define FDBUNLOCKMETHOD(TC_fdb) \ ((TC_fdb)->mmtx ? tcfdbunlockmethod(TC_fdb) : true) #define FDBLOCKATTR(TC_fdb) \ ((TC_fdb)->mmtx ? tcfdblockattr(TC_fdb) : true) #define FDBUNLOCKATTR(TC_fdb) \ ((TC_fdb)->mmtx ? tcfdbunlockattr(TC_fdb) : true) #define FDBLOCKRECORD(TC_fdb, TC_wr, TC_id) \ ((TC_fdb)->mmtx ? tcfdblockrecord((TC_fdb), (TC_wr), (TC_id)) : true) #define FDBUNLOCKRECORD(TC_fdb, TC_id) \ ((TC_fdb)->mmtx ? tcfdbunlockrecord((TC_fdb), (TC_id)) : true) #define FDBLOCKALLRECORDS(TC_fdb, TC_wr) \ ((TC_fdb)->mmtx ? tcfdblockallrecords((TC_fdb), (TC_wr)) : true) #define FDBUNLOCKALLRECORDS(TC_fdb) \ ((TC_fdb)->mmtx ? tcfdbunlockallrecords(TC_fdb) : true) #define FDBLOCKWAL(TC_fdb) \ ((TC_fdb)->mmtx ? tcfdblockwal(TC_fdb) : true) #define FDBUNLOCKWAL(TC_fdb) \ ((TC_fdb)->mmtx ? tcfdbunlockwal(TC_fdb) : true) #define FDBTHREADYIELD(TC_fdb) \ do { if((TC_fdb)->mmtx) sched_yield(); } while(false) /* private function prototypes */ static void tcfdbdumpmeta(TCFDB *fdb, char *hbuf); static void tcfdbloadmeta(TCFDB *fdb, const char *hbuf); static void tcfdbclear(TCFDB *fdb); static void tcfdbsetflag(TCFDB *fdb, int flag, bool sign); static bool tcfdbwalinit(TCFDB *fdb); static bool tcfdbwalwrite(TCFDB *fdb, uint64_t off, int64_t size); static int tcfdbwalrestore(TCFDB *fdb, const char *path); static bool tcfdbwalremove(TCFDB *fdb, const char *path); static bool tcfdbopenimpl(TCFDB *fdb, const char *path, int omode); static bool tcfdbcloseimpl(TCFDB *fdb); static int64_t tcfdbprevid(TCFDB *fdb, int64_t id); static int64_t tcfdbnextid(TCFDB *fdb, int64_t id); static bool tcfdbputimpl(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz, int dmode); static bool tcfdboutimpl(TCFDB *fdb, int64_t id); static const void *tcfdbgetimpl(TCFDB *fdb, int64_t id, int *sp); static bool tcfdbiterinitimpl(TCFDB *fdb); static uint64_t tcfdbiternextimpl(TCFDB *fdb); static uint64_t *tcfdbrangeimpl(TCFDB *fdb, int64_t lower, int64_t upper, int max, int *np); static bool tcfdboptimizeimpl(TCFDB *fdb, int32_t width, int64_t limsiz); static bool tcfdbvanishimpl(TCFDB *fdb); static bool tcfdbcopyimpl(TCFDB *fdb, const char *path); static bool tcfdbiterjumpimpl(TCFDB *fdb, int64_t id); static bool tcfdbforeachimpl(TCFDB *fdb, TCITER iter, void *op); static bool tcfdblockmethod(TCFDB *fdb, bool wr); static bool tcfdbunlockmethod(TCFDB *fdb); static bool tcfdblockattr(TCFDB *fdb); static bool tcfdbunlockattr(TCFDB *fdb); static bool tcfdblockrecord(TCFDB *fdb, bool wr, uint64_t id); static bool tcfdbunlockrecord(TCFDB *fdb, uint64_t id); static bool tcfdblockallrecords(TCFDB *fdb, bool wr); static bool tcfdbunlockallrecords(TCFDB *fdb); static bool tcfdblockwal(TCFDB *fdb); static bool tcfdbunlockwal(TCFDB *fdb); /* debugging function prototypes */ void tcfdbprintmeta(TCFDB *fdb); /************************************************************************************************* * API *************************************************************************************************/ /* Get the message string corresponding to an error code. */ const char *tcfdberrmsg(int ecode){ return tcerrmsg(ecode); } /* Create a fixed-length database object. */ TCFDB *tcfdbnew(void){ TCFDB *fdb; TCMALLOC(fdb, sizeof(*fdb)); tcfdbclear(fdb); return fdb; } /* Delete a fixed-length database object. */ void tcfdbdel(TCFDB *fdb){ assert(fdb); if(fdb->fd >= 0) tcfdbclose(fdb); if(fdb->mmtx){ pthread_key_delete(*(pthread_key_t *)fdb->eckey); pthread_mutex_destroy(fdb->wmtx); pthread_mutex_destroy(fdb->tmtx); for(int i = FDBRMTXNUM - 1; i >= 0; i--){ pthread_rwlock_destroy((pthread_rwlock_t *)fdb->rmtxs + i); } pthread_mutex_destroy(fdb->amtx); pthread_rwlock_destroy(fdb->mmtx); TCFREE(fdb->eckey); TCFREE(fdb->wmtx); TCFREE(fdb->tmtx); TCFREE(fdb->rmtxs); TCFREE(fdb->amtx); TCFREE(fdb->mmtx); } TCFREE(fdb); } /* Get the last happened error code of a fixed-length database object. */ int tcfdbecode(TCFDB *fdb){ assert(fdb); return fdb->mmtx ? (int)(intptr_t)pthread_getspecific(*(pthread_key_t *)fdb->eckey) : fdb->ecode; } /* Set mutual exclusion control of a fixed-length database object for threading. */ bool tcfdbsetmutex(TCFDB *fdb){ assert(fdb); if(!TCUSEPTHREAD) return true; if(fdb->mmtx || fdb->fd >= 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } TCMALLOC(fdb->mmtx, sizeof(pthread_rwlock_t)); TCMALLOC(fdb->amtx, sizeof(pthread_mutex_t)); TCMALLOC(fdb->rmtxs, sizeof(pthread_rwlock_t) * FDBRMTXNUM); TCMALLOC(fdb->tmtx, sizeof(pthread_mutex_t)); TCMALLOC(fdb->wmtx, sizeof(pthread_mutex_t)); TCMALLOC(fdb->eckey, sizeof(pthread_key_t)); bool err = false; if(pthread_rwlock_init(fdb->mmtx, NULL) != 0) err = true; if(pthread_mutex_init(fdb->amtx, NULL) != 0) err = true; for(int i = 0; i < FDBRMTXNUM; i++){ if(pthread_rwlock_init((pthread_rwlock_t *)fdb->rmtxs + i, NULL) != 0) err = true; } if(pthread_mutex_init(fdb->tmtx, NULL) != 0) err = true; if(pthread_mutex_init(fdb->wmtx, NULL) != 0) err = true; if(pthread_key_create(fdb->eckey, NULL) != 0) err = true; if(err){ TCFREE(fdb->eckey); TCFREE(fdb->wmtx); TCFREE(fdb->tmtx); TCFREE(fdb->rmtxs); TCFREE(fdb->amtx); TCFREE(fdb->mmtx); fdb->eckey = NULL; fdb->wmtx = NULL; fdb->tmtx = NULL; fdb->rmtxs = NULL; fdb->amtx = NULL; fdb->mmtx = NULL; return false; } return true; } /* Set the tuning parameters of a fixed-length database object. */ bool tcfdbtune(TCFDB *fdb, int32_t width, int64_t limsiz){ assert(fdb); if(fdb->fd >= 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } fdb->width = (width > 0) ? width : FDBDEFWIDTH; fdb->limsiz = (limsiz > 0) ? limsiz : FDBDEFLIMSIZ; if(fdb->limsiz < FDBHEADSIZ + fdb->width + sizeof(uint32_t)) fdb->limsiz = FDBHEADSIZ + fdb->width + sizeof(uint32_t); fdb->limsiz = tcpagealign(fdb->limsiz); return true; } /* Open a database file and connect a fixed-length database object. */ bool tcfdbopen(TCFDB *fdb, const char *path, int omode){ assert(fdb && path); if(!FDBLOCKMETHOD(fdb, true)) return false; if(fdb->fd >= 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } char *rpath = tcrealpath(path); if(!rpath){ int ecode = TCEOPEN; switch(errno){ case EACCES: ecode = TCENOPERM; break; case ENOENT: ecode = TCENOFILE; break; case ENOTDIR: ecode = TCENOFILE; break; } tcfdbsetecode(fdb, ecode, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(!tcpathlock(rpath)){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); TCFREE(rpath); FDBUNLOCKMETHOD(fdb); return false; } bool rv = tcfdbopenimpl(fdb, path, omode); if(rv){ fdb->rpath = rpath; } else { tcpathunlock(rpath); TCFREE(rpath); } FDBUNLOCKMETHOD(fdb); return rv; } /* Close a fixed-length database object. */ bool tcfdbclose(TCFDB *fdb){ assert(fdb); if(!FDBLOCKMETHOD(fdb, true)) return false; if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } bool rv = tcfdbcloseimpl(fdb); tcpathunlock(fdb->rpath); TCFREE(fdb->rpath); fdb->rpath = NULL; FDBUNLOCKMETHOD(fdb); return rv; } /* Store a record into a fixed-length database object. */ bool tcfdbput(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz){ assert(fdb && vbuf && vsiz >= 0); if(!FDBLOCKMETHOD(fdb, id < 1)) return false; if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(id == FDBIDMIN){ id = fdb->min; } else if(id == FDBIDPREV){ id = fdb->min - 1; } else if(id == FDBIDMAX){ id = fdb->max; } else if(id == FDBIDNEXT){ id = fdb->max + 1; } if(id < 1 || id > fdb->limid){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(!FDBLOCKRECORD(fdb, true, id)){ FDBUNLOCKMETHOD(fdb); return false; } bool rv = tcfdbputimpl(fdb, id, vbuf, vsiz, FDBPDOVER); FDBUNLOCKRECORD(fdb, id); FDBUNLOCKMETHOD(fdb); return rv; } /* 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){ assert(fdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); return tcfdbput(fdb, tcfdbkeytoid(kbuf, ksiz), vbuf, vsiz); } /* Store a string record with a decimal key into a fixed-length database object. */ bool tcfdbput3(TCFDB *fdb, const char *kstr, const void *vstr){ assert(fdb && kstr && vstr); return tcfdbput(fdb, tcfdbkeytoid(kstr, strlen(kstr)), vstr, strlen(vstr)); } /* Store a new record into a fixed-length database object. */ bool tcfdbputkeep(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz){ assert(fdb && vbuf && vsiz >= 0); if(!FDBLOCKMETHOD(fdb, id < 1)) return false; if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(id == FDBIDMIN){ id = fdb->min; } else if(id == FDBIDPREV){ id = fdb->min - 1; } else if(id == FDBIDMAX){ id = fdb->max; } else if(id == FDBIDNEXT){ id = fdb->max + 1; } if(id < 1 || id > fdb->limid){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(!FDBLOCKRECORD(fdb, true, id)){ FDBUNLOCKMETHOD(fdb); return false; } bool rv = tcfdbputimpl(fdb, id, vbuf, vsiz, FDBPDKEEP); FDBUNLOCKRECORD(fdb, id); FDBUNLOCKMETHOD(fdb); return rv; } /* 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){ assert(fdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); return tcfdbputkeep(fdb, tcfdbkeytoid(kbuf, ksiz), vbuf, vsiz); } /* 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){ assert(fdb && kstr && vstr); return tcfdbputkeep(fdb, tcfdbkeytoid(kstr, strlen(kstr)), vstr, strlen(vstr)); } /* 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){ assert(fdb && vbuf && vsiz >= 0); if(!FDBLOCKMETHOD(fdb, id < 1)) return false; if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(id == FDBIDMIN){ id = fdb->min; } else if(id == FDBIDPREV){ id = fdb->min - 1; } else if(id == FDBIDMAX){ id = fdb->max; } else if(id == FDBIDNEXT){ id = fdb->max + 1; } if(id < 1 || id > fdb->limid){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(!FDBLOCKRECORD(fdb, true, id)){ FDBUNLOCKMETHOD(fdb); return false; } bool rv = tcfdbputimpl(fdb, id, vbuf, vsiz, FDBPDCAT); FDBUNLOCKRECORD(fdb, id); FDBUNLOCKMETHOD(fdb); return rv; } /* 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){ assert(fdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); return tcfdbputcat(fdb, tcfdbkeytoid(kbuf, ksiz), vbuf, vsiz); } /* Concatenate a string value with a decimal key in a fixed-length database object. */ bool tcfdbputcat3(TCFDB *fdb, const char *kstr, const void *vstr){ assert(fdb && kstr && vstr); return tcfdbputcat(fdb, tcfdbkeytoid(kstr, strlen(kstr)), vstr, strlen(vstr)); } /* Remove a record of a fixed-length database object. */ bool tcfdbout(TCFDB *fdb, int64_t id){ assert(fdb); if(!FDBLOCKMETHOD(fdb, true)) return false; if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(id == FDBIDMIN){ id = fdb->min; } else if(id == FDBIDMAX){ id = fdb->max; } if(id < 1 || id > fdb->limid){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(!FDBLOCKRECORD(fdb, true, id)){ FDBUNLOCKMETHOD(fdb); return false; } bool rv = tcfdboutimpl(fdb, id); FDBUNLOCKRECORD(fdb, id); FDBUNLOCKMETHOD(fdb); return rv; } /* Remove a record with a decimal key of a fixed-length database object. */ bool tcfdbout2(TCFDB *fdb, const void *kbuf, int ksiz){ assert(fdb && kbuf && ksiz >= 0); return tcfdbout(fdb, tcfdbkeytoid(kbuf, ksiz)); } /* Remove a string record with a decimal key of a fixed-length database object. */ bool tcfdbout3(TCFDB *fdb, const char *kstr){ assert(fdb && kstr); return tcfdbout(fdb, tcfdbkeytoid(kstr, strlen(kstr))); } /* Retrieve a record in a fixed-length database object. */ void *tcfdbget(TCFDB *fdb, int64_t id, int *sp){ assert(fdb && sp); if(!FDBLOCKMETHOD(fdb, false)) return false; if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(id == FDBIDMIN){ id = fdb->min; } else if(id == FDBIDMAX){ id = fdb->max; } if(id < 1 || id > fdb->limid){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(!FDBLOCKRECORD(fdb, false, id)){ FDBUNLOCKMETHOD(fdb); return false; } const void *vbuf = tcfdbgetimpl(fdb, id, sp); char *rv = vbuf ? tcmemdup(vbuf, *sp) : NULL; FDBUNLOCKRECORD(fdb, id); FDBUNLOCKMETHOD(fdb); return rv; } /* Retrieve a record with a decimal key in a fixed-length database object. */ void *tcfdbget2(TCFDB *fdb, const void *kbuf, int ksiz, int *sp){ assert(fdb && kbuf && ksiz >= 0 && sp); return tcfdbget(fdb, tcfdbkeytoid(kbuf, ksiz), sp); } /* Retrieve a string record with a decimal key in a fixed-length database object. */ char *tcfdbget3(TCFDB *fdb, const char *kstr){ assert(fdb && kstr); int vsiz; return tcfdbget(fdb, tcfdbkeytoid(kstr, strlen(kstr)), &vsiz); } /* 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){ assert(fdb && vbuf && max >= 0); if(!FDBLOCKMETHOD(fdb, false)) return false; if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(id == FDBIDMIN){ id = fdb->min; } else if(id == FDBIDMAX){ id = fdb->max; } if(id < 1 || id > fdb->limid){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(!FDBLOCKRECORD(fdb, false, id)){ FDBUNLOCKMETHOD(fdb); return false; } int vsiz; const void *rbuf = tcfdbgetimpl(fdb, id, &vsiz); if(rbuf){ if(vsiz > max) vsiz = max; memcpy(vbuf, rbuf, vsiz); } else { vsiz = -1; } FDBUNLOCKRECORD(fdb, id); FDBUNLOCKMETHOD(fdb); return vsiz; } /* Get the size of the value of a record in a fixed-length database object. */ int tcfdbvsiz(TCFDB *fdb, int64_t id){ assert(fdb); if(!FDBLOCKMETHOD(fdb, false)) return false; if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(id == FDBIDMIN){ id = fdb->min; } else if(id == FDBIDMAX){ id = fdb->max; } if(id < 1 || id > fdb->limid){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(!FDBLOCKRECORD(fdb, false, id)){ FDBUNLOCKMETHOD(fdb); return false; } int vsiz; const void *vbuf = tcfdbgetimpl(fdb, id, &vsiz); if(!vbuf) vsiz = -1; FDBUNLOCKRECORD(fdb, id); FDBUNLOCKMETHOD(fdb); return vsiz; } /* 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){ assert(fdb && kbuf && ksiz >= 0); return tcfdbvsiz(fdb, tcfdbkeytoid(kbuf, ksiz)); } /* Get the size of the string value with a decimal key in a fixed-length database object. */ int tcfdbvsiz3(TCFDB *fdb, const char *kstr){ assert(fdb && kstr); return tcfdbvsiz(fdb, tcfdbkeytoid(kstr, strlen(kstr))); } /* Initialize the iterator of a fixed-length database object. */ bool tcfdbiterinit(TCFDB *fdb){ assert(fdb); if(!FDBLOCKMETHOD(fdb, true)) return false; if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } bool rv = tcfdbiterinitimpl(fdb); FDBUNLOCKMETHOD(fdb); return rv; } /* Get the next ID number of the iterator of a fixed-length database object. */ uint64_t tcfdbiternext(TCFDB *fdb){ assert(fdb); if(!FDBLOCKMETHOD(fdb, true)) return false; if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } uint64_t rv = tcfdbiternextimpl(fdb); FDBUNLOCKMETHOD(fdb); return rv; } /* Get the next decimay key of the iterator of a fixed-length database object. */ void *tcfdbiternext2(TCFDB *fdb, int *sp){ assert(fdb && sp); uint64_t id = tcfdbiternextimpl(fdb); if(id < 1) return NULL; char kbuf[TCNUMBUFSIZ]; int ksiz = sprintf(kbuf, "%llu", (unsigned long long)id); *sp = ksiz; return tcmemdup(kbuf, ksiz); } /* Get the next decimay key string of the iterator of a fixed-length database object. */ char *tcfdbiternext3(TCFDB *fdb){ assert(fdb); int ksiz; return tcfdbiternext2(fdb, &ksiz); } /* Get range matching decimal keys in a fixed-length database object. */ uint64_t *tcfdbrange(TCFDB *fdb, int64_t lower, int64_t upper, int max, int *np){ assert(fdb && np); if(!FDBLOCKMETHOD(fdb, true)) return false; if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); *np = 0; return tcmalloc(1); } if(lower == FDBIDMIN) lower = fdb->min; if(upper == FDBIDMAX) upper = fdb->max; if(lower < 1 || lower > fdb->limid || upper < 1 || upper > fdb->limid){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); *np = 0; return tcmalloc(1); } uint64_t *rv = tcfdbrangeimpl(fdb, lower, upper, max, np); FDBUNLOCKMETHOD(fdb); return rv; } /* 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){ assert(fdb && lbuf && lsiz >= 0 && ubuf && usiz >= 0); int num; uint64_t *ids = tcfdbrange(fdb, tcfdbkeytoid(lbuf, lsiz), tcfdbkeytoid(ubuf, usiz), max, &num); TCLIST *keys = tclistnew2(num); for(int i = 0; i < num; i++){ char kbuf[TCNUMBUFSIZ]; int ksiz = sprintf(kbuf, "%llu", (unsigned long long)ids[i]); TCLISTPUSH(keys, kbuf, ksiz); } TCFREE(ids); return keys; } /* 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){ assert(fdb && lstr && ustr); return tcfdbrange2(fdb, lstr, strlen(lstr), ustr, strlen(ustr), max); } /* Get keys with an interval notation in a fixed-length database object. */ TCLIST *tcfdbrange4(TCFDB *fdb, const void *ibuf, int isiz, int max){ assert(fdb && ibuf && isiz >= 0); char *expr; TCMEMDUP(expr, ibuf, isiz); char *pv = expr; while(*pv > '\0' && *pv <= ' '){ pv++; } bool linc = false; if(*pv == '['){ linc = true; } else if(*pv != '('){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); TCFREE(expr); return tclistnew(); } pv++; char *sep = strchr(pv, ','); if(!sep){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); TCFREE(expr); return tclistnew(); } *sep = '\0'; tcstrtrim(pv); int64_t lower = tcfdbkeytoid(pv, strlen(pv)); pv = sep + 1; bool uinc = false; if((sep = strchr(pv, ']')) != NULL){ uinc = true; *sep = '\0'; } else if((sep = strchr(pv, ')')) != NULL){ *sep = '\0'; } else { tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); TCFREE(expr); return tclistnew(); } tcstrtrim(pv); int64_t upper = tcfdbkeytoid(pv, strlen(pv)); if(lower == FDBIDMIN){ lower = fdb->min; } else if(lower == FDBIDPREV){ lower = fdb->min - 1; } else if(lower == FDBIDMAX){ lower = fdb->max; } else if(lower == FDBIDNEXT){ lower = fdb->max + 1; } if(!linc) lower++; if(upper == FDBIDMIN){ upper = fdb->min; } else if(upper == FDBIDPREV){ upper = fdb->min - 1; } else if(upper == FDBIDMAX){ upper = fdb->max; } else if(upper == FDBIDNEXT){ upper = fdb->max + 1; } if(!uinc) upper--; TCFREE(expr); int num; uint64_t *ids = tcfdbrange(fdb, lower, upper, max, &num); TCLIST *keys = tclistnew2(num); for(int i = 0; i < num; i++){ char kbuf[TCNUMBUFSIZ]; int ksiz = sprintf(kbuf, "%llu", (unsigned long long)ids[i]); TCLISTPUSH(keys, kbuf, ksiz); } TCFREE(ids); return keys; } /* Get keys with an interval notation string in a fixed-length database object. */ TCLIST *tcfdbrange5(TCFDB *fdb, const void *istr, int max){ assert(fdb && istr); return tcfdbrange4(fdb, istr, strlen(istr), max); } /* Add an integer to a record in a fixed-length database object. */ int tcfdbaddint(TCFDB *fdb, int64_t id, int num){ assert(fdb); if(!FDBLOCKMETHOD(fdb, id < 1)) return INT_MIN; if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return INT_MIN; } if(id == FDBIDMIN){ id = fdb->min; } else if(id == FDBIDPREV){ id = fdb->min - 1; } else if(id == FDBIDMAX){ id = fdb->max; } else if(id == FDBIDNEXT){ id = fdb->max + 1; } if(id < 1 || id > fdb->limid){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return INT_MIN; } if(!FDBLOCKRECORD(fdb, true, id)){ FDBUNLOCKMETHOD(fdb); return INT_MIN; } bool rv = tcfdbputimpl(fdb, id, (char *)&num, sizeof(num), FDBPDADDINT); FDBUNLOCKRECORD(fdb, id); FDBUNLOCKMETHOD(fdb); return rv ? num : INT_MIN; } /* Add a real number to a record in a fixed-length database object. */ double tcfdbadddouble(TCFDB *fdb, int64_t id, double num){ assert(fdb); if(!FDBLOCKMETHOD(fdb, id < 1)) return nan(""); if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return nan(""); } if(id == FDBIDMIN){ id = fdb->min; } else if(id == FDBIDPREV){ id = fdb->min - 1; } else if(id == FDBIDMAX){ id = fdb->max; } else if(id == FDBIDNEXT){ id = fdb->max + 1; } if(id < 1 || id > fdb->limid){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return nan(""); } if(!FDBLOCKRECORD(fdb, true, id)){ FDBUNLOCKMETHOD(fdb); return nan(""); } bool rv = tcfdbputimpl(fdb, id, (char *)&num, sizeof(num), FDBPDADDDBL); FDBUNLOCKRECORD(fdb, id); FDBUNLOCKMETHOD(fdb); return rv ? num : nan(""); } /* Synchronize updated contents of a fixed-length database object with the file and the device. */ bool tcfdbsync(TCFDB *fdb){ assert(fdb); if(!FDBLOCKMETHOD(fdb, true)) return false; if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER) || fdb->tran){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } bool rv = tcfdbmemsync(fdb, true); FDBUNLOCKMETHOD(fdb); return rv; } /* Optimize the file of a fixed-length database object. */ bool tcfdboptimize(TCFDB *fdb, int32_t width, int64_t limsiz){ assert(fdb); if(!FDBLOCKMETHOD(fdb, true)) return false; if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER) || fdb->tran){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } FDBTHREADYIELD(fdb); bool rv = tcfdboptimizeimpl(fdb, width, limsiz); FDBUNLOCKMETHOD(fdb); return rv; } /* Remove all records of a fixed-length database object. */ bool tcfdbvanish(TCFDB *fdb){ assert(fdb); if(!FDBLOCKMETHOD(fdb, true)) return false; if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER) || fdb->tran){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } FDBTHREADYIELD(fdb); bool rv = tcfdbvanishimpl(fdb); FDBUNLOCKMETHOD(fdb); return rv; } /* Copy the database file of a fixed-length database object. */ bool tcfdbcopy(TCFDB *fdb, const char *path){ assert(fdb && path); if(!FDBLOCKMETHOD(fdb, false)) return false; if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(!FDBLOCKALLRECORDS(fdb, false)){ FDBUNLOCKMETHOD(fdb); return false; } FDBTHREADYIELD(fdb); bool rv = tcfdbcopyimpl(fdb, path); FDBUNLOCKALLRECORDS(fdb); FDBUNLOCKMETHOD(fdb); return rv; } /* Begin the transaction of a fixed-length database object. */ bool tcfdbtranbegin(TCFDB *fdb){ assert(fdb); for(double wsec = 1.0 / sysconf(_SC_CLK_TCK); true; wsec *= 2){ if(!FDBLOCKMETHOD(fdb, true)) return false; if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER) || fdb->fatal){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(!fdb->tran) break; FDBUNLOCKMETHOD(fdb); if(wsec > 1.0) wsec = 1.0; tcsleep(wsec); } if(!tcfdbmemsync(fdb, false)){ FDBUNLOCKMETHOD(fdb); return false; } if((fdb->omode & FDBOTSYNC) && fsync(fdb->fd) == -1){ tcfdbsetecode(fdb, TCESYNC, __FILE__, __LINE__, __func__); return false; } if(fdb->walfd < 0){ char *tpath = tcsprintf("%s%c%s", fdb->path, MYEXTCHR, FDBWALSUFFIX); int walfd = open(tpath, O_RDWR | O_CREAT | O_TRUNC, FDBFILEMODE); TCFREE(tpath); if(walfd < 0){ int ecode = TCEOPEN; switch(errno){ case EACCES: ecode = TCENOPERM; break; case ENOENT: ecode = TCENOFILE; break; case ENOTDIR: ecode = TCENOFILE; break; } tcfdbsetecode(fdb, ecode, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } fdb->walfd = walfd; } tcfdbsetflag(fdb, FDBFOPEN, false); if(!tcfdbwalinit(fdb)){ tcfdbsetflag(fdb, FDBFOPEN, true); FDBUNLOCKMETHOD(fdb); return false; } tcfdbsetflag(fdb, FDBFOPEN, true); fdb->tran = true; FDBUNLOCKMETHOD(fdb); return true; } /* Commit the transaction of a fixed-length database object. */ bool tcfdbtrancommit(TCFDB *fdb){ assert(fdb); if(!FDBLOCKMETHOD(fdb, true)) return false; if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER) || fdb->fatal || !fdb->tran){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } bool err = false; if(!tcfdbmemsync(fdb, fdb->omode & FDBOTSYNC)) err = true; if(!err && ftruncate(fdb->walfd, 0) == -1){ tcfdbsetecode(fdb, TCETRUNC, __FILE__, __LINE__, __func__); err = true; } fdb->tran = false; FDBUNLOCKMETHOD(fdb); return !err; } /* Abort the transaction of a fixed-length database object. */ bool tcfdbtranabort(TCFDB *fdb){ assert(fdb); if(!FDBLOCKMETHOD(fdb, true)) return false; if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER) || !fdb->tran){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } bool err = false; if(!tcfdbmemsync(fdb, false)) err = true; if(!tcfdbwalrestore(fdb, fdb->path)) err = true; char hbuf[FDBHEADSIZ]; if(lseek(fdb->fd, 0, SEEK_SET) == -1){ tcfdbsetecode(fdb, TCESEEK, __FILE__, __LINE__, __func__); err = false; } else if(!tcread(fdb->fd, hbuf, FDBHEADSIZ)){ tcfdbsetecode(fdb, TCEREAD, __FILE__, __LINE__, __func__); err = false; } else { tcfdbloadmeta(fdb, hbuf); } fdb->tran = false; FDBUNLOCKMETHOD(fdb); return !err; } /* Get the file path of a fixed-length database object. */ const char *tcfdbpath(TCFDB *fdb){ assert(fdb); if(!FDBLOCKMETHOD(fdb, false)) return NULL; if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return NULL; } const char *rv = fdb->path; FDBUNLOCKMETHOD(fdb); return rv; } /* Get the number of records of a fixed-length database object. */ uint64_t tcfdbrnum(TCFDB *fdb){ assert(fdb); if(!FDBLOCKMETHOD(fdb, false)) return 0; if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return 0; } uint64_t rv = fdb->rnum; FDBUNLOCKMETHOD(fdb); return rv; } /* Get the size of the database file of a fixed-length database object. */ uint64_t tcfdbfsiz(TCFDB *fdb){ assert(fdb); if(!FDBLOCKMETHOD(fdb, false)) return 0; if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return 0; } uint64_t rv = fdb->fsiz; FDBUNLOCKMETHOD(fdb); return rv; } /************************************************************************************************* * features for experts *************************************************************************************************/ /* Set the error code of a fixed-length database object. */ void tcfdbsetecode(TCFDB *fdb, int ecode, const char *filename, int line, const char *func){ assert(fdb && filename && line >= 1 && func); int myerrno = errno; if(!fdb->fatal){ fdb->ecode = ecode; if(fdb->mmtx) pthread_setspecific(*(pthread_key_t *)fdb->eckey, (void *)(intptr_t)ecode); } if(ecode != TCEINVALID && ecode != TCEKEEP && ecode != TCENOREC){ fdb->fatal = true; if(fdb->fd >= 0 && (fdb->omode & FDBOWRITER)) tcfdbsetflag(fdb, FDBFFATAL, true); } if(fdb->dbgfd >= 0 && (fdb->dbgfd != UINT16_MAX || fdb->fatal)){ int dbgfd = (fdb->dbgfd == UINT16_MAX) ? 1 : fdb->dbgfd; char obuf[FDBIOBUFSIZ]; int osiz = sprintf(obuf, "ERROR:%s:%d:%s:%s:%d:%s:%d:%s\n", filename, line, func, fdb->path ? fdb->path : "-", ecode, tcfdberrmsg(ecode), myerrno, strerror(myerrno)); tcwrite(dbgfd, obuf, osiz); } } /* Set the file descriptor for debugging output. */ void tcfdbsetdbgfd(TCFDB *fdb, int fd){ assert(fdb && fd >= 0); fdb->dbgfd = fd; } /* Get the file descriptor for debugging output. */ int tcfdbdbgfd(TCFDB *fdb){ assert(fdb); return fdb->dbgfd; } /* Check whether mutual exclusion control is set to a fixed-length database object. */ bool tcfdbhasmutex(TCFDB *fdb){ assert(fdb); return fdb->mmtx != NULL; } /* Synchronize updating contents on memory of a fixed-length database object. */ bool tcfdbmemsync(TCFDB *fdb, bool phys){ assert(fdb); if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } bool err = false; char hbuf[FDBHEADSIZ]; tcfdbdumpmeta(fdb, hbuf); memcpy(fdb->map, hbuf, FDBOPAQUEOFF); if(phys){ if(msync(fdb->map, fdb->limsiz, MS_SYNC) == -1){ tcfdbsetecode(fdb, TCEMMAP, __FILE__, __LINE__, __func__); err = true; } if(fsync(fdb->fd) == -1){ tcfdbsetecode(fdb, TCESYNC, __FILE__, __LINE__, __func__); err = true; } } return !err; } /* Get the minimum ID number of records of a fixed-length database object. */ uint64_t tcfdbmin(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return fdb->min; } /* Get the maximum ID number of records of a fixed-length database object. */ uint64_t tcfdbmax(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return fdb->max; } /* Get the width of the value of each record of a fixed-length database object. */ uint32_t tcfdbwidth(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return fdb->width; } /* Get the limit file size of a fixed-length database object. */ uint64_t tcfdblimsiz(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return fdb->limsiz; } /* Get the limit ID number of a fixed-length database object. */ uint64_t tcfdblimid(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return fdb->limid; } /* Get the inode number of the database file of a fixed-length database object. */ uint64_t tcfdbinode(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return fdb->inode; } /* Get the modification time of the database file of a fixed-length database object. */ time_t tcfdbmtime(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return fdb->mtime; } /* Get the connection mode of a fixed-length database object. */ int tcfdbomode(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return fdb->omode; } /* Get the database type of a fixed-length database object. */ uint8_t tcfdbtype(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return fdb->type; } /* Get the additional flags of a fixed-length database object. */ uint8_t tcfdbflags(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return fdb->flags; } /* Get the pointer to the opaque field of a fixed-length database object. */ char *tcfdbopaque(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return NULL; } return fdb->map + FDBOPAQUEOFF; } /* Store a record into a fixed-length database object with a duplication handler. */ bool tcfdbputproc(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz, TCPDPROC proc, void *op){ assert(fdb && proc); if(!FDBLOCKMETHOD(fdb, id < 1)) return false; if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(id == FDBIDMIN){ id = fdb->min; } else if(id == FDBIDPREV){ id = fdb->min - 1; } else if(id == FDBIDMAX){ id = fdb->max; } else if(id == FDBIDNEXT){ id = fdb->max + 1; } if(id < 1 || id > fdb->limid){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(!FDBLOCKRECORD(fdb, true, id)){ FDBUNLOCKMETHOD(fdb); return false; } FDBPDPROCOP procop; procop.proc = proc; procop.op = op; FDBPDPROCOP *procptr = &procop; tcgeneric_t stack[(FDBDEFWIDTH+TCNUMBUFSIZ)/sizeof(tcgeneric_t)+1]; char *rbuf; if(vbuf){ if(vsiz <= sizeof(stack) - sizeof(procptr)){ rbuf = (char *)stack; } else { TCMALLOC(rbuf, vsiz + sizeof(procptr)); } char *wp = rbuf; memcpy(wp, &procptr, sizeof(procptr)); wp += sizeof(procptr); memcpy(wp, vbuf, vsiz); vbuf = rbuf + sizeof(procptr); } else { rbuf = (char *)stack; memcpy(rbuf, &procptr, sizeof(procptr)); vbuf = rbuf + sizeof(procptr); vsiz = -1; } bool rv = tcfdbputimpl(fdb, id, vbuf, vsiz, FDBPDPROC); if(rbuf != (char *)stack) TCFREE(rbuf); FDBUNLOCKRECORD(fdb, id); FDBUNLOCKMETHOD(fdb); return rv; } /* Move the iterator to the record corresponding a key of a fixed-length database object. */ bool tcfdbiterinit2(TCFDB *fdb, int64_t id){ assert(fdb); if(!FDBLOCKMETHOD(fdb, true)) return false; if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(id == FDBIDMIN){ id = fdb->min; } else if(id == FDBIDMAX){ id = fdb->max; } if(id < 1 || id > fdb->limid){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } bool rv = tcfdbiterjumpimpl(fdb, id); FDBUNLOCKMETHOD(fdb); return rv; } /* Move the iterator to the decimal record of a fixed-length database object. */ bool tcfdbiterinit3(TCFDB *fdb, const void *kbuf, int ksiz){ assert(fdb && kbuf && ksiz >= 0); return tcfdbiterinit2(fdb, tcfdbkeytoid(kbuf, ksiz)); } /* Move the iterator to the decimal string record of a fixed-length database object. */ bool tcfdbiterinit4(TCFDB *fdb, const char *kstr){ assert(fdb && kstr); return tcfdbiterinit2(fdb, tcfdbkeytoid(kstr, strlen(kstr))); } /* Process each record atomically of a fixed-length database object. */ bool tcfdbforeach(TCFDB *fdb, TCITER iter, void *op){ assert(fdb && iter); if(!FDBLOCKMETHOD(fdb, false)) return false; if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); FDBUNLOCKMETHOD(fdb); return false; } if(!FDBLOCKALLRECORDS(fdb, false)){ FDBUNLOCKMETHOD(fdb); return false; } FDBTHREADYIELD(fdb); bool rv = tcfdbforeachimpl(fdb, iter, op); FDBUNLOCKALLRECORDS(fdb); FDBUNLOCKMETHOD(fdb); return rv; } /* Generate the ID number from arbitrary binary data. */ int64_t tcfdbkeytoid(const char *kbuf, int ksiz){ assert(kbuf && ksiz >= 0); if(ksiz == 3 && !memcmp(kbuf, "min", 3)){ return FDBIDMIN; } else if(ksiz == 4 && !memcmp(kbuf, "prev", 4)){ return FDBIDPREV; } else if(ksiz == 3 && !memcmp(kbuf, "max", 3)){ return FDBIDMAX; } else if(ksiz == 4 && !memcmp(kbuf, "next", 4)){ return FDBIDNEXT; } int64_t id = 0; const char *end = kbuf + ksiz; while(kbuf < end){ int c = *(unsigned char *)(kbuf++); if(c >= '0' && c <= '9') id = id * 10 + c - '0'; } return id; } /************************************************************************************************* * private features *************************************************************************************************/ /* Serialize meta data into a buffer. `fdb' specifies the fixed-length database object. `hbuf' specifies the buffer. */ static void tcfdbdumpmeta(TCFDB *fdb, char *hbuf){ memset(hbuf, 0, FDBHEADSIZ); sprintf(hbuf, "%s\n%s:%d\n", FDBMAGICDATA, _TC_FORMATVER, _TC_LIBVER); memcpy(hbuf + FDBTYPEOFF, &(fdb->type), sizeof(fdb->type)); memcpy(hbuf + FDBFLAGSOFF, &(fdb->flags), sizeof(fdb->flags)); uint64_t llnum; llnum = fdb->rnum; llnum = TCHTOILL(llnum); memcpy(hbuf + FDBRNUMOFF, &llnum, sizeof(llnum)); llnum = fdb->fsiz; llnum = TCHTOILL(llnum); memcpy(hbuf + FDBFSIZOFF, &llnum, sizeof(llnum)); llnum = fdb->width; llnum = TCHTOILL(llnum); memcpy(hbuf + FDBWIDTHOFF, &llnum, sizeof(llnum)); llnum = fdb->limsiz; llnum = TCHTOILL(llnum); memcpy(hbuf + FDBLIMSIZOFF, &llnum, sizeof(llnum)); llnum = fdb->min; llnum = TCHTOILL(llnum); memcpy(hbuf + FDBMINOFF, &llnum, sizeof(llnum)); llnum = fdb->max; llnum = TCHTOILL(llnum); memcpy(hbuf + FDBMAXOFF, &llnum, sizeof(llnum)); } /* Deserialize meta data from a buffer. `fdb' specifies the fixed-length database object. `hbuf' specifies the buffer. */ static void tcfdbloadmeta(TCFDB *fdb, const char *hbuf){ memcpy(&(fdb->type), hbuf + FDBTYPEOFF, sizeof(fdb->type)); memcpy(&(fdb->flags), hbuf + FDBFLAGSOFF, sizeof(fdb->flags)); uint64_t llnum; memcpy(&llnum, hbuf + FDBRNUMOFF, sizeof(llnum)); fdb->rnum = TCITOHLL(llnum); memcpy(&llnum, hbuf + FDBFSIZOFF, sizeof(llnum)); fdb->fsiz = TCITOHLL(llnum); memcpy(&llnum, hbuf + FDBWIDTHOFF, sizeof(llnum)); fdb->width = TCITOHLL(llnum); memcpy(&llnum, hbuf + FDBLIMSIZOFF, sizeof(llnum)); fdb->limsiz = TCITOHLL(llnum); memcpy(&llnum, hbuf + FDBMINOFF, sizeof(llnum)); fdb->min = TCITOHLL(llnum); memcpy(&llnum, hbuf + FDBMAXOFF, sizeof(llnum)); fdb->max = TCITOHLL(llnum); } /* Clear all members. `fdb' specifies the fixed-length database object. */ static void tcfdbclear(TCFDB *fdb){ assert(fdb); fdb->mmtx = NULL; fdb->amtx = NULL; fdb->rmtxs = NULL; fdb->tmtx = NULL; fdb->wmtx = NULL; fdb->eckey = NULL; fdb->rpath = NULL; fdb->type = TCDBTFIXED; fdb->flags = 0; fdb->width = FDBDEFWIDTH; fdb->limsiz = FDBDEFLIMSIZ; fdb->wsiz = 0; fdb->rsiz = 0; fdb->limid = 0; fdb->path = NULL; fdb->fd = -1; fdb->omode = 0; fdb->rnum = 0; fdb->fsiz = 0; fdb->min = 0; fdb->max = 0; fdb->iter = 0; fdb->map = NULL; fdb->array = NULL; fdb->ecode = TCESUCCESS; fdb->fatal = false; fdb->inode = 0; fdb->mtime = 0; fdb->tran = false; fdb->walfd = -1; fdb->walend = 0; fdb->dbgfd = -1; fdb->cnt_writerec = -1; fdb->cnt_readrec = -1; fdb->cnt_truncfile = -1; TCDODEBUG(fdb->cnt_writerec = 0); TCDODEBUG(fdb->cnt_readrec = 0); TCDODEBUG(fdb->cnt_truncfile = 0); } /* Set the open flag. `fdb' specifies the fixed-length database object. `flag' specifies the flag value. `sign' specifies the sign. */ static void tcfdbsetflag(TCFDB *fdb, int flag, bool sign){ assert(fdb); char *fp = (char *)fdb->map + FDBFLAGSOFF; if(sign){ *fp |= (uint8_t)flag; } else { *fp &= ~(uint8_t)flag; } fdb->flags = *fp; } /* Initialize the write ahead logging file. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */ static bool tcfdbwalinit(TCFDB *fdb){ assert(fdb); if(lseek(fdb->walfd, 0, SEEK_SET) == -1){ tcfdbsetecode(fdb, TCESEEK, __FILE__, __LINE__, __func__); return false; } if(ftruncate(fdb->walfd, 0) == -1){ tcfdbsetecode(fdb, TCETRUNC, __FILE__, __LINE__, __func__); return false; } uint64_t llnum = fdb->fsiz; llnum = TCHTOILL(llnum); if(!tcwrite(fdb->walfd, &llnum, sizeof(llnum))){ tcfdbsetecode(fdb, TCEWRITE, __FILE__, __LINE__, __func__); return false; } fdb->walend = fdb->fsiz; if(!tcfdbwalwrite(fdb, 0, FDBHEADSIZ)) return false; return true; } /* Write an event into the write ahead logging file. `fdb' specifies the fixed-length database object. `off' specifies the offset of the region to be updated. `size' specifies the size of the region. If successful, the return value is true, else, it is false. */ static bool tcfdbwalwrite(TCFDB *fdb, uint64_t off, int64_t size){ assert(fdb && off >= 0 && size >= 0); if(off + size > fdb->walend) size = fdb->walend - off; if(size < 1) return true; char stack[FDBIOBUFSIZ]; char *buf; if(size + sizeof(off) + sizeof(size) <= FDBIOBUFSIZ){ buf = stack; } else { TCMALLOC(buf, size + sizeof(off) + sizeof(size)); } char *wp = buf; uint64_t llnum = TCHTOILL(off); memcpy(wp, &llnum, sizeof(llnum)); wp += sizeof(llnum); uint32_t lnum = TCHTOIL(size); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); memcpy(wp, fdb->map + off, size); wp += size; if(!FDBLOCKWAL(fdb)) return false; if(!tcwrite(fdb->walfd, buf, wp - buf)){ tcfdbsetecode(fdb, TCEWRITE, __FILE__, __LINE__, __func__); if(buf != stack) TCFREE(buf); FDBUNLOCKWAL(fdb); return false; } if(buf != stack) TCFREE(buf); if((fdb->omode & FDBOTSYNC) && fsync(fdb->walfd) == -1){ tcfdbsetecode(fdb, TCESYNC, __FILE__, __LINE__, __func__); FDBUNLOCKWAL(fdb); return false; } FDBUNLOCKWAL(fdb); return true; } /* Restore the database from the write ahead logging file. `fdb' specifies the fixed-length database object. `path' specifies the path of the database file. If successful, the return value is true, else, it is false. */ static int tcfdbwalrestore(TCFDB *fdb, const char *path){ assert(fdb && path); char *tpath = tcsprintf("%s%c%s", path, MYEXTCHR, FDBWALSUFFIX); int walfd = open(tpath, O_RDONLY, FDBFILEMODE); TCFREE(tpath); if(walfd < 0) return false; bool err = false; uint64_t walsiz = 0; struct stat sbuf; if(fstat(walfd, &sbuf) == 0){ walsiz = sbuf.st_size; } else { tcfdbsetecode(fdb, TCESTAT, __FILE__, __LINE__, __func__); err = true; } if(walsiz >= sizeof(walsiz) + FDBHEADSIZ){ int dbfd = fdb->fd; int tfd = -1; if(!(fdb->omode & FDBOWRITER)){ tfd = open(path, O_WRONLY, FDBFILEMODE); if(tfd >= 0){ dbfd = tfd; } else { int ecode = TCEOPEN; switch(errno){ case EACCES: ecode = TCENOPERM; break; case ENOENT: ecode = TCENOFILE; break; case ENOTDIR: ecode = TCENOFILE; break; } tcfdbsetecode(fdb, ecode, __FILE__, __LINE__, __func__); err = true; } } uint64_t fsiz = 0; if(tcread(walfd, &fsiz, sizeof(fsiz))){ fsiz = TCITOHLL(fsiz); } else { tcfdbsetecode(fdb, TCEREAD, __FILE__, __LINE__, __func__); err = true; } TCLIST *list = tclistnew(); uint64_t waloff = sizeof(fsiz); char stack[FDBIOBUFSIZ]; while(waloff < walsiz){ uint64_t off; uint32_t size; if(!tcread(walfd, stack, sizeof(off) + sizeof(size))){ tcfdbsetecode(fdb, TCEREAD, __FILE__, __LINE__, __func__); err = true; break; } memcpy(&off, stack, sizeof(off)); off = TCITOHLL(off); memcpy(&size, stack + sizeof(off), sizeof(size)); size = TCITOHL(size); char *buf; if(sizeof(off) + size <= FDBIOBUFSIZ){ buf = stack; } else { TCMALLOC(buf, sizeof(off) + size); } *(uint64_t *)buf = off; if(!tcread(walfd, buf + sizeof(off), size)){ tcfdbsetecode(fdb, TCEREAD, __FILE__, __LINE__, __func__); err = true; if(buf != stack) TCFREE(buf); break; } TCLISTPUSH(list, buf, sizeof(off) + size); if(buf != stack) TCFREE(buf); waloff += sizeof(off) + sizeof(size) + size; } for(int i = TCLISTNUM(list) - 1; i >= 0; i--){ const char *rec; int size; TCLISTVAL(rec, list, i, size); uint64_t off = *(uint64_t *)rec; rec += sizeof(off); size -= sizeof(off); if(lseek(dbfd, off, SEEK_SET) == -1){ tcfdbsetecode(fdb, TCESEEK, __FILE__, __LINE__, __func__); err = true; break; } if(!tcwrite(dbfd, rec, size)){ tcfdbsetecode(fdb, TCEWRITE, __FILE__, __LINE__, __func__); err = true; break; } } tclistdel(list); if(ftruncate(dbfd, fsiz) == -1){ tcfdbsetecode(fdb, TCETRUNC, __FILE__, __LINE__, __func__); err = true; } if((fdb->omode & FDBOTSYNC) && fsync(dbfd) == -1){ tcfdbsetecode(fdb, TCESYNC, __FILE__, __LINE__, __func__); err = true; } if(tfd >= 0 && close(tfd) == -1){ tcfdbsetecode(fdb, TCECLOSE, __FILE__, __LINE__, __func__); err = true; } } else { err = true; } if(close(walfd) == -1){ tcfdbsetecode(fdb, TCECLOSE, __FILE__, __LINE__, __func__); err = true; } return !err; } /* Remove the write ahead logging file. `fdb' specifies the fixed-length database object. `path' specifies the path of the database file. If successful, the return value is true, else, it is false. */ static bool tcfdbwalremove(TCFDB *fdb, const char *path){ assert(fdb && path); char *tpath = tcsprintf("%s%c%s", path, MYEXTCHR, FDBWALSUFFIX); bool err = false; if(unlink(tpath) == -1 && errno != ENOENT){ tcfdbsetecode(fdb, TCEUNLINK, __FILE__, __LINE__, __func__); err = true; } TCFREE(tpath); return !err; } /* Open a database file and connect a fixed-length database object. `fdb' specifies the fixed-length database object. `path' specifies the path of the database file. `omode' specifies the connection mode. If successful, the return value is true, else, it is false. */ static bool tcfdbopenimpl(TCFDB *fdb, const char *path, int omode){ assert(fdb && path); int mode = O_RDONLY; if(omode & FDBOWRITER){ mode = O_RDWR; if(omode & FDBOCREAT) mode |= O_CREAT; } int fd = open(path, mode, FDBFILEMODE); if(fd < 0){ int ecode = TCEOPEN; switch(errno){ case EACCES: ecode = TCENOPERM; break; case ENOENT: ecode = TCENOFILE; break; case ENOTDIR: ecode = TCENOFILE; break; } tcfdbsetecode(fdb, ecode, __FILE__, __LINE__, __func__); return false; } if(!(omode & FDBONOLCK)){ if(!tclock(fd, omode & FDBOWRITER, omode & FDBOLCKNB)){ tcfdbsetecode(fdb, TCELOCK, __FILE__, __LINE__, __func__); close(fd); return false; } } if((omode & FDBOWRITER) && (omode & FDBOTRUNC)){ if(ftruncate(fd, 0) == -1){ tcfdbsetecode(fdb, TCETRUNC, __FILE__, __LINE__, __func__); close(fd); return false; } if(!tcfdbwalremove(fdb, path)){ close(fd); return false; } } struct stat sbuf; if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){ tcfdbsetecode(fdb, TCESTAT, __FILE__, __LINE__, __func__); close(fd); return false; } char hbuf[FDBHEADSIZ]; if((omode & FDBOWRITER) && sbuf.st_size < 1){ fdb->flags = 0; fdb->rnum = 0; fdb->fsiz = FDBHEADSIZ; fdb->min = 0; fdb->max = 0; tcfdbdumpmeta(fdb, hbuf); if(!tcwrite(fd, hbuf, FDBHEADSIZ)){ tcfdbsetecode(fdb, TCEWRITE, __FILE__, __LINE__, __func__); close(fd); return false; } sbuf.st_size = fdb->fsiz; } if(lseek(fd, 0, SEEK_SET) == -1){ tcfdbsetecode(fdb, TCESEEK, __FILE__, __LINE__, __func__); close(fd); return false; } if(!tcread(fd, hbuf, FDBHEADSIZ)){ tcfdbsetecode(fdb, TCEREAD, __FILE__, __LINE__, __func__); close(fd); return false; } int type = fdb->type; tcfdbloadmeta(fdb, hbuf); if((fdb->flags & FDBFOPEN) && tcfdbwalrestore(fdb, path)){ if(lseek(fd, 0, SEEK_SET) == -1){ tcfdbsetecode(fdb, TCESEEK, __FILE__, __LINE__, __func__); close(fd); return false; } if(!tcread(fd, hbuf, FDBHEADSIZ)){ tcfdbsetecode(fdb, TCEREAD, __FILE__, __LINE__, __func__); close(fd); return false; } tcfdbloadmeta(fdb, hbuf); if(!tcfdbwalremove(fdb, path)){ close(fd); return false; } } if(!(omode & FDBONOLCK)){ if(memcmp(hbuf, FDBMAGICDATA, strlen(FDBMAGICDATA)) || fdb->type != type || fdb->width < 1 || sbuf.st_size < fdb->fsiz || fdb->limsiz < FDBHEADSIZ || fdb->fsiz > fdb->limsiz){ tcfdbsetecode(fdb, TCEMETA, __FILE__, __LINE__, __func__); close(fd); return false; } if(sbuf.st_size > fdb->fsiz) fdb->fsiz = sbuf.st_size; } void *map = mmap(0, fdb->limsiz, PROT_READ | ((omode & FDBOWRITER) ? PROT_WRITE : 0), MAP_SHARED, fd, 0); if(map == MAP_FAILED){ tcfdbsetecode(fdb, TCEMMAP, __FILE__, __LINE__, __func__); close(fd); return false; } if(fdb->width <= UINT8_MAX){ fdb->wsiz = sizeof(uint8_t); } else if(fdb->width <= UINT16_MAX){ fdb->wsiz = sizeof(uint16_t); } else { fdb->wsiz = sizeof(uint32_t); } fdb->rsiz = fdb->width + fdb->wsiz; fdb->limid = (fdb->limsiz - FDBHEADSIZ) / fdb->rsiz; fdb->path = tcstrdup(path); fdb->fd = fd; fdb->omode = omode; fdb->iter = 0; fdb->map = map; fdb->array = (unsigned char *)map + FDBHEADSIZ; fdb->ecode = TCESUCCESS; fdb->fatal = false; fdb->inode = (uint64_t)sbuf.st_ino; fdb->mtime = sbuf.st_mtime; fdb->tran = false; fdb->walfd = -1; fdb->walend = 0; if(fdb->omode & FDBOWRITER) tcfdbsetflag(fdb, FDBFOPEN, true); return true; } /* Close a fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */ static bool tcfdbcloseimpl(TCFDB *fdb){ assert(fdb); bool err = false; if(fdb->omode & FDBOWRITER) tcfdbsetflag(fdb, FDBFOPEN, false); if((fdb->omode & FDBOWRITER) && !tcfdbmemsync(fdb, false)) err = true; if(munmap(fdb->map, fdb->limsiz) == -1){ tcfdbsetecode(fdb, TCEMMAP, __FILE__, __LINE__, __func__); err = true; } if(fdb->tran){ if(!tcfdbwalrestore(fdb, fdb->path)) err = true; fdb->tran = false; } if(fdb->walfd >= 0){ if(close(fdb->walfd) == -1){ tcfdbsetecode(fdb, TCECLOSE, __FILE__, __LINE__, __func__); err = true; } if(!fdb->fatal && !tcfdbwalremove(fdb, fdb->path)) err = true; } if(close(fdb->fd) == -1){ tcfdbsetecode(fdb, TCECLOSE, __FILE__, __LINE__, __func__); err = true; } TCFREE(fdb->path); fdb->path = NULL; fdb->fd = -1; return !err; } /* Get the previous record of a record. `fdb' specifies the fixed-length database object. `id' specifies the ID number. The return value is the ID number of the previous record or 0 if no record corresponds. */ static int64_t tcfdbprevid(TCFDB *fdb, int64_t id){ assert(fdb && id >= 0); id--; while(id >= fdb->min){ TCDODEBUG(fdb->cnt_readrec++); unsigned char *rec = fdb->array + (id - 1) * (fdb->rsiz); unsigned char *rp = rec; uint32_t osiz; uint16_t snum; uint32_t lnum; switch(fdb->wsiz){ case 1: osiz = *(rp++); break; case 2: memcpy(&snum, rp, sizeof(snum)); osiz = TCITOHS(snum); rp += sizeof(snum); break; default: memcpy(&lnum, rp, sizeof(lnum)); osiz = TCITOHL(lnum); rp += sizeof(lnum); break; } if(osiz > 0 || *rp != 0) return id; id--; } return 0; } /* Get the next record of a record. `fdb' specifies the fixed-length database object. `id' specifies the ID number. The return value is the ID number of the next record or 0 if no record corresponds. */ static int64_t tcfdbnextid(TCFDB *fdb, int64_t id){ assert(fdb && id >= 0); id++; while(id <= fdb->max){ TCDODEBUG(fdb->cnt_readrec++); unsigned char *rec = fdb->array + (id - 1) * (fdb->rsiz); unsigned char *rp = rec; uint32_t osiz; uint16_t snum; uint32_t lnum; switch(fdb->wsiz){ case 1: osiz = *(rp++); break; case 2: memcpy(&snum, rp, sizeof(snum)); osiz = TCITOHS(snum); rp += sizeof(snum); break; default: memcpy(&lnum, rp, sizeof(lnum)); osiz = TCITOHL(lnum); rp += sizeof(lnum); break; } if(osiz > 0 || *rp != 0) return id; id++; } return 0; } /* Store a record. `fdb' specifies the fixed-length database object. `id' specifies the ID number. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. `dmode' specifies behavior when the key overlaps. If successful, the return value is true, else, it is false. */ static bool tcfdbputimpl(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz, int dmode){ assert(fdb && id > 0); if(vsiz > (int64_t)fdb->width) vsiz = fdb->width; TCDODEBUG(fdb->cnt_readrec++); unsigned char *rec = fdb->array + (id - 1) * (fdb->rsiz); uint64_t nsiz = FDBHEADSIZ + id * fdb->rsiz; if(nsiz > fdb->fsiz){ if(nsiz > fdb->limsiz){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } if(!FDBLOCKATTR(fdb)) return false; if(nsiz > fdb->fsiz){ if(vsiz < 0){ tcfdbsetecode(fdb, TCENOREC, __FILE__, __LINE__, __func__); FDBUNLOCKATTR(fdb); return false; } if(nsiz + fdb->rsiz * FDBTRUNCALW < fdb->limsiz) nsiz += fdb->rsiz * FDBTRUNCALW; if(ftruncate(fdb->fd, nsiz) == -1){ tcfdbsetecode(fdb, TCETRUNC, __FILE__, __LINE__, __func__); FDBUNLOCKATTR(fdb); return false; } TCDODEBUG(fdb->cnt_truncfile++); fdb->fsiz = nsiz; unsigned char *wp = rec; uint16_t snum; uint32_t lnum; switch(fdb->wsiz){ case 1: *(wp++) = vsiz; break; case 2: snum = TCHTOIS(vsiz); memcpy(wp, &snum, sizeof(snum)); wp += sizeof(snum); break; default: lnum = TCHTOIL(vsiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); break; } if(vsiz > 0){ memcpy(wp, vbuf, vsiz); } else { *wp = 1; } TCDODEBUG(fdb->cnt_writerec++); fdb->rnum++; if(fdb->min < 1 || id < fdb->min) fdb->min = id; if(fdb->max < 1 || id > fdb->max) fdb->max = id; FDBUNLOCKATTR(fdb); return true; } FDBUNLOCKATTR(fdb); } unsigned char *rp = rec; uint32_t osiz; uint16_t snum; uint32_t lnum; switch(fdb->wsiz){ case 1: osiz = *(rp++); break; case 2: memcpy(&snum, rp, sizeof(snum)); osiz = TCITOHS(snum); rp += sizeof(snum); break; default: memcpy(&lnum, rp, sizeof(lnum)); osiz = TCITOHL(lnum); rp += sizeof(lnum); break; } bool miss = osiz == 0 && *rp == 0; if(dmode != FDBPDOVER && !miss){ if(dmode == FDBPDKEEP){ tcfdbsetecode(fdb, TCEKEEP, __FILE__, __LINE__, __func__); return false; } if(dmode == FDBPDCAT){ if(fdb->tran && !tcfdbwalwrite(fdb, (char *)rec - fdb->map, fdb->width)) return false; vsiz = tclmin(vsiz, fdb->width - osiz); unsigned char *wp = rec; int usiz = osiz + vsiz; switch(fdb->wsiz){ case 1: *(wp++) = usiz; break; case 2: snum = TCHTOIS(usiz); memcpy(wp, &snum, sizeof(snum)); wp += sizeof(snum); break; default: lnum = TCHTOIL(usiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); break; } if(usiz > 0){ memcpy(wp + osiz, vbuf, vsiz); } else { *wp = 1; } TCDODEBUG(fdb->cnt_writerec++); return true; } if(dmode == FDBPDADDINT){ if(osiz != sizeof(int)){ tcfdbsetecode(fdb, TCEKEEP, __FILE__, __LINE__, __func__); return false; } int lnum; memcpy(&lnum, rp, sizeof(lnum)); if(*(int *)vbuf == 0){ *(int *)vbuf = lnum; return true; } if(fdb->tran && !tcfdbwalwrite(fdb, (char *)rec - fdb->map, fdb->width)) return false; lnum += *(int *)vbuf; *(int *)vbuf = lnum; memcpy(rp, &lnum, sizeof(lnum)); TCDODEBUG(fdb->cnt_writerec++); return true; } if(dmode == FDBPDADDDBL){ if(osiz != sizeof(double)){ tcfdbsetecode(fdb, TCEKEEP, __FILE__, __LINE__, __func__); return false; } double dnum; memcpy(&dnum, rp, sizeof(dnum)); if(*(double *)vbuf == 0.0){ *(double *)vbuf = dnum; return true; } if(fdb->tran && !tcfdbwalwrite(fdb, (char *)rec - fdb->map, fdb->width)) return false; dnum += *(double *)vbuf; *(double *)vbuf = dnum; memcpy(rp, &dnum, sizeof(dnum)); TCDODEBUG(fdb->cnt_writerec++); return true; } if(dmode == FDBPDPROC){ FDBPDPROCOP *procptr = *(FDBPDPROCOP **)((char *)vbuf - sizeof(procptr)); int nvsiz; char *nvbuf = procptr->proc(rp, osiz, &nvsiz, procptr->op); if(nvbuf == (void *)-1){ if(fdb->tran && !tcfdbwalwrite(fdb, (char *)rec - fdb->map, fdb->width)) return false; memset(rec, 0, fdb->wsiz + 1); TCDODEBUG(fdb->cnt_writerec++); if(!FDBLOCKATTR(fdb)) return false; fdb->rnum--; if(fdb->rnum < 1){ fdb->min = 0; fdb->max = 0; } else if(fdb->rnum < 2){ if(fdb->min == id){ fdb->min = fdb->max; } else if(fdb->max == id){ fdb->max = fdb->min; } } else { if(id == fdb->min) fdb->min = tcfdbnextid(fdb, id); if(id == fdb->max) fdb->max = tcfdbprevid(fdb, id); } FDBUNLOCKATTR(fdb); return true; } if(!nvbuf){ tcfdbsetecode(fdb, TCEKEEP, __FILE__, __LINE__, __func__); return false; } if(fdb->tran && !tcfdbwalwrite(fdb, (char *)rec - fdb->map, fdb->width)) return false; if(nvsiz > fdb->width) nvsiz = fdb->width; unsigned char *wp = rec; switch(fdb->wsiz){ case 1: *(wp++) = nvsiz; break; case 2: snum = TCHTOIS(nvsiz); memcpy(wp, &snum, sizeof(snum)); wp += sizeof(snum); break; default: lnum = TCHTOIL(nvsiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); break; } if(nvsiz > 0){ memcpy(wp, nvbuf, nvsiz); } else { *wp = 1; } TCFREE(nvbuf); TCDODEBUG(fdb->cnt_writerec++); return true; } } if(vsiz < 0){ tcfdbsetecode(fdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } if(fdb->tran && !tcfdbwalwrite(fdb, (char *)rec - fdb->map, fdb->width)) return false; unsigned char *wp = rec; switch(fdb->wsiz){ case 1: *(wp++) = vsiz; break; case 2: snum = TCHTOIS(vsiz); memcpy(wp, &snum, sizeof(snum)); wp += sizeof(snum); break; default: lnum = TCHTOIL(vsiz); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); break; } if(vsiz > 0){ memcpy(wp, vbuf, vsiz); } else { *wp = 1; } TCDODEBUG(fdb->cnt_writerec++); if(miss){ if(!FDBLOCKATTR(fdb)) return false; fdb->rnum++; if(fdb->min < 1 || id < fdb->min) fdb->min = id; if(fdb->max < 1 || id > fdb->max) fdb->max = id; FDBUNLOCKATTR(fdb); } return true; } /* Remove a record of a fixed-length database object. `fdb' specifies the fixed-length database object. `id' specifies the ID number. If successful, the return value is true, else, it is false. */ static bool tcfdboutimpl(TCFDB *fdb, int64_t id){ assert(fdb && id >= 0); TCDODEBUG(fdb->cnt_readrec++); unsigned char *rec = fdb->array + (id - 1) * (fdb->rsiz); uint64_t nsiz = FDBHEADSIZ + id * fdb->rsiz; if(nsiz > fdb->fsiz){ tcfdbsetecode(fdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } unsigned char *rp = rec; uint32_t osiz; uint16_t snum; uint32_t lnum; switch(fdb->wsiz){ case 1: osiz = *(rp++); break; case 2: memcpy(&snum, rp, sizeof(snum)); osiz = TCITOHS(snum); rp += sizeof(snum); break; default: memcpy(&lnum, rp, sizeof(lnum)); osiz = TCITOHL(lnum); rp += sizeof(lnum); break; } if(osiz == 0 && *rp == 0){ tcfdbsetecode(fdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } if(fdb->tran && !tcfdbwalwrite(fdb, (char *)rec - fdb->map, fdb->width)) return false; memset(rec, 0, fdb->wsiz + 1); TCDODEBUG(fdb->cnt_writerec++); if(!FDBLOCKATTR(fdb)) return false; fdb->rnum--; if(fdb->rnum < 1){ fdb->min = 0; fdb->max = 0; } else if(fdb->rnum < 2){ if(fdb->min == id){ fdb->min = fdb->max; } else if(fdb->max == id){ fdb->max = fdb->min; } } else { if(id == fdb->min) fdb->min = tcfdbnextid(fdb, id); if(id == fdb->max) fdb->max = tcfdbprevid(fdb, id); } FDBUNLOCKATTR(fdb); return true; } /* Retrieve a record. `fdb' specifies the fixed-length database object. `id' specifies the ID number. `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. */ static const void *tcfdbgetimpl(TCFDB *fdb, int64_t id, int *sp){ assert(fdb && id >= 0 && sp); TCDODEBUG(fdb->cnt_readrec++); unsigned char *rec = fdb->array + (id - 1) * (fdb->rsiz); uint64_t nsiz = FDBHEADSIZ + id * fdb->rsiz; if(nsiz > fdb->fsiz){ tcfdbsetecode(fdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } unsigned char *rp = rec; uint32_t osiz; uint16_t snum; uint32_t lnum; switch(fdb->wsiz){ case 1: osiz = *(rp++); break; case 2: memcpy(&snum, rp, sizeof(snum)); osiz = TCITOHS(snum); rp += sizeof(snum); break; default: memcpy(&lnum, rp, sizeof(lnum)); osiz = TCITOHL(lnum); rp += sizeof(lnum); break; } if(osiz == 0 && *rp == 0){ tcfdbsetecode(fdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } *sp = osiz; return rp; } /* Initialize the iterator of a fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */ static bool tcfdbiterinitimpl(TCFDB *fdb){ assert(fdb); fdb->iter = fdb->min; return true; } /* Get the next key of the iterator of a fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is the next ID number of the iterator, else, it is 0. */ static uint64_t tcfdbiternextimpl(TCFDB *fdb){ assert(fdb); if(fdb->iter < 1){ tcfdbsetecode(fdb, TCENOREC, __FILE__, __LINE__, __func__); return 0; } uint64_t cur = fdb->iter; fdb->iter = tcfdbnextid(fdb, fdb->iter); return cur; } /* Get range matching ID numbers in a fixed-length database object. `fdb' specifies the fixed-length database object. `lower' specifies the lower limit of the range. `upper' specifies the upper limit of the range. `max' specifies the maximum number of keys to be fetched. `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. */ static uint64_t *tcfdbrangeimpl(TCFDB *fdb, int64_t lower, int64_t upper, int max, int *np){ assert(fdb && lower > 0 && upper > 0 && np); if(lower < fdb->min) lower = fdb->min; if(upper > fdb->max) upper = fdb->max; if(max < 0) max = INT_MAX; int anum = FDBIDARYUNIT; uint64_t *ids; TCMALLOC(ids, anum * sizeof(*ids)); int num = 0; for(int64_t i = lower; i <= upper && num < max; i++){ int vsiz; const void *vbuf = tcfdbgetimpl(fdb, i, &vsiz); if(vbuf){ if(num >= anum){ anum *= 2; TCREALLOC(ids, ids, anum * sizeof(*ids)); } ids[num++] = i; } } *np = num; return ids; } /* Optimize the file of a fixed-length database object. `fdb' specifies the fixed-length database object. `width' specifies the width of the value of each record. `limsiz' specifies the limit size of the database file. If successful, the return value is true, else, it is false. */ static bool tcfdboptimizeimpl(TCFDB *fdb, int32_t width, int64_t limsiz){ assert(fdb); char *tpath = tcsprintf("%s%ctmp%c%llu", fdb->path, MYEXTCHR, MYEXTCHR, fdb->inode); TCFDB *tfdb = tcfdbnew(); tfdb->dbgfd = fdb->dbgfd; if(width < 1) width = fdb->width; if(limsiz < 1) limsiz = fdb->limsiz; tcfdbtune(tfdb, width, limsiz); if(!tcfdbopen(tfdb, tpath, FDBOWRITER | FDBOCREAT | FDBOTRUNC)){ tcfdbsetecode(fdb, tfdb->ecode, __FILE__, __LINE__, __func__); tcfdbdel(tfdb); TCFREE(tpath); return false; } bool err = false; int64_t max = fdb->max; for(int i = fdb->min; !err && i <= max; i++){ int vsiz; const void *vbuf = tcfdbgetimpl(fdb, i, &vsiz); if(vbuf && !tcfdbput(tfdb, i, vbuf, vsiz)){ tcfdbsetecode(fdb, tfdb->ecode, __FILE__, __LINE__, __func__); err = true; } } if(!tcfdbclose(tfdb)){ tcfdbsetecode(fdb, tfdb->ecode, __FILE__, __LINE__, __func__); err = true; } tcfdbdel(tfdb); if(unlink(fdb->path) == -1){ tcfdbsetecode(fdb, TCEUNLINK, __FILE__, __LINE__, __func__); err = true; } if(rename(tpath, fdb->path) == -1){ tcfdbsetecode(fdb, TCERENAME, __FILE__, __LINE__, __func__); err = true; } TCFREE(tpath); if(err) return false; tpath = tcstrdup(fdb->path); int omode = (fdb->omode & ~FDBOCREAT) & ~FDBOTRUNC; if(!tcfdbcloseimpl(fdb)){ TCFREE(tpath); return false; } bool rv = tcfdbopenimpl(fdb, tpath, omode); TCFREE(tpath); return rv; } /* Remove all records of a fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */ static bool tcfdbvanishimpl(TCFDB *fdb){ assert(fdb); char *path = tcstrdup(fdb->path); int omode = fdb->omode; bool err = false; if(!tcfdbcloseimpl(fdb)) err = true; if(!tcfdbopenimpl(fdb, path, FDBOTRUNC | omode)){ tcpathunlock(fdb->rpath); TCFREE(fdb->rpath); err = true; } TCFREE(path); return !err; } /* Copy the database file of a fixed-length database object. `fdb' specifies the fixed-length database object. `path' specifies the path of the destination file. If successful, the return value is true, else, it is false. */ static bool tcfdbcopyimpl(TCFDB *fdb, const char *path){ assert(fdb && path); bool err = false; if(fdb->omode & FDBOWRITER){ if(!tcfdbmemsync(fdb, false)) err = true; tcfdbsetflag(fdb, FDBFOPEN, false); } if(*path == '@'){ char tsbuf[TCNUMBUFSIZ]; sprintf(tsbuf, "%llu", (unsigned long long)(tctime() * 1000000)); const char *args[3]; args[0] = path + 1; args[1] = fdb->path; args[2] = tsbuf; if(tcsystem(args, sizeof(args) / sizeof(*args)) != 0) err = true; } else { if(!tccopyfile(fdb->path, path)){ tcfdbsetecode(fdb, TCEMISC, __FILE__, __LINE__, __func__); err = true; } } if(fdb->omode & FDBOWRITER) tcfdbsetflag(fdb, FDBFOPEN, true); return !err; } /* Move the iterator to the record corresponding a key of a fixed-length database object. `fdb' specifies the fixed-length database object. `id' specifies the ID number. If successful, the return value is true, else, it is false. */ static bool tcfdbiterjumpimpl(TCFDB *fdb, int64_t id){ assert(fdb && id >= 0); if(id <= fdb->min){ fdb->iter = fdb->min; } else { int vsiz; if(tcfdbgetimpl(fdb, id, &vsiz)){ fdb->iter = id; } else { uint64_t iter = tcfdbnextid(fdb, id); if(iter > 0){ fdb->iter = iter; } else { return false; } } } return true; } /* Process each record atomically of a fixed-length database object. `fdb' specifies the fixed-length database object. `iter' specifies the pointer to the iterator function called for each record. `op' specifies an arbitrary pointer to be given as a parameter of the iterator function. If successful, the return value is true, else, it is false. */ static bool tcfdbforeachimpl(TCFDB *fdb, TCITER iter, void *op){ bool err = false; uint64_t id = fdb->min; while(id > 0){ int vsiz; const void *vbuf = tcfdbgetimpl(fdb, id, &vsiz); if(vbuf){ char kbuf[TCNUMBUFSIZ]; int ksiz = sprintf(kbuf, "%llu", (unsigned long long)id); if(!iter(kbuf, ksiz, vbuf, vsiz, op)) break; } else { tcfdbsetecode(fdb, TCEMISC, __FILE__, __LINE__, __func__); err = true; } id = tcfdbnextid(fdb, id); } return !err; } /* Lock a method of the fixed-length database object. `fdb' specifies the fixed-length database object. `wr' specifies whether the lock is writer or not. If successful, the return value is true, else, it is false. */ static bool tcfdblockmethod(TCFDB *fdb, bool wr){ assert(fdb); if(wr ? pthread_rwlock_wrlock(fdb->mmtx) != 0 : pthread_rwlock_rdlock(fdb->mmtx) != 0){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Unlock a method of the fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */ static bool tcfdbunlockmethod(TCFDB *fdb){ assert(fdb); if(pthread_rwlock_unlock(fdb->mmtx) != 0){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Lock the attributes of the fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */ static bool tcfdblockattr(TCFDB *fdb){ assert(fdb); if(pthread_mutex_lock(fdb->amtx) != 0){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Unlock the attributes of the fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */ static bool tcfdbunlockattr(TCFDB *fdb){ assert(fdb); if(pthread_mutex_unlock(fdb->amtx) != 0){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Lock a record of the fixed-length database object. `fdb' specifies the fixed-length database object. `wr' specifies whether the lock is writer or not. If successful, the return value is true, else, it is false. */ static bool tcfdblockrecord(TCFDB *fdb, bool wr, uint64_t id){ assert(fdb && id > 0); if(wr ? pthread_rwlock_wrlock((pthread_rwlock_t *)fdb->rmtxs + id % FDBRMTXNUM) != 0 : pthread_rwlock_rdlock((pthread_rwlock_t *)fdb->rmtxs + id % FDBRMTXNUM) != 0){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Unlock a record of the fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */ static bool tcfdbunlockrecord(TCFDB *fdb, uint64_t id){ assert(fdb); if(pthread_rwlock_unlock((pthread_rwlock_t *)fdb->rmtxs + id % FDBRMTXNUM) != 0){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Lock all records of the fixed-length database object. `fdb' specifies the fixed-length database object. `wr' specifies whether the lock is writer or not. If successful, the return value is true, else, it is false. */ static bool tcfdblockallrecords(TCFDB *fdb, bool wr){ assert(fdb); for(int i = 0; i < FDBRMTXNUM; i++){ if(wr ? pthread_rwlock_wrlock((pthread_rwlock_t *)fdb->rmtxs + i) != 0 : pthread_rwlock_rdlock((pthread_rwlock_t *)fdb->rmtxs + i) != 0){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); while(--i >= 0){ pthread_rwlock_unlock((pthread_rwlock_t *)fdb->rmtxs + i); } return false; } } TCTESTYIELD(); return true; } /* Unlock all records of the fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */ static bool tcfdbunlockallrecords(TCFDB *fdb){ assert(fdb); bool err = false; for(int i = FDBRMTXNUM - 1; i >= 0; i--){ if(pthread_rwlock_unlock((pthread_rwlock_t *)fdb->rmtxs + i)) err = true; } TCTESTYIELD(); if(err){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } return true; } /* Lock the write ahead logging file of the fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */ static bool tcfdblockwal(TCFDB *fdb){ assert(fdb); if(pthread_mutex_lock(fdb->wmtx) != 0){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Unlock the write ahead logging file of the fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */ static bool tcfdbunlockwal(TCFDB *fdb){ assert(fdb); if(pthread_mutex_unlock(fdb->wmtx) != 0){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /************************************************************************************************* * debugging functions *************************************************************************************************/ /* Print meta data of the header into the debugging output. `fdb' specifies the fixed-length database object. */ void tcfdbprintmeta(TCFDB *fdb){ assert(fdb); if(fdb->dbgfd < 0) return; int dbgfd = (fdb->dbgfd == UINT16_MAX) ? 1 : fdb->dbgfd; char buf[FDBIOBUFSIZ]; char *wp = buf; wp += sprintf(wp, "META:"); wp += sprintf(wp, " mmtx=%p", (void *)fdb->mmtx); wp += sprintf(wp, " amtx=%p", (void *)fdb->amtx); wp += sprintf(wp, " rmtxs=%p", (void *)fdb->rmtxs); wp += sprintf(wp, " tmtx=%p", (void *)fdb->tmtx); wp += sprintf(wp, " wmtx=%p", (void *)fdb->wmtx); wp += sprintf(wp, " eckey=%p", (void *)fdb->eckey); wp += sprintf(wp, " rpath=%s", fdb->rpath ? fdb->rpath : "-"); wp += sprintf(wp, " type=%02X", fdb->type); wp += sprintf(wp, " flags=%02X", fdb->flags); wp += sprintf(wp, " width=%u", fdb->width); wp += sprintf(wp, " limsiz=%llu", (unsigned long long)fdb->limsiz); wp += sprintf(wp, " wsiz=%u", fdb->wsiz); wp += sprintf(wp, " rsiz=%u", fdb->rsiz); wp += sprintf(wp, " limid=%llu", (unsigned long long)fdb->limid); wp += sprintf(wp, " path=%s", fdb->path ? fdb->path : "-"); wp += sprintf(wp, " fd=%d", fdb->fd); wp += sprintf(wp, " omode=%u", fdb->omode); wp += sprintf(wp, " rnum=%llu", (unsigned long long)fdb->rnum); wp += sprintf(wp, " fsiz=%llu", (unsigned long long)fdb->fsiz); wp += sprintf(wp, " min=%llu", (unsigned long long)fdb->min); wp += sprintf(wp, " max=%llu", (unsigned long long)fdb->max); wp += sprintf(wp, " iter=%llu", (unsigned long long)fdb->iter); wp += sprintf(wp, " map=%p", (void *)fdb->map); wp += sprintf(wp, " array=%p", (void *)fdb->array); wp += sprintf(wp, " ecode=%d", fdb->ecode); wp += sprintf(wp, " fatal=%u", fdb->fatal); wp += sprintf(wp, " inode=%llu", (unsigned long long)fdb->inode); wp += sprintf(wp, " mtime=%llu", (unsigned long long)fdb->mtime); wp += sprintf(wp, " tran=%d", fdb->tran); wp += sprintf(wp, " walfd=%d", fdb->walfd); wp += sprintf(wp, " walend=%llu", (unsigned long long)fdb->walend); wp += sprintf(wp, " dbgfd=%d", fdb->dbgfd); wp += sprintf(wp, " cnt_writerec=%lld", (long long)fdb->cnt_writerec); wp += sprintf(wp, " cnt_readrec=%lld", (long long)fdb->cnt_readrec); wp += sprintf(wp, " cnt_truncfile=%lld", (long long)fdb->cnt_truncfile); *(wp++) = '\n'; tcwrite(dbgfd, buf, wp - buf); } // END OF FILE tokyocabinet-1.4.48/ChangeLog0000644000175000017500000010116212013574344015063 0ustar mikiomikio2012-08-10 FAL Labs - tcadb.cc (tcadbmisc): compilation warnings ware supressed. - Release: 1.4.48 2010-08-15 FAL Labs * tchdb.cc (tchdbfbpmerge): size checking was added. * tcbdb.cc (tcbdbrangeimpl): a bug of dead locking was fixed. - Release: 1.4.47 2010-08-01 FAL Labs - site documents were modified. - Release: 1.4.46 2010-04-27 FAL Labs * tcbdb.c (tcbdboutlist): a bug related to reorganizing tree was fixed. - Release: 1.4.45 2010-04-23 FAL Labs * tcbdb.c (tcbdbrangefwm): a bug related to empty list was fixed. * tcadb.c (tcadbmulopen): a bug related to handling table indices was fixed. - Release: 1.4.44 2010-01-26 FAL Labs * tcutil.c (tcmapout): a useless condition was fixed. * tchdb.h, tcbdb.h: some members have volatile flag now. - Release: 1.4.43 2010-01-01 FAL Labs * tchdb.c (tchdbnew, tchdbsetmutex): a useless lock object was removed. * tcutil.c (tcsysinfo): CPU information was added. * tchmgr.c (runput, procput): addint and adddouble ouput the result now. * tcbmgr.c (runput, procput): addint and adddouble ouput the result now. * tcfmgr.c (runput, procput): addint and adddouble ouput the result now. - Release: 1.4.42 2009-12-07 FAL Labs * tctdb.c (tctdbidxputtoken, tctdbidxputqgram): domain overflow checking was added. - Release: 1.4.41 2009-11-24 FAL Labs * tchdb.c (tchdbputimpl): efficiency of using the free block pool was improved. * tchdb.c (tchdboptimizeimpl): the behaviour in no lock mode was modified. - Release: 1.4.40 2009-11-11 FAL Labs * tcutil.h (TCALIGNOF, tcgeneric_t): new macros. * tcutil.h (TCALIGNPAD): the alignment is now calculated by the generic union. * myconf.h (_alignof, _issigned, _maxof): new macros. - Release: 1.4.39 2009-11-09 FAL Labs * tcutil.c (tcsysinfo): FreeBSD and Mac OS X are now supported. * tcutil.c (tcmpoolclear): new function. * tchdb.c (tchdbputproc): a potential bug of memory aligment violation was fixed. * tcbdb.c (tcbdbputproc): a potential bug of memory aligment violation was fixed. * tcfdb.c (tcfdbputproc): a potential bug of memory aligment violation was fixed. - Release: 1.4.38 2009-10-27 FAL Labs * tctdb.c (tctdbcacheclear, tctdbcacheclearimpl): new functions. * tcadb.c (tcadbmisc): "getpart", "cacheclear", and "regex" functions were added. - Release: 1.4.37 2009-10-26 FAL Labs * tcutil.c (tcwwwformdecode2): a bug related to multiple cookie handling was fixed. * tcadb.c (tcadbmisc): the "error" functions were added. - Release: 1.4.36 2009-10-06 FAL Labs * tchdb.c (tchdbseekwrite): a bug related to emulation of the UBC was fixed. * tchdb.c (tchdbvanishimpl): potential memory leak was removed. * tcfdb.c (tcfdbvanishimpl): potential memory leak was removed. * tcadb.c (tcadbsetskelmulti, tcadbmulnew, tcadbmuldel): new functions. * tcadb.c (tcadbmisc): "range" sub function for B+ tree was added. * tcamgr.c (setskeltran): new function. * tcamttest.c: new file. - Release: 1.4.35 2009-09-11 FAL Labs * myconf.h: a macro for environment without "nanl" was added. * tctdbmetasearch (tctdbmetasearch): a bug related to the limit parameters was fixed. - Release: 1.4.34 2009-08-31 FAL Labs * tcutil.h, tchdb.h, tcbdb.h, tcfdb.h, tctdb.h, tcadb.h: including stdbool.h was wrapped. * tcutil.c (tccmpdecimal): accuracy was improved and now real number is supported. * tctdb.c (tctdbqrycondmatch): accuracy of decimal operators was improved. - Release: 1.4.33 2009-08-14 FAL Labs * tcutil.c (tctmpldumpeval): "INC", "PRT" operators and "SET" directive were added. * tcutil.c (tcwwwformdecode2): a bug of memory corruption was fixed. * tchdb.c (tchdbgetimpl): a bug related to caching large records was fixed. - Release: 1.4.32 2009-07-23 FAL Labs * tcutil.c (tcmpoolpop, tcstatfile): new functions. * tcutil.c (tcwwwformdecode2, tcarccipher): new functions. * tcutil.c (tctmpldumpeval, tctmpldumpevalvar): type checking was improved. - Release: 1.4.31 2009-07-13 FAL Labs * tctdb.c (tctdbstrtometasearcytype): new function. * tcadb.c (tcadbmisc): "metasearch" command was added. - Release: 1.4.30 2009-07-03 FAL Labs * tctdb.c (tctdbidxgetbyftsunion): efficiency counting sort was improved. - Release: 1.4.29 2009-06-22 FAL Labs * tcutil.c (tcstrutfnorm, tcstrkwic, tcstrtokenize): new functions. * tcutil.c (tcstrutfnorm, tcstrucsnorm): "TCUNWIDTH" option was added. * tctdb.c (tctdbsetinvcache, tctdbidxsyncicc, tctdbidxcmpkey): new functions. * tctdb.c (tctdbmetasearch, tctdbget4, tctdbqrykwic): new functions. * tctdb.c (tctdbsetindeximpl): the xmsiz parameter is now configured. - Release: 1.4.28 2009-06-16 FAL Labs * tcutil.c (tcstrskipspc, tcstrucsnorm): new functions. * tctdb.c (tctdbqryaddcond): full-text search operators were added. * tctdb.c (tctdbsetindex): q-gram inverted index was added. * tctdb.c (tctdbidxputqgram, tctdbidxoutqgram, tctdbidxgetbyfts): new functions. - Release: 1.4.27 2009-06-13 FAL Labs * tctdb.c (tctdbsetindex, tctdbsearchimpl): token inverted index was added. * tctdb.c (tctdbidxputone, tctdbidxoutone): new functions. * tctdb.c (tctdbidxputtoken, tctdbidxouttoken, tctdbidxgetbytokens): new functions. * tcadb.c (tcadbput, tcadbputkeep, tcadbputcat): capnum behaviour was modified. - Release: 1.4.26 2009-06-09 FAL Labs * tcutil.c (tccstrescape, tccstrunescape, tcjsonescape, tcjsonunescape): new functions. * tcutil.c (tcpathlock, tcpathunlock): new functions. * tchdb.c (tchdbopen): double opening detection was added. * tchdb.c (tchdbtranbegin): a bug of invalid flag setting was fixed. * tcfdb.c (tcfdbopen): double opening detection was added. * tcfdb.c (tcfdbtranbegin): a bug of invalid flag setting was fixed. * tctdb.c (tctdbiternext3): new function. - Release: 1.4.25 2009-06-04 FAL Labs * tcutil.c (tcwwwformencode, tcwwwformdecode): new functions. * tcutil.c (tctmplnew, tctmpldel, tctmplload, tctmpldump): new functions. * tcutil.c (tcmapget4, tctreeget4): new functions. * tcutil.c (tclistprintf, tcmapprintf, tctreeprintf): new functions. * tcucodec.c (runtmpl, proctmpl): new functions. - Release: 1.4.24 2009-05-24 FAL Labs * tcutil.c (tcmapiterinit2, tcmdbiterinit2): new functions. * tchdb.c (tchdbiterinit2, tchdbiterjumpimpl): new functions. * tcfdb.c (tcfdbiterinit2, tcfdbiterjumpimpl): new functions. * tctdb.c (tctdbiterinit2): new function. - Release: 1.4.23 2009-05-21 FAL Labs * tcadb.c (tcadbmisc): "sync", "optimize", "vanish" commands were added. - Release: 1.4.22 2009-05-10 FAL Labs * tchdb.c (tchdbsetdfunit, tchdbdfunit, tchdbdefrag): new functions. * tchdb.c (tchdbdefragimpl, tchdbfbptrim, tchdbshiftrec): new functions. * tcbdb.c (tcbdbsetdfunit, tcbdbdfunit, tcbdbdefrag): new functions. * tctdb.c (tctdbsetdfunit, tctdbdfunit, tctdbdefrag): new functions. * tcadb.c (tcadbopen): "dfunit" parameter was added. * tcadb.c (tcadbmisc): "defrag" command was added. - Release: 1.4.21 2009-05-08 FAL Labs * tcutil.h: signedness of some members of TCMAP and TCTREE were changed. * tcutil.c (tchexdecode): a bug related to handling space characters was fixed. - Release: 1.4.20 2009-04-30 FAL Labs * tctdb.c (tctdbidxhash, tctdbqryproc2, tctdbqrysearchout2): new functions. * tctdb.c (tctdbsearchimpl, tctdbidxput, tctdbidxout): the index format was modified. * tcadb.c (tcadbmisc): "out" option of "search" command became non-atomic. - Release: 1.4.19 2009-04-25 FAL Labs * tcutil.c (tcatoih): new function. * tcbdb.c (tcbdbputimpl): cache adjustment on the putkeep mode was added. * tcadb.c (tcadbsetskel): new function. - Release: 1.4.18 2009-04-23 FAL Labs * tcutil.c (tcmapput, tcmapout, tcmapget): memory usage was reduced. * tcutil.c (tcsysinfo): new function. * tcutil.c (tcatoix): "strtold" was replaced by own implementation. * tctdb.c (tctdbqryidxfetch): new function. - Release: 1.4.17 2009-04-20 FAL Labs * tcbdb.c (tcbdbcuroutimpl): shift mechanism of cursors on deleted leaves was added. * tcbdb.c (tcbdbleafcheck): new function. - Release: 1.4.16 2009-04-08 FAL Labs * tcutil.c (tcsleep): new function. * tchdb.c (tchdbtranbegin): locking algorithm was modified. * tchdb.c (tchdblocktran, tchdbunlocktran): abolished. * tcbdb.c (tcbdbtranbegin): locking algorithm was modified. * tcbdb.c (tcbdbcurjumpimpl): a bug related to cursor initialization was fixed. * tcfdb.c (tcbdbtranbegin): locking algorithm was modified. * tcfdb.c (tcfdblocktran, tcfdbunlocktran): abolished. * tctdb.c (tctdbqryallcondmatch): a bug related to handling null value was fixed. * tcadb.c (tcadboptimize, tcadbpath): new functions. - Release: 1.4.15 2009-04-07 FAL Labs * tcbdb.c (tcbdbputimpl, tcbdbcurputimpl): page size limitation was added. * tcbdb.c (tcbdbleafdatasize): removed. * tctdb.c (tctdbsetindeximpl): inner indexes were tuned. - Release: 1.4.14 2009-04-02 FAL Labs * configure.in: a bug related to support for traditional shells was fixed. * tcfdb.c (tcfdbtranbegin, tcfdbtrancommit, tcfdbtranabort): new functions. * tcadb.c (tcadbtranbegin, tcadbtrancommit, tcadbtranabort): new functions. - Release: 1.4.13 2009-03-21 FAL Labs * tctdb.c (tctdbqrycount): new function. * tcadb.c (tcadbmisc): "count" option was added. - Release: 1.4.12 2009-03-11 FAL Labs * tcutil.c (tctopsort): new function. * tchdb.c (tchdbfbpinsert, tchdbfbpsearch, tchdbfbpsplice): performance was improved. * tchdb.c (tchdbwriterec): concurrency was improved. * tctdb.c (tctdbqrysearchimpl): a bug related to the skip parameter was fixed. * tctdb.c (tctdbputimpl, tctdbidxout, tctdbqrysearchimpl): performance was improved. - Release: 1.4.11 2009-03-02 FAL Labs * tcutil.c (tcmdbputproc, tcndbputfunc): removing mechanism was added. * tchdb.c (tchdbwalrestore): a bug of memory corruption was fixed. * tchdb.c (tchdbremoverec): new function. * tchdb.c (tchdbputproc): removing mechanism was added. * tcbdb.c (tchdbputproc): removing mechanism was added. * tcfdb.c (tcfdbputproc): removing mechanism was added. * tctdb.c (tctdbsetlimit): new function instead of "tctdbqrysetmax". * tcadb.c (tcadbmisc): "setlimit" parameter was added. - Release: 1.4.10 2009-02-18 FAL Labs * tcbdb.c (tcbdbnodesubidx): a bug related to tree reconstruction was fixed. * tcbdb.c (tcbdboptimizeimpl): memory usage was reduced. - Release: 1.4.9 2009-02-18 FAL Labs * tcutil.c (tclrand): bias of random numbers was lightened. * tchdb.c (tchdbsetecode): a trick to print fatal errors only was added. * tcbdb.c (tcbdbputimpl): a bug of mixing an useless entry on division was fixed. * tcbdb.c (tcbdbnodesubidx): regions of useless nodes are now removed recursively. * tcadb.c (tcadbopen): aliases of the database suffixes were added. - Release: 1.4.8 2009-02-15 FAL Labs * tctdb.c (tctdbsetindex): "TDBITOPT" option was added. - Release: 1.4.7 2009-02-13 FAL Labs * tctdb.c (tctdbgenuidimpl): the meta data format was normalized to the big endian. * tctdb.c (tctdbsetuidseed): new function. * tcadb.c (tcadbmisc): "get" function of the table database was enhanced. - Release: 1.4.6 2009-02-02 FAL Labs * tchdb.c (tchdbput, tchdbget, tchdbout): bugs related to race condition were fixed. * tchdb.c (tchdbputimpl): bugs related to race condition were fixed. * tchmttest.c (runrace, procrace): new functions. * tcbmttest.c (runrace, procrace): new functions. - Release: 1.4.5 2009-01-29 FAL Labs * tcutil.c (tcmapputproc, tctreeputproc, tcmdbputproc, tcndbputproc): new functions. * tchdb.c (tchdbputproc): new function. * tchdb.c (TDBTHREADYIELD): "sched_yield" is now used instead of "pthread_yield". * tcbdb.c (tcbdbputproc): new function. * tcbdb.c (tcbdbcurkey, tcbdbcurval): the type of the return value was modified. * tcfdb.c (tcfdbputproc): new function. * tcadb.c (tcadbputproc): new function. * tcadb.c (tcadbmisc): sub funcitons "put", "out", and "get" were added. * tcadb.c (tcstrisnum): new function. - Release: 1.4.4 2009-01-26 FAL Labs * tctdb.c (tctdbqryprocout): renamed as "tctdbqrysearchout". * tcadb.c (tcadbmisc): sub functions "searchget" and "searchget" were integrated. - Release: 1.4.3 2009-01-21 FAL Labs * tcutil.c (tcatof): new function. * tcbdb.c (tcbdbleafaddrec): allocation tuning of duplicated records was modified. * tcadb.c: all methods now support the table database API. - Release: 1.4.2 2009-01-19 FAL Labs * tcutil.c (tctdbsearchimpl): optimized with macros. * tcbdb.c (tcbdbcurjumpimpl): a bug related to backword positioning was fixed. * tctdb.c (tctdbsearchimpl): a bug related to numeric range search was fixed. * tctdb.c (tctdbsetcache, tctdbforeach, tctdbqryproc): new functions. * tctdb.c (tctdbqryonecondmatch): new function. - Release: 1.4.1 2009-01-04 FAL Labs * tcutil.c (tccmpdecimal): equal numbers are now distinct by lexical order. * tcutil.c (tclistnew3, tcmapnew3): new functions. * tcutil.c (tcatoix, tclistinvert, tclog2l, tclog2d): new functions. * tcutil.c (tcstrsplit2, tcstrsplit3, tcstrsplit4): new functions. * tcutil.c (tcstrjoin2, tcstrjoin3, tcstrjoin4): new functions. * tchdb.c (tchdbputimpl): a bug of memory corruption was fixed. * tchdb.c (tchdbgetnext3): new function. * tcbdb.c (tcbdbleafaddrec): a bug of memory corruption was fixed. * tcbdb.c (tcbdboptimizeimpl): a bug related to parameter accession was fixed. * tctdb.h, tctdb.c, tcttest.c, tctmttest.c, tctmgr.c: new files. - Release: 1.4.0 2008-12-27 FAL Labs * tcadb.c (tcadbmisc): the return value of "getlist" function was modified. - Release: 1.3.27 2008-12-17 FAL Labs * tchdb.c (tchdbforeach): global locking was replaced by record locking. * tcbdb.c (tcbdbforeachimpl): cache adjustment was added. * tcadb.c (tcadbmisc): new function. - Release: 1.3.26 2008-12-16 FAL Labs * tcbdb.c (tcbdbsearchleaf): performance was improved. - Release: 1.3.25 2008-12-08 FAL Labs * tcutil.c (tcmdbforeach, tcmdbforeachimpl): new functions. * tcutil.c (tcndbforeach, tcndbforeachimpl): new functions. * tcutil.c (tctreenew2, tcndbnew2): type of the comparison function was modified. * tcutil.c (tcstrdist, tcstrdistutf): maximum memory usage was limited. * tchdb.c (tchdbforeach, tchdbforeachimpl): new functions. * tchdb.c (tchdboptimizeimpl): performance was improved. * tcbdb.c (tcbdbforeach, tcbdbforeachimpl): new functions. * tcbdb.c (tcbdbsetcmpfunc): BDBCMP was changed to TCCMP. * tcbdb.c (tcbdbcmplexical, tcbdbcmpdecimal, tcbdbcmpint32, tcbdbcmpint64): removed. * tcfdb.c (tcfdbforeach, tcfdbforeachimpl): new functions. * tcadb.c (tcadbomode, tcadbreveal): new functions. - Release: 1.3.24 2008-12-03 FAL Labs * tcbdb.c (tcbdbtranbegin): conbination of non-transaction and transaction became safer. - Release: 1.3.23 2008-11-28 FAL Labs * tcutil.c (tcmapput3, tcmdbput3, tcmapputcat3, tcmdbputcat3): new functions. * tcutil.c (tctreeput3, tcndbput3): new functions. * tcadb.c (tcadbput, tcadbputcat): LRU rule is now strictly applied. - Release: 1.3.22 2008-11-22 FAL Labs * tchdb.c (tchdbwalrestore): a bug on systems without unified buffer cache was fixed. - Release: 1.3.21 2008-11-21 FAL Labs * tchdb.c (tchdbtranbegin, tchdbtrancommit, tchdbtranabort): new functions. - Release: 1.3.20 2008-11-19 FAL Labs * tchdb.c (tchdbaddint, tchdbadddouble): bugs related to the return value were fixed. - Release: 1.3.19 2008-11-10 FAL Labs * tcutil.c (tcptrlistnew, tcptrlistdel): new functions. * tcbdb.c (tcbdbputimpl, tcbdboutimpl, tcbdbgetimpl): new functions. - Release: 1.3.18 2008-11-07 FAL Labs * tcutil.c (tcmdbnew, tcmdbdel, tcmdbopen, tcmdbclose): new functions. * tcadb.c (tcadbnew, tcadbdel): on-memory tree database is now supported. - Release: 1.3.17 2008-10-30 FAL Labs * tcutil.c (tcmapdup): performance was improved. * tcutil.c (tctreenew, tctreedel, tctreeopen, tctreeclose): new functions. * tcutil.c (tcsystem): new function. * tchdb.c (tchdbopenimpl): memory usage of reader declined. * tcadb.c (tcadbsync, tcadbcopy): on-memory database is now supported. - Release: 1.3.16 2008-10-20 FAL Labs * tcutil.c (tcmapput, tcmapout, tcmapget): performance was improved. * tcutil.c (tcnumtobinstr): new function. * tchdb.c (tchdbbidx): performance was improved. - Release: 1.3.15 2008-10-05 FAL Labs * myconf.h: missing features of PATH_MAX and nan is now emulated. * tcutil.c (tczeromap, tczerounmap): new functions. - Release: 1.3.14 2008-10-19 FAL Labs * tcutil.c (tcmdbadddouble): NAN were replaced by the nan function. * Makefile.in: the compilation command now supports Solaris. - Release: 1.3.13 2008-10-05 FAL Labs * tcutil.c (tchexencode, tchexdecode): new functions. * tchdb.c (tchdbaddint): a bug of error code setting was fixed. - Release: 1.3.12 2008-09-23 FAL Labs * tchdb.c (tchdbputimpl): performance of the ADDINT mode was improved. * tcbdb.c (tcbdbleafaddrec): performance of the ADDINT mode was improved. * tcfdb.c (tcfdbputimpl): performance of the ADDINT mode was improved. * tcutil.c (tcpagealign): new function. * tchdb.c (tchdbsetxmsiz): the parameter is now rounded up to multiple of the page size. * tcfdb.c (tcfdbtune): the parameter is now rounded up to multiple of the page size. * tchdb.c (tcseekwrite, tcseekread, tcseekreadtry): page border handling was added. - Release: 1.3.11 2008-09-20 FAL Labs * tcutil.c (tcunlock): new function. * tchdb.c (tchdbsetmutex): useless locking was eliminated. * tcbdb.c (tcbdbsetmutex): useless locking was eliminated. * tcfdb.c (tcfdbsetmutex): useless locking was eliminated. - Release: 1.3.10 2008-09-10 FAL Labs * tchdb.c (tchdblockallrecords, tchdbunlockallrecords): new functions. * tcfdb.c (tcfdblockallrecords, tcfdbunlockallrecords): new functions. - Release: 1.3.9 2008-09-05 FAL Labs * tcutil.c (tcmapaddint, tcmapadddouble): checking data size was added. - Release: 1.3.8 2008-09-03 FAL Labs * tcutil.c (tcmapaddint, tcmapadddouble): type of the return value was changed. * tcfdb.c (tcfdbkeytoid): added as API. * tcadb.c (tcadbaddint, tcadbadddouble): new functions. - Release: 1.3.7 2008-09-03 FAL Labs * tcutil.c (tclrand, tcdrand): a bug of overflow on 64-bit environment was fixed. * tcutil.c (tcmdbaddint, tcmdbadddouble): new functions. * tchdb.c (tchdbaddint, tchdbadddouble): new functions. * tcbdb.c (tcbdbaddint, tcbdbadddouble): new functions. * tcfdb.c (tcfdbaddint, tcfdbadddouble): new functions. - Release: 1.3.6 2008-08-25 FAL Labs * tcutil.c (tcatoi): new function. * tchdb.c (HDBDEFBNUM): increased for typical usecases. * tcbdb.c (BDBDEFBNUM): increased for typical usecases. * tchdb.c (tchdbsetxmsiz, tchdbxmsiz): new functions. - Release: 1.3.5 2008-08-23 FAL Labs * tchdb.c (tcseekreadtry): new function. - Release: 1.3.4 2008-08-20 FAL Labs * tchdb.c (tchdblockrecord, tchdbunlockrecord): new functions. * tchdb.c (tchdbputimpl, tchdboutimpl, tchdbgutimpl): concurrency was improved. - Release: 1.3.3 2008-08-01 FAL Labs * tcutil.c (tcmapaddint, tcmapput3, tcmdbput3): mismatch of signature was fixed. * tcutil.c (tcmapadddouble): new function. - Release: 1.3.2 2008-07-29 FAL Labs * tcadb.c (tcadbopen): "opts=b" option is now supported. - Release: 1.3.1 2008-07-15 FAL Labs * tcutil.c (tcmd5hash): new function. * tcutil.c (tcchidxnew, tcchidxdel, tcchidxhash): refactoring. * tcutil.c (tcbzipencode, tcbzipdecode): new functions. * tchdb.c (tchdbsetcodecfunc, tchdbcodecfunc): new functions. * tcbdb.c (tcbdbsetcodecfunc, tcbdbcodecfunc): new functions. * md5.h, md5.c: new files, whose author is L. Peter Deutsch. * myconf.c (_tc_bzcompress_impl, _tc_bzdecompress_impl): new functions. * myconf.c (_tc_recencode, _tc_recdecode): new functions. - Release: 1.3.0 2008-07-11 FAL Labs * tchdb.c (tchdbcacheclear): new function. * tcbdb.c (tcbdbcacheclear): new function. - Release: 1.2.12 2008-07-08 FAL Labs * tcutil.c (tcmapkeys2, tcmapvals2): new functions. * tchdb.c (tchdboptimizeimpl): user meta data is now to be copied. - Release: 1.2.11 2008-07-01 FAL Labs * tcutil.c (tcjetlag): missing of "timezone" variable is now allowed. - Release: 1.2.10 2008-06-17 FAL Labs * tcutil.c (tcjetlag): new function. * tcutil.c (tcmkgmtime): a bug related to time difference was fixed. * tchdb.c (tchdbgetnext, tchdbgetnext2): new functions. * tchtest.c (runwrite, procwrite): "-rnd" option was added. * tcbtest.c (runwrite, procwrite): "-rnd" option was added. * tcftest.c (runwrite, procwrite): "-rnd" option was added. - Release: 1.2.9 2008-06-01 FAL Labs * tcutil.c (tcerrmsg): new function. * tcfdb.h, tcfdb.c: new files. * tcadb.c: fixed-length database is now supported. - Release: 1.2.8 2008-05-23 FAL Labs * tcutil.c (tcchordnew, tcchorddel, tcchordhash): new functions. * tcbdb.c (tcbdbleafload, tcbdbnodeload): memory leak bugs with multi-thread were fixed. - Release: 1.2.7 2008-05-01 FAL Labs * myconf.h: code cleanup. * tcutil.c (tcmimebreak, tcmimeparts): new functions. * tchdb.c, tcbdb.c, tcadb.c: "free" was replaced by "tcfree". * tcbdb.c (tcbdbtrancommit, tcbdbtranabort): cache adjustment was added. * tchdb.c (tchdbsetecode): a bug of processing unopened files was fixed. * tcawmgr.c: new file. - Release: 1.2.6 2008-04-22 FAL Labs * tcbdb.c (tcbdbcmpdecimal, tcbdbcmpint32, tcbdbcmpint64): bugs of overflow were fixed. - Release: 1.2.5 2008-04-13 FAL Labs * tcbdb.c (tcbdbopenimpl): comparison function checking was added. * tcadb.c (tcadbopen): "capnum" option is now supported for B+ tree. - Release: 1.2.4 2008-04-07 FAL Labs * tcutil.c (tcregexmatch, tcregexreplace, tcglobpat): new functions. * tcbdb.c (tcbdbcmpfunc, tcbdbcmpop): new functions. * tcbdb.c (tcbdboptimizeimpl): leaf size limitation is now implemented. * tcbdb.c (tcbdbsetcapnum): new function. - Release: 1.2.3 2008-03-19 FAL Labs * tcutil.c (tcmapnew2): "calloc" is now used. * tcbdb.c (tcbdbputimpl): algorithm to divide large leaves was modified. - Release: 1.2.2 2008-03-13 FAL Labs * tcutil.c (tclistelemcmp, tclistelemcmpci): bugs about multibyte ordering were fixed. * tcutil.c (tclistsortex): new function. * tcbdb.c (tcbdbsetecode): new function. * tcadb.c (tcadbopen): "c", "t", "e", and "w" options were added. - Release: 1.2.1 2008-02-18 FAL Labs * tcmdb.c (tcmdbfwmkeys, tcmdbfwmkeys2): new functions. * tchdb.c (tchdbfwmkeys, tchdbfwmkeys2): new functions. * tcbdb.c (tcbdbfwmkeys, tcbdbfwmkeys2): new functions instead of "tcbdbrange3". * tcadb.c (tcadbfwmkeys, tcadbfwmkeys2): new functions. * tcbdb.c (tcbdbrangeimpl): a bug related to mutex operation was fixed. - The library version was bumped up for some binary packages. - Release: 1.2.0 2008-02-15 FAL Labs * tcutil.c (tcbwtsortchrcount): time efficiency was improved. - The library version was bumped up for some binary packages. - Release: 1.1.15 2008-02-05 FAL Labs * tcatest.c (runwicked, procwicked): new functions. - Release: 1.1.14 2008-01-30 FAL Labs * tcutil.c (tctime): measure unit was changed to in seconds. * tchdb.c (tchdbcopy): shared lock is now used. * tcadb.c (tcadbsync, tcadbcopy): new functions. - Release: 1.1.13 2008-01-23 FAL Labs * tcbdb.c (tcbdbleafkill, tcbdbnodesubidx): new functions. * tcbtest.c (runqueue, procqueue): new functions. - Release: 1.1.12 2008-01-20 FAL Labs * tcutil.c (tcmapmsiz, tcmdbmsiz, tcadbsize): new functions. * tcutil.c (tcglobalmutexshared): new function. * tcutil.c (tcglobalmutexinit, tcglobalmutexdestroy): new functions. * tcutil.c (tcreadfile): a bug related to the size assignment was fixed. * tcadb.c (tcadbopen): "capsiz" parameter was added. - Release: 1.1.11 2008-01-17 FAL Labs * tcutil.c (tclistshift, tclistshift2): memory alignment was improved. * tcutil.c (tcvxstrprintf): a bug related to format of long long integer was fixed. - Release: 1.1.10 2008-01-10 FAL Labs * tcutil.c (tcmdbvsiz, tcmdbvsiz2): new functions. * tcutil.c (tcmdbiternext): a bug related to handling sparse map was fixed. * tcadb.h, tcadb.c: new files. * tcatest.c, tcadb.c: new files. - Release: 1.1.9 2008-01-03 FAL Labs * tcutil.c (tcstrutftoucs, tcstrucstoutf, tcstrjoin): new function. * tcutil.c (tcstrdist, tcstrdistutf): new function. - Release: 1.1.8 2007-12-28 FAL Labs * myconf.h: some header includings were removed for environments not conforming to C99. * tcutil.c (tctccalendar, tcdatestrwww, tcdatestrhttp): new functions. * tcutil.c (tcmapaddint): new function. * tcucodec.c (rundate, procdate): new functions. - Release: 1.1.7 2007-12-24 FAL Labs * tchdb.c (tcseekread, tcseekwrite): pread and pwrite were to be used. - Release: 1.1.6 2007-12-21 FAL Labs * tcutil.c (tcmdbnew, tcmdbput, tcmdbget): concurrency was improved. * tcutil.c (tcmapcutfront, tcmdbcutfront): new functions. * tchdb.c (tchdbasyncimpl): large deferred buffer was to be flushed. * tcumttest.c: new file. - Release: 1.1.5 2007-12-19 FAL Labs * tcutil.c (tclrand, tcdrand, tcdrandnd): new functions. * tchdb.c (tchdbcacheadjust): count checking was removed. * tchmttest.c (myrandnd): new function. * tcbmttest.c (myrandnd): new function. - Release: 1.1.4 2007-12-10 FAL Labs * tcutil.c (tcglobalmutexlock): the type of the global mutex was changed to rwlock. * tchdb.c (tchdbsetmutex, tchdbopenimpl): multiple reading file descriptors were added. * tchmttest.c (runtypical, proctypical): reading ratio assignment was added. * tcbmttest.c (runtypical, proctypical): reading ratio assignment was added. - Release: 1.1.3 2007-12-08 FAL Labs * tcutil.c (tcmpoolput): mutex for operations was added. - Release: 1.1.2 2007-12-08 FAL Labs * tcutil.c (tcberencode, tcberdecode): new functions. * tcbdb.c (tcbdbleafload): speed was improved. - Release: 1.1.1 2007-12-07 FAL Labs * tchdb.c (tchdbsetcache): new function. * tchmttest.c (procwrite, procread): random mode was added. * tcbmttest.c (procwrite, procread): random mode was added. - Release: 1.1.0 2007-12-01 FAL Labs * tchmgr.c (procget): a bug related to the open omode was fixed. * tchdb.c (tchdbreadrec): a bug related to overflow on 32-bit environment was fixed. * tchdb.c (tchdbgetbucket): the type of the return value was changed. * tcbdb.c (tcbdbcurfirst, tcbdbcurnext): cache adjustment was to be performed. * tcbdb.c (tcbdbrange, tcbdbrange2, tcbdbrange3): new functions. * tchmgr.c (proclist): "-m" option was added. * tchmgr.c (procimporttsv): new function. * tcbmgr.c (proclist): "-m", "-rb", and "-rp" options were added. * tcbmgr.c (procimporttsv): new function. - Release: 1.0.9 2007-11-28 FAL Labs * tchmttest.c (threadtypical): a test case was added. * tchdb.c (tchdbfbpmerge): a bug related to iterator was fixed. * tcbdb.c (tcbdbgetimpl): a bug related to concurrent cache cleaning was fixed. - Release: 1.0.8 2007-11-20 FAL Labs * tchdb.c (tchdblockmethod): a bug related to R/W lock was fixed. * tcbdb.c (tcbdblockmethod): a bug related to R/W lock was fixed. * tchdb.c, tcbdb.c: locking functions were re-imlemented as macros. * tchmttest.c, tcbmttest.c: test cases of typical operations were added. * tokyocabinet.idl: methods handling list parameters were added. - Release: 1.0.7 2007-11-15 FAL Labs * tcbdb.c (tcbdboptimize): the default behaviour of bnum was changed. - Release: 1.0.6 2007-11-10 FAL Labs * tcutil.c (tcrealpath, tccopyfile): new functions. * tchdb.c (tchdbcopy): new function. * tcbdb.c (tcbdbcopy): new function. - Release: 1.0.5 2007-11-10 FAL Labs * tcbdb.c (tcbdbtune): comparing functions were optimized with macros. * tcbdb.c (tcbdbsetlsmax): new function. * myconf.c, tcutil.c, tchdb.c, tcbdb.c: code cleanup and optimization. - Release: 1.0.4 2007-11-08 FAL Labs * tcbdb.c (tcbdbleafsave, tcbdbleafload): warnings on 64-bit system were cleared. * configure.in: 64-bit offset mode for 32-bit system was added. * Makefile.in: Mac OS X is now supported. - Release: 1.0.3 2007-11-01 FAL Labs * tcbdb.c (tcbdbdel): a bug that opened database was not closed was fixed. * tcbdb.c (tcbdbcurout): a bug of deleting always the first value was fixed. * tcbdb.c (tcbdbopenimpl): a potential bug of not initializing meta data was fixed. * tcutil.h, tchdb.h, tcbdb.h: wrapped in C linkage block for C++. * tokyocabinet.idl: definition of constants were added. - commands for performance test were added. - Release: 1.0.2 2007-10-28 FAL Labs * tcbdb.c (tchdboptimize): a bug related to custom comparison function was fixed. * tcbdb.c (tcbdbleafsave): empty pages was to be removed for time space efficiency. * tcbdb.c (tcbdbputdup3): new function. * tchdb.c (tchdbvanish): new function. * tcbdb.c (tcbdbvanish): new function. * tokyocabinet.idl: new file. - Release: 1.0.1 2007-10-24 FAL Labs * tokyocabinet.pc.in: new file. - document files were fulfilled. - Release: 1.0.0 2007-10-15 FAL Labs * tcbdb.c (tchdbtranbegin): locking mode was aolished. * tcbdb.c (tcbdbsetcmpfunc): new function. - Release: 0.4.1 2007-10-11 FAL Labs * tchdb.c (tchdbopenimpl): database corruption was to be handled automatically. * tcbdb.c, tcbtest.c, tcbmttest.c, tcbmgr.c: new files. - Release: 0.4.0 2007-09-09 FAL Labs * tchdb.c (tchdbsetmutex, tchdblockobj, tchdbunlockobj): new functions. * tchmttest.c: new file. - Release: 0.3.4 2007-09-05 FAL Labs * tchdb.c (tchdbopen): TCBS compression mode is now supported. - Release: 0.3.3 2007-09-01 FAL Labs * tchdb.c (tcbsencode, tcbsdecode): new functions. - Release: 0.3.2 2007-08-25 FAL Labs * tchdb.c (tcpackencode, tcpackdecode): new functions. * tchdb.c (tcbwtencode, tcbwtdecode): new functions. - Release: 0.3.1 2007-08-22 FAL Labs * tchdb.c (tchdbputasync, tchdbputasync2): new functions. - Release: 0.3.0 2007-08-18 FAL Labs * tchdb.c (tchdboptimize): a bug causing data corruption was fixed. - Release: 0.2.8 2007-08-15 FAL Labs * tchdb.c (tchdboptimize): new function. - Release: 0.2.7 2007-08-14 FAL Labs * tchdb.c (tchdbsavefbp, tchdbloadfbp): a bug related to 64-bit support was fixed. * tchdb.c (tchdbreadrec, tchdbwriterec): a bug related to 64-bit support was fixed. - Release: 0.2.6 2007-08-13 FAL Labs * tchdb.c (tchdbsavefbp, tchdbloadfbp): new functions. - Release: 0.2.5 2007-08-12 FAL Labs - The initial version. - Release: 0.2.4 tokyocabinet-1.4.48/tchtest.c0000644000175000017500000017434612013574446015154 0ustar mikiomikio/************************************************************************************************* * The test cases of the hash database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" #define RECBUFSIZ 48 // buffer for records /* global variables */ const char *g_progname; // program name unsigned int g_randseed; // random seed int g_dbgfd; // debugging output /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void iprintf(const char *format, ...); static void iputchar(int c); static void eprint(TCHDB *hdb, int line, const char *func); static void mprint(TCHDB *hdb); static void sysprint(void); static int myrand(int range); static void *pdprocfunc(const void *vbuf, int vsiz, int *sp, void *op); static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op); static int runwrite(int argc, char **argv); static int runread(int argc, char **argv); static int runremove(int argc, char **argv); static int runrcat(int argc, char **argv); static int runmisc(int argc, char **argv); static int runwicked(int argc, char **argv); static int procwrite(const char *path, int rnum, int bnum, int apow, int fpow, bool mt, int opts, int rcnum, int xmsiz, int dfunit, int omode, bool as, bool rnd); static int procread(const char *path, bool mt, int rcnum, int xmsiz, int dfunit, int omode, bool wb, bool rnd); static int procremove(const char *path, bool mt, int rcnum, int xmsiz, int dfunit, int omode, bool rnd); static int procrcat(const char *path, int rnum, int bnum, int apow, int fpow, bool mt, int opts, int rcnum, int xmsiz, int dfunit, int omode, int pnum, bool dai, bool dad, bool rl, bool ru); static int procmisc(const char *path, int rnum, bool mt, int opts, int omode); static int procwicked(const char *path, int rnum, bool mt, int opts, int omode); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; const char *ebuf = getenv("TCRNDSEED"); g_randseed = ebuf ? tcatoix(ebuf) : tctime() * 1000; srand(g_randseed); ebuf = getenv("TCDBGFD"); g_dbgfd = ebuf ? tcatoix(ebuf) : UINT16_MAX; if(argc < 2) usage(); int 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], "remove")){ rv = runremove(argc, argv); } else if(!strcmp(argv[1], "rcat")){ rv = runrcat(argc, argv); } else if(!strcmp(argv[1], "misc")){ rv = runmisc(argc, argv); } else if(!strcmp(argv[1], "wicked")){ rv = runwicked(argc, argv); } else { usage(); } if(rv != 0){ printf("FAILED: TCRNDSEED=%u PID=%d", g_randseed, (int)getpid()); for(int i = 0; i < argc; i++){ printf(" %s", argv[i]); } printf("\n\n"); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: test cases of the hash database API of Tokyo Cabinet\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s write [-mt] [-tl] [-td|-tb|-tt|-tx] [-rc num] [-xm num] [-df num]" " [-nl|-nb] [-as] [-rnd] path rnum [bnum [apow [fpow]]]\n", g_progname); fprintf(stderr, " %s read [-mt] [-rc num] [-xm num] [-df num] [-nl|-nb] [-wb] [-rnd] path\n", g_progname); fprintf(stderr, " %s remove [-mt] [-rc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path\n", g_progname); fprintf(stderr, " %s 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]]]\n", g_progname); fprintf(stderr, " %s misc [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum\n", g_progname); fprintf(stderr, " %s wicked [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path 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); } /* print a character and flush the buffer */ static void iputchar(int c){ putchar(c); fflush(stdout); } /* print error message of hash database */ static void eprint(TCHDB *hdb, int line, const char *func){ const char *path = tchdbpath(hdb); int ecode = tchdbecode(hdb); fprintf(stderr, "%s: %s: %d: %s: error: %d: %s\n", g_progname, path ? path : "-", line, func, ecode, tchdberrmsg(ecode)); } /* print members of hash database */ static void mprint(TCHDB *hdb){ if(hdb->cnt_writerec < 0) return; iprintf("bucket number: %lld\n", (long long)tchdbbnum(hdb)); iprintf("used bucket number: %lld\n", (long long)tchdbbnumused(hdb)); iprintf("cnt_writerec: %lld\n", (long long)hdb->cnt_writerec); iprintf("cnt_reuserec: %lld\n", (long long)hdb->cnt_reuserec); iprintf("cnt_moverec: %lld\n", (long long)hdb->cnt_moverec); iprintf("cnt_readrec: %lld\n", (long long)hdb->cnt_readrec); iprintf("cnt_searchfbp: %lld\n", (long long)hdb->cnt_searchfbp); iprintf("cnt_insertfbp: %lld\n", (long long)hdb->cnt_insertfbp); iprintf("cnt_splicefbp: %lld\n", (long long)hdb->cnt_splicefbp); iprintf("cnt_dividefbp: %lld\n", (long long)hdb->cnt_dividefbp); iprintf("cnt_mergefbp: %lld\n", (long long)hdb->cnt_mergefbp); iprintf("cnt_reducefbp: %lld\n", (long long)hdb->cnt_reducefbp); iprintf("cnt_appenddrp: %lld\n", (long long)hdb->cnt_appenddrp); iprintf("cnt_deferdrp: %lld\n", (long long)hdb->cnt_deferdrp); iprintf("cnt_flushdrp: %lld\n", (long long)hdb->cnt_flushdrp); iprintf("cnt_adjrecc: %lld\n", (long long)hdb->cnt_adjrecc); iprintf("cnt_defrag: %lld\n", (long long)hdb->cnt_defrag); iprintf("cnt_shiftrec: %lld\n", (long long)hdb->cnt_shiftrec); iprintf("cnt_trunc: %lld\n", (long long)hdb->cnt_trunc); } /* print system information */ static void sysprint(void){ TCMAP *info = tcsysinfo(); if(info){ tcmapiterinit(info); const char *kbuf; while((kbuf = tcmapiternext2(info)) != NULL){ iprintf("sys_%s: %s\n", kbuf, tcmapiterval2(kbuf)); } tcmapdel(info); } } /* get a random number */ static int myrand(int range){ if(range < 2) return 0; int high = (unsigned int)rand() >> 4; int low = range * (rand() / (RAND_MAX + 1.0)); low &= (unsigned int)INT_MAX >> 4; return (high + low) % range; } /* duplication callback function */ static void *pdprocfunc(const void *vbuf, int vsiz, int *sp, void *op){ if(op){ char *buf = NULL; int len = 0; switch((int)(intptr_t)op){ case 1: len = vsiz + 1; buf = tcmalloc(len + 1); memset(buf, '*', len); break; case 2: buf = (void *)-1; break; } *sp = len; return buf; } if(myrand(4) == 0) return (void *)-1; if(myrand(2) == 0) return NULL; int len = myrand(RECBUFSIZ); char buf[RECBUFSIZ]; memset(buf, '*', len); *sp = len; return tcmemdup(buf, len); } /* iterator function */ static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op){ unsigned int sum = 0; while(--ksiz >= 0){ sum += ((char *)kbuf)[ksiz]; } while(--vsiz >= 0){ sum += ((char *)vbuf)[vsiz]; } return myrand(100 + (sum & 0xff)) > 0; } /* parse arguments of write command */ static int runwrite(int argc, char **argv){ char *path = NULL; char *rstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; bool mt = false; int opts = 0; int rcnum = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; bool as = false; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-tl")){ opts |= HDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= HDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= HDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= HDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= HDBTEXCODEC; } else if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else if(!strcmp(argv[i], "-as")){ as = true; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = procwrite(path, rnum, bnum, apow, fpow, mt, opts, rcnum, xmsiz, dfunit, omode, as, rnd); return rv; } /* parse arguments of read command */ static int runread(int argc, char **argv){ char *path = NULL; bool mt = false; int rcnum = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; bool wb = false; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else if(!strcmp(argv[i], "-wb")){ wb = true; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procread(path, mt, rcnum, xmsiz, dfunit, omode, wb, rnd); return rv; } /* parse arguments of remove command */ static int runremove(int argc, char **argv){ char *path = NULL; bool mt = false; int rcnum = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procremove(path, mt, rcnum, xmsiz, dfunit, omode, rnd); return rv; } /* parse arguments of rcat command */ static int runrcat(int argc, char **argv){ char *path = NULL; char *rstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; bool mt = false; int opts = 0; int rcnum = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; int pnum = 0; bool dai = false; bool dad = false; bool rl = false; bool ru = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-tl")){ opts |= HDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= HDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= HDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= HDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= HDBTEXCODEC; } else if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else if(!strcmp(argv[i], "-pn")){ if(++i >= argc) usage(); pnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-dai")){ dai = true; } else if(!strcmp(argv[i], "-dad")){ dad = true; } else if(!strcmp(argv[i], "-rl")){ rl = true; } else if(!strcmp(argv[i], "-ru")){ ru = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = procrcat(path, rnum, bnum, apow, fpow, mt, opts, rcnum, xmsiz, dfunit, omode, pnum, dai, dad, rl, ru); return rv; } /* parse arguments of misc command */ static int runmisc(int argc, char **argv){ char *path = NULL; char *rstr = NULL; bool mt = false; int opts = 0; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-tl")){ opts |= HDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= HDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= HDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= HDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= HDBTEXCODEC; } else if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int rv = procmisc(path, rnum, mt, opts, omode); return rv; } /* parse arguments of wicked command */ static int runwicked(int argc, char **argv){ char *path = NULL; char *rstr = NULL; bool mt = false; int opts = 0; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-tl")){ opts |= HDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= HDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= HDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= HDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= HDBTEXCODEC; } else if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int rv = procwicked(path, rnum, mt, opts, omode); return rv; } /* perform write command */ static int procwrite(const char *path, int rnum, int bnum, int apow, int fpow, bool mt, int opts, int rcnum, int xmsiz, int dfunit, int omode, bool as, bool rnd){ iprintf("\n seed=%u path=%s rnum=%d bnum=%d apow=%d fpow=%d mt=%d" " opts=%d rcnum=%d xmsiz=%d dfunit=%d omode=%d as=%d rnd=%d\n\n", g_randseed, path, rnum, bnum, apow, fpow, mt, opts, rcnum, xmsiz, dfunit, omode, as, rnd); bool err = false; double stime = tctime(); TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(mt && !tchdbsetmutex(hdb)){ eprint(hdb, __LINE__, "tchdbsetmutex"); err = true; } if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(hdb, __LINE__, "tchdbsetcodecfunc"); err = true; } if(!tchdbtune(hdb, bnum, apow, fpow, opts)){ eprint(hdb, __LINE__, "tchdbtune"); err = true; } if(!tchdbsetcache(hdb, rcnum)){ eprint(hdb, __LINE__, "tchdbsetcache"); err = true; } if(xmsiz >= 0 && !tchdbsetxmsiz(hdb, xmsiz)){ eprint(hdb, __LINE__, "tchdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tchdbsetdfunit(hdb, dfunit)){ eprint(hdb, __LINE__, "tchdbsetdfunit"); err = true; } if(!rnd) omode |= HDBOTRUNC; if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | omode)){ eprint(hdb, __LINE__, "tchdbopen"); err = true; } for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", rnd ? myrand(rnum) + 1 : i); if(as){ if(!tchdbputasync(hdb, buf, len, buf, len)){ eprint(hdb, __LINE__, "tchdbput"); err = true; break; } } else { if(!tchdbput(hdb, buf, len, buf, len)){ eprint(hdb, __LINE__, "tchdbput"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb)); iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb)); mprint(hdb); sysprint(); if(!tchdbclose(hdb)){ eprint(hdb, __LINE__, "tchdbclose"); err = true; } tchdbdel(hdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform read command */ static int procread(const char *path, bool mt, int rcnum, int xmsiz, int dfunit, int omode, bool wb, bool rnd){ iprintf("\n seed=%u path=%s mt=%d rcnum=%d xmsiz=%d dfunit=%d omode=%d" " wb=%d rnd=%d\n\n", g_randseed, path, mt, rcnum, xmsiz, dfunit, omode, wb, rnd); bool err = false; double stime = tctime(); TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(mt && !tchdbsetmutex(hdb)){ eprint(hdb, __LINE__, "tchdbsetmutex"); err = true; } if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(hdb, __LINE__, "tchdbsetcodecfunc"); err = true; } if(!tchdbsetcache(hdb, rcnum)){ eprint(hdb, __LINE__, "tchdbsetcache"); err = true; } if(xmsiz >= 0 && !tchdbsetxmsiz(hdb, xmsiz)){ eprint(hdb, __LINE__, "tchdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tchdbsetdfunit(hdb, dfunit)){ eprint(hdb, __LINE__, "tchdbsetdfunit"); err = true; } if(!tchdbopen(hdb, path, HDBOREADER | omode)){ eprint(hdb, __LINE__, "tchdbopen"); err = true; } int rnum = tchdbrnum(hdb); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", rnd ? myrand(rnum) + 1 : i); int vsiz; if(wb){ char vbuf[RECBUFSIZ]; int vsiz = tchdbget3(hdb, kbuf, ksiz, vbuf, RECBUFSIZ); if(vsiz < 0 && !(rnd && tchdbecode(hdb) == TCENOREC)){ eprint(hdb, __LINE__, "tchdbget3"); err = true; break; } } else { char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz); if(!vbuf && !(rnd && tchdbecode(hdb) == TCENOREC)){ eprint(hdb, __LINE__, "tchdbget"); err = true; break; } tcfree(vbuf); } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb)); iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb)); mprint(hdb); sysprint(); if(!tchdbclose(hdb)){ eprint(hdb, __LINE__, "tchdbclose"); err = true; } tchdbdel(hdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform remove command */ static int procremove(const char *path, bool mt, int rcnum, int xmsiz, int dfunit, int omode, bool rnd){ iprintf("\n seed=%u path=%s mt=%d rcnum=%d xmsiz=%d dfunit=%d" " omode=%d rnd=%d\n\n", g_randseed, path, mt, rcnum, xmsiz, dfunit, omode, rnd); bool err = false; double stime = tctime(); TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(mt && !tchdbsetmutex(hdb)){ eprint(hdb, __LINE__, "tchdbsetmutex"); err = true; } if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(hdb, __LINE__, "tchdbsetcodecfunc"); err = true; } if(!tchdbsetcache(hdb, rcnum)){ eprint(hdb, __LINE__, "tchdbsetcache"); err = true; } if(xmsiz >= 0 && !tchdbsetxmsiz(hdb, xmsiz)){ eprint(hdb, __LINE__, "tchdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tchdbsetdfunit(hdb, dfunit)){ eprint(hdb, __LINE__, "tchdbsetdfunit"); err = true; } if(!tchdbopen(hdb, path, HDBOWRITER | omode)){ eprint(hdb, __LINE__, "tchdbopen"); err = true; } int rnum = tchdbrnum(hdb); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", rnd ? myrand(rnum) + 1 : i); if(!tchdbout(hdb, kbuf, ksiz) && !(rnd && tchdbecode(hdb) == TCENOREC)){ eprint(hdb, __LINE__, "tchdbout"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb)); iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb)); mprint(hdb); sysprint(); if(!tchdbclose(hdb)){ eprint(hdb, __LINE__, "tchdbclose"); err = true; } tchdbdel(hdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform rcat command */ static int procrcat(const char *path, int rnum, int bnum, int apow, int fpow, bool mt, int opts, int rcnum, int xmsiz, int dfunit, int omode, int pnum, bool dai, bool dad, bool rl, bool ru){ iprintf("\n" " seed=%u path=%s rnum=%d bnum=%d apow=%d fpow=%d mt=%d opts=%d" " rcnum=%d xmsiz=%d dfunit=%d omode=%d pnum=%d dai=%d dad=%d rl=%d ru=%d\n\n", g_randseed, path, rnum, bnum, apow, fpow, mt, opts, rcnum, xmsiz, dfunit, omode, pnum, dai, dad, rl, ru); if(pnum < 1) pnum = rnum; bool err = false; double stime = tctime(); TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(mt && !tchdbsetmutex(hdb)){ eprint(hdb, __LINE__, "tchdbsetmutex"); err = true; } if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(hdb, __LINE__, "tchdbsetcodecfunc"); err = true; } if(!tchdbtune(hdb, bnum, apow, fpow, opts)){ eprint(hdb, __LINE__, "tchdbtune"); err = true; } if(!tchdbsetcache(hdb, rcnum)){ eprint(hdb, __LINE__, "tchdbsetcache"); err = true; } if(xmsiz >= 0 && !tchdbsetxmsiz(hdb, xmsiz)){ eprint(hdb, __LINE__, "tchdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tchdbsetdfunit(hdb, dfunit)){ eprint(hdb, __LINE__, "tchdbsetdfunit"); err = true; } if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){ eprint(hdb, __LINE__, "tchdbopen"); err = true; } for(int i = 1; i <= rnum; i++){ if(ru){ char fmt[RECBUFSIZ]; sprintf(fmt, "%%0%dd", myrand(RECBUFSIZ)); char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, fmt, myrand(pnum)); switch(myrand(8)){ case 0: if(!tchdbput(hdb, kbuf, ksiz, kbuf, ksiz)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } break; case 1: if(!tchdbputkeep(hdb, kbuf, ksiz, kbuf, ksiz) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbputkeep"); err = true; } break; case 2: if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbout"); err = true; } break; case 3: if(tchdbaddint(hdb, kbuf, ksiz, 1) == INT_MIN && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbaddint"); err = true; } break; case 4: if(isnan(tchdbadddouble(hdb, kbuf, ksiz, 1.0)) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbadddouble"); err = true; } break; case 5: if(myrand(2) == 0){ if(!tchdbputproc(hdb, kbuf, ksiz, kbuf, ksiz, pdprocfunc, NULL) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbputproc"); err = true; } } else { if(!tchdbputproc(hdb, kbuf, ksiz, NULL, 0, pdprocfunc, NULL) && tchdbecode(hdb) != TCEKEEP && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbputproc"); err = true; } } break; default: if(!tchdbputcat(hdb, kbuf, ksiz, kbuf, ksiz)){ eprint(hdb, __LINE__, "tchdbputcat"); err = true; } break; } if(err) break; } else { char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(pnum)); if(dai){ if(tchdbaddint(hdb, kbuf, ksiz, myrand(3)) == INT_MIN){ eprint(hdb, __LINE__, "tchdbaddint"); err = true; break; } } else if(dad){ if(isnan(tchdbadddouble(hdb, kbuf, ksiz, myrand(30) / 10.0))){ eprint(hdb, __LINE__, "tchdbadddouble"); err = true; break; } } else if(rl){ char vbuf[PATH_MAX]; int vsiz = myrand(PATH_MAX); for(int j = 0; j < vsiz; j++){ vbuf[j] = myrand(0x100); } if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbputcat"); err = true; break; } } else { if(!tchdbputcat(hdb, kbuf, ksiz, kbuf, ksiz)){ eprint(hdb, __LINE__, "tchdbputcat"); err = true; break; } } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb)); iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb)); mprint(hdb); sysprint(); if(!tchdbclose(hdb)){ eprint(hdb, __LINE__, "tchdbclose"); err = true; } tchdbdel(hdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform misc command */ static int procmisc(const char *path, int rnum, bool mt, int opts, int omode){ iprintf("\n seed=%u path=%s rnum=%d mt=%d opts=%d omode=%d\n\n", g_randseed, path, rnum, mt, opts, omode); bool err = false; double stime = tctime(); TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(mt && !tchdbsetmutex(hdb)){ eprint(hdb, __LINE__, "tchdbsetmutex"); err = true; } if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(hdb, __LINE__, "tchdbsetcodecfunc"); err = true; } if(!tchdbtune(hdb, rnum / 50, 2, -1, opts)){ eprint(hdb, __LINE__, "tchdbtune"); err = true; } if(!tchdbsetcache(hdb, rnum / 10)){ eprint(hdb, __LINE__, "tchdbsetcache"); err = true; } if(!tchdbsetxmsiz(hdb, rnum * sizeof(int))){ eprint(hdb, __LINE__, "tchdbsetxmsiz"); err = true; } if(!tchdbsetdfunit(hdb, 8)){ eprint(hdb, __LINE__, "tchdbsetdfunit"); err = true; } if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){ eprint(hdb, __LINE__, "tchdbopen"); err = true; } if(TCUSEPTHREAD){ TCHDB *hdbdup = tchdbnew(); if(tchdbopen(hdbdup, path, HDBOREADER)){ eprint(hdb, __LINE__, "(validation)"); err = true; } else if(tchdbecode(hdbdup) != TCETHREAD){ eprint(hdb, __LINE__, "(validation)"); err = true; } tchdbdel(hdbdup); } iprintf("writing:\n"); for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", i); if(i % 3 == 0){ if(!tchdbputkeep(hdb, buf, len, buf, len)){ eprint(hdb, __LINE__, "tchdbputkeep"); err = true; break; } } else { if(!tchdbputasync(hdb, buf, len, buf, len)){ eprint(hdb, __LINE__, "tchdbputasync"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("reading:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", i); int vsiz; char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz); if(!vbuf){ eprint(hdb, __LINE__, "tchdbget"); err = true; break; } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){ eprint(hdb, __LINE__, "(validation)"); err = true; tcfree(vbuf); break; } tcfree(vbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(tchdbrnum(hdb) != rnum){ eprint(hdb, __LINE__, "(validation)"); err = true; } iprintf("random writing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); char vbuf[RECBUFSIZ]; int vsiz = myrand(RECBUFSIZ); memset(vbuf, '*', vsiz); if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbput"); err = true; break; } int rsiz; char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz); if(!rbuf){ eprint(hdb, __LINE__, "tchdbget"); err = true; break; } if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(hdb, __LINE__, "(validation)"); err = true; tcfree(rbuf); break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } tcfree(rbuf); } iprintf("word writing:\n"); const char *words[] = { "a", "A", "bb", "BB", "ccc", "CCC", "dddd", "DDDD", "eeeee", "EEEEEE", "mikio", "hirabayashi", "tokyo", "cabinet", "hyper", "estraier", "19780211", "birth day", "one", "first", "two", "second", "three", "third", "four", "fourth", "five", "fifth", "_[1]_", "uno", "_[2]_", "dos", "_[3]_", "tres", "_[4]_", "cuatro", "_[5]_", "cinco", "[\xe5\xb9\xb3\xe6\x9e\x97\xe5\xb9\xb9\xe9\x9b\x84]", "[\xe9\xa6\xac\xe9\xb9\xbf]", NULL }; for(int i = 0; words[i] != NULL; i += 2){ const char *kbuf = words[i]; int ksiz = strlen(kbuf); const char *vbuf = words[i+1]; int vsiz = strlen(vbuf); if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbputkeep"); err = true; break; } if(rnum > 250) iputchar('.'); } if(rnum > 250) iprintf(" (%08d)\n", (int)(sizeof(words) / sizeof(*words))); iprintf("random erasing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbout"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("writing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "[%d]", i); char vbuf[RECBUFSIZ]; int vsiz = i % RECBUFSIZ; memset(vbuf, '*', vsiz); if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbputkeep"); err = true; break; } if(vsiz < 1){ char tbuf[PATH_MAX]; for(int j = 0; j < PATH_MAX; j++){ tbuf[j] = myrand(0x100); } if(!tchdbput(hdb, kbuf, ksiz, tbuf, PATH_MAX)){ eprint(hdb, __LINE__, "tchdbput"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("erasing:\n"); for(int i = 1; i <= rnum; i++){ if(i % 2 == 1){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "[%d]", i); if(!tchdbout(hdb, kbuf, ksiz)){ eprint(hdb, __LINE__, "tchdbout"); err = true; break; } if(tchdbout(hdb, kbuf, ksiz) || tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbout"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("random writing and reopening:\n"); for(int i = 1; i <= rnum; i++){ if(myrand(10) == 0){ int ksiz, vsiz; char *kbuf, *vbuf; ksiz = (myrand(5) == 0) ? myrand(UINT16_MAX) : myrand(RECBUFSIZ); kbuf = tcmalloc(ksiz + 1); memset(kbuf, '@', ksiz); vsiz = (myrand(5) == 0) ? myrand(UINT16_MAX) : myrand(RECBUFSIZ); vbuf = tcmalloc(vsiz + 1); for(int j = 0; j < vsiz; j++){ vbuf[j] = myrand(256); } switch(myrand(4)){ case 0: if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } break; case 1: if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbputcat"); err = true; } break; case 2: if(!tchdbputasync(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbputasync"); err = true; } break; case 3: if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbout"); err = true; } break; } tcfree(vbuf); tcfree(kbuf); } else { char kbuf[RECBUFSIZ]; int ksiz = myrand(RECBUFSIZ); memset(kbuf, '@', ksiz); char vbuf[RECBUFSIZ]; int vsiz = myrand(RECBUFSIZ); memset(vbuf, '@', vsiz); if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbputcat"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tchdbclose(hdb)){ eprint(hdb, __LINE__, "tchdbclose"); err = true; } if(!tchdbopen(hdb, path, HDBOWRITER | omode)){ eprint(hdb, __LINE__, "tchdbopen"); err = true; } iprintf("checking:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "[%d]", i); int vsiz; char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz); if(i % 2 == 0){ if(!vbuf){ eprint(hdb, __LINE__, "tchdbget"); err = true; break; } if(vsiz != i % RECBUFSIZ && vsiz != PATH_MAX){ eprint(hdb, __LINE__, "(validation)"); err = true; tcfree(vbuf); break; } } else { if(vbuf || tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "(validation)"); err = true; tcfree(vbuf); break; } } tcfree(vbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("writing:\n"); for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", i); if(!tchdbput(hdb, buf, len, buf, len)){ eprint(hdb, __LINE__, "tchdbput"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("reading:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", i); int vsiz; char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz); if(!vbuf){ eprint(hdb, __LINE__, "tchdbget"); err = true; break; } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){ eprint(hdb, __LINE__, "(validation)"); err = true; tcfree(vbuf); break; } tcfree(vbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("checking words:\n"); for(int i = 0; words[i] != NULL; i += 2){ const char *kbuf = words[i]; int ksiz = strlen(kbuf); const char *vbuf = words[i+1]; int vsiz = strlen(vbuf); int rsiz; char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz); if(!rbuf){ eprint(hdb, __LINE__, "tchdbget"); err = true; break; } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(hdb, __LINE__, "(validation)"); err = true; tcfree(rbuf); break; } tcfree(rbuf); if(rnum > 250) iputchar('.'); } if(rnum > 250) iprintf(" (%08d)\n", (int)(sizeof(words) / sizeof(*words))); iprintf("checking iterator:\n"); int inum = 0; if(!tchdbiterinit(hdb)){ eprint(hdb, __LINE__, "tchdbiterinit"); err = true; } char *kbuf; int ksiz; for(int i = 1; (kbuf = tchdbiternext(hdb, &ksiz)) != NULL; i++, inum++){ int vsiz; char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz); if(!vbuf){ eprint(hdb, __LINE__, "tchdbget"); err = true; tcfree(kbuf); break; } tcfree(vbuf); tcfree(kbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); if(tchdbecode(hdb) != TCENOREC || inum != tchdbrnum(hdb)){ eprint(hdb, __LINE__, "(validation)"); err = true; } iprintf("iteration updating:\n"); if(!tchdbiterinit(hdb)){ eprint(hdb, __LINE__, "tchdbiterinit"); err = true; } inum = 0; for(int i = 1; (kbuf = tchdbiternext(hdb, &ksiz)) != NULL; i++, inum++){ if(myrand(2) == 0){ if(!tchdbputcat(hdb, kbuf, ksiz, "0123456789", 10)){ eprint(hdb, __LINE__, "tchdbputcat"); err = true; tcfree(kbuf); break; } } else { if(!tchdbout(hdb, kbuf, ksiz)){ eprint(hdb, __LINE__, "tchdbout"); err = true; tcfree(kbuf); break; } } tcfree(kbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); if(tchdbecode(hdb) != TCENOREC || inum < tchdbrnum(hdb)){ eprint(hdb, __LINE__, "(validation)"); err = true; } if(myrand(10) == 0 && !tchdbsync(hdb)){ eprint(hdb, __LINE__, "tchdbsync"); err = true; } if(!tchdbvanish(hdb)){ eprint(hdb, __LINE__, "tchdbvanish"); err = true; } TCMAP *map = tcmapnew(); iprintf("random writing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); char vbuf[RECBUFSIZ]; int vsiz = sprintf(vbuf, "%d", myrand(rnum)); switch(myrand(4)){ case 0: if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbputkeep"); err = true; } tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 2: if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbputcat"); err = true; } tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 3: if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbout"); err = true; } tcmapout(map, kbuf, ksiz); break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(myrand(4) == 0 && !tchdbdefrag(hdb, 0)){ eprint(hdb, __LINE__, "tchdbdefrag"); err = true; } if(myrand(4) == 0 && !tchdbcacheclear(hdb)){ eprint(hdb, __LINE__, "tchdbcacheclear"); err = true; } iprintf("checking transaction commit:\n"); if(!tchdbtranbegin(hdb)){ eprint(hdb, __LINE__, "tchdbtranbegin"); err = true; } for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); char vbuf[RECBUFSIZ]; int vsiz = sprintf(vbuf, "[%d]", myrand(rnum)); switch(myrand(7)){ case 0: if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbputkeep"); err = true; } tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 2: if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbputcat"); err = true; } tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 3: if(tchdbaddint(hdb, kbuf, ksiz, 1) == INT_MIN && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbaddint"); err = true; } tcmapaddint(map, kbuf, ksiz, 1); break; case 4: if(isnan(tchdbadddouble(hdb, kbuf, ksiz, 1.0)) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbadddouble"); err = true; } tcmapadddouble(map, kbuf, ksiz, 1.0); break; case 5: if(myrand(2) == 0){ void *op = (void *)(intptr_t)(myrand(3) + 1); if(!tchdbputproc(hdb, kbuf, ksiz, vbuf, vsiz, pdprocfunc, op) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbputproc"); err = true; } tcmapputproc(map, kbuf, ksiz, vbuf, vsiz, pdprocfunc, op); } else { vsiz = myrand(10); void *op = (void *)(intptr_t)(myrand(3) + 1); if(!tchdbputproc(hdb, kbuf, ksiz, NULL, vsiz, pdprocfunc, op) && tchdbecode(hdb) != TCEKEEP && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbputproc"); err = true; } tcmapputproc(map, kbuf, ksiz, NULL, vsiz, pdprocfunc, op); } break; case 6: if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbout"); err = true; } tcmapout(map, kbuf, ksiz); break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tchdbtrancommit(hdb)){ eprint(hdb, __LINE__, "tchdbtrancommit"); err = true; } iprintf("checking transaction abort:\n"); uint64_t ornum = tchdbrnum(hdb); uint64_t ofsiz = tchdbfsiz(hdb); if(!tchdbtranbegin(hdb)){ eprint(hdb, __LINE__, "tchdbtranbegin"); err = true; } for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); char vbuf[RECBUFSIZ]; int vsiz = sprintf(vbuf, "((%d))", myrand(rnum)); switch(myrand(7)){ case 0: if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } break; case 1: if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbputkeep"); err = true; } break; case 2: if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbputcat"); err = true; } break; case 3: if(tchdbaddint(hdb, kbuf, ksiz, 1) == INT_MIN && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbaddint"); err = true; } break; case 4: if(isnan(tchdbadddouble(hdb, kbuf, ksiz, 1.0)) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbadddouble"); err = true; } break; case 5: if(myrand(2) == 0){ void *op = (void *)(intptr_t)(myrand(3) + 1); if(!tchdbputproc(hdb, kbuf, ksiz, vbuf, vsiz, pdprocfunc, op) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbputproc"); err = true; } } else { vsiz = myrand(10); void *op = (void *)(intptr_t)(myrand(3) + 1); if(!tchdbputproc(hdb, kbuf, ksiz, NULL, vsiz, pdprocfunc, op) && tchdbecode(hdb) != TCEKEEP && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbputproc"); err = true; } } break; case 6: if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbout"); err = true; } break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tchdbtranabort(hdb)){ eprint(hdb, __LINE__, "tchdbtranabort"); err = true; } iprintf("checking consistency:\n"); if(tchdbrnum(hdb) != ornum || tchdbfsiz(hdb) != ofsiz || tchdbrnum(hdb) != tcmaprnum(map)){ eprint(hdb, __LINE__, "(validation)"); err = true; } inum = 0; tcmapiterinit(map); const char *tkbuf; int tksiz; for(int i = 1; (tkbuf = tcmapiternext(map, &tksiz)) != NULL; i++, inum++){ int tvsiz; const char *tvbuf = tcmapiterval(tkbuf, &tvsiz); int rsiz; char *rbuf = tchdbget(hdb, tkbuf, tksiz, &rsiz); if(!rbuf || rsiz != tvsiz || memcmp(rbuf, tvbuf, rsiz)){ eprint(hdb, __LINE__, "(validation)"); err = true; tcfree(rbuf); break; } tcfree(rbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); inum = 0; if(!tchdbiterinit(hdb)){ eprint(hdb, __LINE__, "tchdbiterinit"); err = true; } for(int i = 1; (kbuf = tchdbiternext(hdb, &ksiz)) != NULL; i++, inum++){ int vsiz; char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz); int rsiz; const char *rbuf = tcmapget(map, kbuf, ksiz, &rsiz); if(!rbuf || rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(hdb, __LINE__, "(validation)"); err = true; tcfree(vbuf); tcfree(kbuf); break; } tcfree(vbuf); tcfree(kbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); tcmapdel(map); if(!tchdbvanish(hdb)){ eprint(hdb, __LINE__, "tchdbvanish"); err = true; } for(int i = myrand(3) + 1; i < PATH_MAX; i = i * 2 + myrand(3)){ char vbuf[i]; memset(vbuf, '@', i - 1); vbuf[i-1] = '\0'; if(!tchdbput2(hdb, "mikio", vbuf)){ eprint(hdb, __LINE__, "tchdbput2"); err = true; } } if(!tchdbput2(hdb, "mikio", "nanashi")){ eprint(hdb, __LINE__, "tchdbput2"); err = true; } if(!tchdbtranbegin(hdb)){ eprint(hdb, __LINE__, "tchdbtranbegin"); err = true; } if(!tchdbput2(hdb, "mikio", "hirabayashi")){ eprint(hdb, __LINE__, "tchdbput2"); err = true; } for(int i = 0; i < 10; i++){ char buf[RECBUFSIZ]; int size = sprintf(buf, "%d", myrand(rnum)); if(!tchdbput(hdb, buf, size, buf, size)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } } for(int i = myrand(3) + 1; i < PATH_MAX; i = i * 2 + myrand(3)){ char vbuf[i]; memset(vbuf, '@', i - 1); vbuf[i-1] = '\0'; if(!tchdbput2(hdb, "mikio", vbuf)){ eprint(hdb, __LINE__, "tchdbput2"); err = true; } } if(!tchdbforeach(hdb, iterfunc, NULL)){ eprint(hdb, __LINE__, "tchdbforeach"); err = true; } iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb)); iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb)); mprint(hdb); sysprint(); if(!tchdbclose(hdb)){ eprint(hdb, __LINE__, "tchdbclose"); err = true; } tchdbdel(hdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform wicked command */ static int procwicked(const char *path, int rnum, bool mt, int opts, int omode){ iprintf("\n seed=%u path=%s rnum=%d mt=%d opts=%d omode=%d\n\n", g_randseed, path, rnum, mt, opts, omode); bool err = false; double stime = tctime(); TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(mt && !tchdbsetmutex(hdb)){ eprint(hdb, __LINE__, "tchdbsetmutex"); err = true; } if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(hdb, __LINE__, "tchdbsetcodecfunc"); err = true; } if(!tchdbtune(hdb, rnum / 50, 2, -1, opts)){ eprint(hdb, __LINE__, "tchdbtune"); err = true; } if(!tchdbsetcache(hdb, rnum / 10)){ eprint(hdb, __LINE__, "tchdbsetcache"); err = true; } if(!tchdbsetxmsiz(hdb, rnum * sizeof(int))){ eprint(hdb, __LINE__, "tchdbsetxmsiz"); err = true; } if(!tchdbsetdfunit(hdb, 8)){ eprint(hdb, __LINE__, "tchdbsetdfunit"); err = true; } if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){ eprint(hdb, __LINE__, "tchdbopen"); err = true; } if(!tchdbiterinit(hdb)){ eprint(hdb, __LINE__, "tchdbiterinit"); err = true; } TCMAP *map = tcmapnew2(rnum / 5); for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); char vbuf[RECBUFSIZ]; int vsiz = myrand(RECBUFSIZ); memset(vbuf, '*', vsiz); vbuf[vsiz] = '\0'; char *rbuf; switch(myrand(16)){ case 0: iputchar('0'); if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: iputchar('1'); if(!tchdbput2(hdb, kbuf, vbuf)){ eprint(hdb, __LINE__, "tchdbput2"); err = true; } tcmapput2(map, kbuf, vbuf); break; case 2: iputchar('2'); if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbputkeep"); err = true; } tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 3: iputchar('3'); if(!tchdbputkeep2(hdb, kbuf, vbuf) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbputkeep2"); err = true; } tcmapputkeep2(map, kbuf, vbuf); break; case 4: iputchar('4'); if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbputcat"); err = true; } tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 5: iputchar('5'); if(!tchdbputcat2(hdb, kbuf, vbuf)){ eprint(hdb, __LINE__, "tchdbputcat2"); err = true; } tcmapputcat2(map, kbuf, vbuf); break; case 6: iputchar('6'); if(!tchdbputasync(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbputasync"); err = true; } tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 7: iputchar('7'); if(!tchdbputasync2(hdb, kbuf, vbuf)){ eprint(hdb, __LINE__, "tchdbputasync2"); err = true; } tcmapput2(map, kbuf, vbuf); break; case 8: iputchar('8'); if(myrand(10) == 0){ if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbout"); err = true; } tcmapout(map, kbuf, ksiz); } break; case 9: iputchar('9'); if(myrand(10) == 0){ if(!tchdbout2(hdb, kbuf) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbout2"); err = true; } tcmapout2(map, kbuf); } break; case 10: iputchar('A'); if(!(rbuf = tchdbget(hdb, kbuf, ksiz, &vsiz))){ if(tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbget"); err = true; } rbuf = tcsprintf("[%d]", myrand(i + 1)); vsiz = strlen(rbuf); } vsiz += myrand(vsiz); if(myrand(3) == 0) vsiz += PATH_MAX; rbuf = tcrealloc(rbuf, vsiz + 1); for(int j = 0; j < vsiz; j++){ rbuf[j] = myrand(0x100); } if(!tchdbput(hdb, kbuf, ksiz, rbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } tcmapput(map, kbuf, ksiz, rbuf, vsiz); tcfree(rbuf); break; case 11: iputchar('B'); if(!(rbuf = tchdbget(hdb, kbuf, ksiz, &vsiz)) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbget"); err = true; } tcfree(rbuf); break; case 12: iputchar('C'); if(!(rbuf = tchdbget2(hdb, kbuf)) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbget2"); err = true; } tcfree(rbuf); break; case 13: iputchar('D'); if(myrand(1) == 0) vsiz = 1; if((vsiz = tchdbget3(hdb, kbuf, ksiz, vbuf, vsiz)) < 0 && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbget3"); err = true; } break; case 14: iputchar('E'); if(myrand(rnum / 50) == 0){ if(!tchdbiterinit(hdb)){ eprint(hdb, __LINE__, "tchdbiterinit"); err = true; } } TCXSTR *ikey = tcxstrnew(); TCXSTR *ival = tcxstrnew(); for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){ if(j % 3 == 0){ if(tchdbiternext3(hdb, ikey, ival)){ if(tcxstrsize(ival) != tchdbvsiz(hdb, tcxstrptr(ikey), tcxstrsize(ikey))){ eprint(hdb, __LINE__, "(validation)"); err = true; } } else { int ecode = tchdbecode(hdb); if(ecode != TCEINVALID && ecode != TCENOREC){ eprint(hdb, __LINE__, "tchdbiternext3"); err = true; } } } else { int iksiz; char *ikbuf = tchdbiternext(hdb, &iksiz); if(ikbuf){ tcfree(ikbuf); } else { int ecode = tchdbecode(hdb); if(ecode != TCEINVALID && ecode != TCENOREC){ eprint(hdb, __LINE__, "tchdbiternext"); err = true; } } } } tcxstrdel(ival); tcxstrdel(ikey); break; default: iputchar('@'); if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX); if(myrand(rnum / 16 + 1) == 0){ int cnt = myrand(30); for(int j = 0; j < rnum && !err; j++){ ksiz = sprintf(kbuf, "%d", i + j); if(tchdbout(hdb, kbuf, ksiz)){ cnt--; } else if(tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbout"); err = true; } tcmapout(map, kbuf, ksiz); if(cnt < 0) break; } } break; } if(i % 50 == 0) iprintf(" (%08d)\n", i); if(i == rnum / 2){ if(!tchdbclose(hdb)){ eprint(hdb, __LINE__, "tchdbclose"); err = true; } if(!tchdbopen(hdb, path, HDBOWRITER | omode)){ eprint(hdb, __LINE__, "tchdbopen"); err = true; } } else if(i == rnum / 4){ char *npath = tcsprintf("%s-tmp", path); if(!tchdbcopy(hdb, npath)){ eprint(hdb, __LINE__, "tchdbcopy"); err = true; } TCHDB *nhdb = tchdbnew(); if(!tchdbsetcodecfunc(nhdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(nhdb, __LINE__, "tchdbsetcodecfunc"); err = true; } if(!tchdbopen(nhdb, npath, HDBOREADER | omode)){ eprint(nhdb, __LINE__, "tchdbopen"); err = true; } tchdbdel(nhdb); unlink(npath); tcfree(npath); if(!tchdboptimize(hdb, rnum / 50, -1, -1, -1)){ eprint(hdb, __LINE__, "tchdboptimize"); err = true; } if(!tchdbiterinit(hdb)){ eprint(hdb, __LINE__, "tchdbiterinit"); err = true; } } else if(i == rnum / 8){ if(!tchdbtranbegin(hdb)){ eprint(hdb, __LINE__, "tchdbtranbegin"); err = true; } } else if(i == rnum / 8 + rnum / 16){ if(!tchdbtrancommit(hdb)){ eprint(hdb, __LINE__, "tchdbtrancommit"); err = true; } } } if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum); if(!tchdbsync(hdb)){ eprint(hdb, __LINE__, "tchdbsync"); err = true; } if(tchdbrnum(hdb) != tcmaprnum(map)){ eprint(hdb, __LINE__, "(validation)"); err = true; } for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", i - 1); int vsiz; const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz); int rsiz; char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz); if(vbuf){ iputchar('.'); if(!rbuf){ eprint(hdb, __LINE__, "tchdbget"); err = true; } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(hdb, __LINE__, "(validation)"); err = true; } } else { iputchar('*'); if(rbuf || tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "(validation)"); err = true; } } tcfree(rbuf); if(i % 50 == 0) iprintf(" (%08d)\n", i); } if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum); int inum = 0; char *cbuf; int csiz; if(myrand(2) == 0){ cbuf = tchdbgetnext(hdb, NULL, -1, &csiz); } else { const char *cvbuf; int cvsiz; cbuf = tchdbgetnext3(hdb, NULL, -1, &csiz, &cvbuf, &cvsiz); } while(cbuf){ inum++; iputchar(':'); int nsiz; char *nbuf; if(myrand(2) == 0){ nbuf = tchdbgetnext(hdb, cbuf, csiz, &nsiz); } else { const char *cvbuf; int cvsiz; nbuf = tchdbgetnext3(hdb, cbuf, csiz, &nsiz, &cvbuf, &cvsiz); } if(myrand(10) == 0){ if(!tchdbiterinit2(hdb, cbuf, csiz)){ eprint(hdb, __LINE__, "tchdbiterinit2"); err = true; } int ksiz; char *kbuf = tchdbiternext(hdb, &ksiz); if(kbuf){ tcfree(kbuf); } else { eprint(hdb, __LINE__, "tchdbiternext"); err = true; } for(int i = 0; i < 5; i++){ kbuf = tchdbiternext(hdb, &ksiz); if(kbuf){ tcfree(kbuf); } else if(tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbiternext"); err = true; } } } tcfree(cbuf); cbuf = nbuf; csiz = nsiz; if(inum % 50 == 0) iprintf(" (%08d)\n", inum); } tcfree(cbuf); if(inum % 50 > 0) iprintf(" (%08d)\n", inum); if(inum != tchdbrnum(hdb)){ eprint(hdb, __LINE__, "(validation)"); err = true; } tcmapiterinit(map); int ksiz; const char *kbuf; for(int i = 1; (kbuf = tcmapiternext(map, &ksiz)) != NULL; i++){ iputchar('+'); int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); int rsiz; char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz); if(!rbuf){ eprint(hdb, __LINE__, "tchdbget"); err = true; } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(hdb, __LINE__, "(validation)"); err = true; } tcfree(rbuf); if(!tchdbout(hdb, kbuf, ksiz)){ eprint(hdb, __LINE__, "tchdbout"); err = true; } if(i % 50 == 0) iprintf(" (%08d)\n", i); } int mrnum = tcmaprnum(map); if(mrnum % 50 > 0) iprintf(" (%08d)\n", mrnum); if(tchdbrnum(hdb) != 0){ eprint(hdb, __LINE__, "(validation)"); err = true; } iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb)); iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb)); mprint(hdb); sysprint(); tcmapdel(map); if(!tchdbclose(hdb)){ eprint(hdb, __LINE__, "tchdbclose"); err = true; } tchdbdel(hdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } // END OF FILE tokyocabinet-1.4.48/myconf.h0000644000175000017500000003575312013574446014774 0ustar mikiomikio/************************************************************************************************* * System-dependent configurations of Tokyo Cabinet * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #ifndef _MYCONF_H // duplication check #define _MYCONF_H /************************************************************************************************* * system discrimination *************************************************************************************************/ #if defined(__linux__) #define _SYS_LINUX_ #define TCSYSNAME "Linux" #elif defined(__FreeBSD__) #define _SYS_FREEBSD_ #define TCSYSNAME "FreeBSD" #elif defined(__NetBSD__) #define _SYS_NETBSD_ #define TCSYSNAME "NetBSD" #elif defined(__OpenBSD__) #define _SYS_OPENBSD_ #define TCSYSNAME "OpenBSD" #elif defined(__sun__) || defined(__sun) #define _SYS_SUNOS_ #define TCSYSNAME "SunOS" #elif defined(__hpux) #define _SYS_HPUX_ #define TCSYSNAME "HP-UX" #elif defined(__osf) #define _SYS_TRU64_ #define TCSYSNAME "Tru64" #elif defined(_AIX) #define _SYS_AIX_ #define TCSYSNAME "AIX" #elif defined(__APPLE__) && defined(__MACH__) #define _SYS_MACOSX_ #define TCSYSNAME "Mac OS X" #elif defined(_MSC_VER) #define _SYS_MSVC_ #define TCSYSNAME "Windows (VC++)" #elif defined(_WIN32) #define _SYS_MINGW_ #define TCSYSNAME "Windows (MinGW)" #elif defined(__CYGWIN__) #define _SYS_CYGWIN_ #define TCSYSNAME "Windows (Cygwin)" #else #define _SYS_GENERIC_ #define TCSYSNAME "Generic" #endif /************************************************************************************************* * common settings *************************************************************************************************/ #if defined(NDEBUG) #define TCDODEBUG(TC_expr) \ do { \ } while(false) #else #define TCDODEBUG(TC_expr) \ do { \ TC_expr; \ } while(false) #endif #define TCSWAB16(TC_num) \ ( \ ((TC_num & 0x00ffU) << 8) | \ ((TC_num & 0xff00U) >> 8) \ ) #define TCSWAB32(TC_num) \ ( \ ((TC_num & 0x000000ffUL) << 24) | \ ((TC_num & 0x0000ff00UL) << 8) | \ ((TC_num & 0x00ff0000UL) >> 8) | \ ((TC_num & 0xff000000UL) >> 24) \ ) #define TCSWAB64(TC_num) \ ( \ ((TC_num & 0x00000000000000ffULL) << 56) | \ ((TC_num & 0x000000000000ff00ULL) << 40) | \ ((TC_num & 0x0000000000ff0000ULL) << 24) | \ ((TC_num & 0x00000000ff000000ULL) << 8) | \ ((TC_num & 0x000000ff00000000ULL) >> 8) | \ ((TC_num & 0x0000ff0000000000ULL) >> 24) | \ ((TC_num & 0x00ff000000000000ULL) >> 40) | \ ((TC_num & 0xff00000000000000ULL) >> 56) \ ) #if defined(_MYBIGEND) || defined(_MYSWAB) #define TCBIGEND 1 #define TCHTOIS(TC_num) TCSWAB16(TC_num) #define TCHTOIL(TC_num) TCSWAB32(TC_num) #define TCHTOILL(TC_num) TCSWAB64(TC_num) #define TCITOHS(TC_num) TCSWAB16(TC_num) #define TCITOHL(TC_num) TCSWAB32(TC_num) #define TCITOHLL(TC_num) TCSWAB64(TC_num) #else #define TCBIGEND 0 #define TCHTOIS(TC_num) (TC_num) #define TCHTOIL(TC_num) (TC_num) #define TCHTOILL(TC_num) (TC_num) #define TCITOHS(TC_num) (TC_num) #define TCITOHL(TC_num) (TC_num) #define TCITOHLL(TC_num) (TC_num) #endif #if defined(_MYNOUBC) #define TCUBCACHE 0 #elif defined(_SYS_LINUX_) || defined(_SYS_FREEBSD_) || defined(_SYS_NETBSD_) || \ defined(_SYS_MACOSX_) || defined(_SYS_SUNOS_) #define TCUBCACHE 1 #else #define TCUBCACHE 0 #endif #if defined(_MYNOZLIB) #define TCUSEZLIB 0 #else #define TCUSEZLIB 1 #endif #if defined(_MYNOBZIP) #define TCUSEBZIP 0 #else #define TCUSEBZIP 1 #endif #if defined(_MYEXLZMA) #define TCUSEEXLZMA 1 #else #define TCUSEEXLZMA 0 #endif #if defined(_MYEXLZO) #define TCUSEEXLZO 1 #else #define TCUSEEXLZO 0 #endif #if defined(_MYNOPTHREAD) #define TCUSEPTHREAD 0 #else #define TCUSEPTHREAD 1 #endif #if defined(_MYMICROYIELD) #define TCMICROYIELD 1 #else #define TCMICROYIELD 0 #endif #define MYMALLOC malloc #define MYCALLOC calloc #define MYREALLOC realloc #define MYFREE free /************************************************************************************************* * general headers *************************************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if TCUSEPTHREAD #include #if defined(_POSIX_PRIORITY_SCHEDULING) #include #endif #endif /************************************************************************************************* * miscellaneous hacks *************************************************************************************************/ #if defined(__GNUC__) #define _alignof(TC_a) ((size_t)__alignof__(TC_a)) #else #define _alignof(TC_a) sizeof(TC_a) #endif #define _issigned(TC_a) ((TC_a)-1 < 1 ? true : false) #define _maxof(TC_a) \ ((TC_a)(sizeof(TC_a) == sizeof(int64_t) ? _issigned(TC_a) ? INT64_MAX : UINT64_MAX : \ sizeof(TC_a) == sizeof(int32_t) ? _issigned(TC_a) ? INT32_MAX : UINT32_MAX : \ sizeof(TC_a) == sizeof(int16_t) ? _issigned(TC_a) ? INT16_MAX : UINT16_MAX : \ _issigned(TC_a) ? INT8_MAX : UINT8_MAX)) #if defined(_SYS_FREEBSD_) || defined(_SYS_NETBSD_) || defined(_SYS_OPENBSD_) #define nan(TC_a) strtod("nan", NULL) #define nanl(TC_a) ((long double)strtod("nan", NULL)) #endif #if ! defined(PATH_MAX) #if defined(MAXPATHLEN) #define PATH_MAX MAXPATHLEN #else #define PATH_MAX 4096 #endif #endif #if ! defined(NAME_MAX) #define NAME_MAX 255 #endif extern int _tc_dummy_cnt; int _tc_dummyfunc(void); int _tc_dummyfuncv(int a, ...); /************************************************************************************************* * notation of filesystems *************************************************************************************************/ #define MYPATHCHR '/' #define MYPATHSTR "/" #define MYEXTCHR '.' #define MYEXTSTR "." #define MYCDIRSTR "." #define MYPDIRSTR ".." /************************************************************************************************* * for ZLIB *************************************************************************************************/ enum { _TCZMZLIB, _TCZMRAW, _TCZMGZIP }; extern char *(*_tc_deflate)(const char *, int, int *, int); extern char *(*_tc_inflate)(const char *, int, int *, int); extern unsigned int (*_tc_getcrc)(const char *, int); /************************************************************************************************* * for BZIP2 *************************************************************************************************/ extern char *(*_tc_bzcompress)(const char *, int, int *); extern char *(*_tc_bzdecompress)(const char *, int, int *); /************************************************************************************************* * for test of custom codec functions *************************************************************************************************/ void *_tc_recencode(const void *ptr, int size, int *sp, void *op); void *_tc_recdecode(const void *ptr, int size, int *sp, void *op); /************************************************************************************************* * for POSIX thread disability *************************************************************************************************/ #if ! TCUSEPTHREAD #define pthread_t intptr_t #define pthread_once_t intptr_t #undef PTHREAD_ONCE_INIT #define PTHREAD_ONCE_INIT 0 #define pthread_once(TC_a, TC_b) _tc_dummyfuncv((intptr_t)(TC_a), (TC_b)) #define pthread_mutexattr_t intptr_t #undef PTHREAD_MUTEX_RECURSIVE #define PTHREAD_MUTEX_RECURSIVE 0 #define pthread_mutexattr_init(TC_a) _tc_dummyfuncv((intptr_t)(TC_a)) #define pthread_mutexattr_destroy(TC_a) _tc_dummyfuncv((intptr_t)(TC_a)) #define pthread_mutexattr_settype(TC_a, TC_b) _tc_dummyfuncv((intptr_t)(TC_a), (TC_b)) #define pthread_mutex_t intptr_t #undef PTHREAD_MUTEX_INITIALIZER #define PTHREAD_MUTEX_INITIALIZER 0 #define pthread_mutex_init(TC_a, TC_b) _tc_dummyfuncv((intptr_t)(TC_a), (TC_b)) #define pthread_mutex_destroy(TC_a) _tc_dummyfuncv((intptr_t)(TC_a)) #define pthread_mutex_lock(TC_a) _tc_dummyfuncv((intptr_t)(TC_a)) #define pthread_mutex_unlock(TC_a) _tc_dummyfuncv((intptr_t)(TC_a)) #define pthread_rwlock_t intptr_t #undef PTHREAD_RWLOCK_INITIALIZER #define PTHREAD_RWLOCK_INITIALIZER 0 #define pthread_rwlock_init(TC_a, TC_b) _tc_dummyfuncv((intptr_t)(TC_a), (TC_b)) #define pthread_rwlock_destroy(TC_a) _tc_dummyfuncv((intptr_t)(TC_a)) #define pthread_rwlock_rdlock(TC_a) _tc_dummyfuncv((intptr_t)(TC_a)) #define pthread_rwlock_wrlock(TC_a) _tc_dummyfuncv((intptr_t)(TC_a)) #define pthread_rwlock_unlock(TC_a) _tc_dummyfuncv((intptr_t)(TC_a)) #define pthread_key_t intptr_t #define pthread_key_create(TC_a, TC_b) _tc_dummyfuncv((intptr_t)(TC_a), (TC_b)) #define pthread_key_delete(TC_a) _tc_dummyfuncv((intptr_t)(TC_a)) #define pthread_setspecific(TC_a, TC_b) _tc_dummyfuncv((intptr_t)(TC_a)) #define pthread_getspecific(TC_a) _tc_dummyfuncv((intptr_t)(TC_a)) #define pthread_create(TC_th, TC_attr, TC_func, TC_arg) \ (*(TC_th) = 0, (TC_func)(TC_arg), 0) #define pthread_join(TC_th, TC_rv) (*(TC_rv) = NULL, 0) #define pthread_detach(TC_th) 0 #define sched_yield() _tc_dummyfunc() #endif #if TCUSEPTHREAD && TCMICROYIELD #define TCTESTYIELD() \ do { \ if(((++_tc_dummy_cnt) & (0x20 - 1)) == 0){ \ sched_yield(); \ if(_tc_dummy_cnt > 0x1000) _tc_dummy_cnt = (uint32_t)time(NULL) % 0x1000; \ } \ } while(false) #undef assert #define assert(TC_expr) \ do { \ if(!(TC_expr)){ \ fprintf(stderr, "assertion failed: %s\n", #TC_expr); \ abort(); \ } \ TCTESTYIELD(); \ } while(false) #define if(TC_cond) \ if((((++_tc_dummy_cnt) & (0x100 - 1)) != 0 || (sched_yield() * 0) == 0) && (TC_cond)) #define while(TC_cond) \ while((((++_tc_dummy_cnt) & (0x100 - 1)) != 0 || (sched_yield() * 0) == 0) && (TC_cond)) #else #define TCTESTYIELD() \ do { \ } while(false) #endif #if !defined(_POSIX_PRIORITY_SCHEDULING) && TCUSEPTHREAD #define sched_yield() usleep(1000 * 20) #endif /************************************************************************************************* * utilities for implementation *************************************************************************************************/ #define TCNUMBUFSIZ 32 // size of a buffer for a number /* set a buffer for a variable length number */ #define TCSETVNUMBUF(TC_len, TC_buf, TC_num) \ do { \ int _TC_num = (TC_num); \ if(_TC_num == 0){ \ ((signed char *)(TC_buf))[0] = 0; \ (TC_len) = 1; \ } else { \ (TC_len) = 0; \ while(_TC_num > 0){ \ int _TC_rem = _TC_num & 0x7f; \ _TC_num >>= 7; \ if(_TC_num > 0){ \ ((signed char *)(TC_buf))[(TC_len)] = -_TC_rem - 1; \ } else { \ ((signed char *)(TC_buf))[(TC_len)] = _TC_rem; \ } \ (TC_len)++; \ } \ } \ } while(false) /* set a buffer for a variable length number of 64-bit */ #define TCSETVNUMBUF64(TC_len, TC_buf, TC_num) \ do { \ long long int _TC_num = (TC_num); \ if(_TC_num == 0){ \ ((signed char *)(TC_buf))[0] = 0; \ (TC_len) = 1; \ } else { \ (TC_len) = 0; \ while(_TC_num > 0){ \ int _TC_rem = _TC_num & 0x7f; \ _TC_num >>= 7; \ if(_TC_num > 0){ \ ((signed char *)(TC_buf))[(TC_len)] = -_TC_rem - 1; \ } else { \ ((signed char *)(TC_buf))[(TC_len)] = _TC_rem; \ } \ (TC_len)++; \ } \ } \ } while(false) /* read a variable length buffer */ #define TCREADVNUMBUF(TC_buf, TC_num, TC_step) \ do { \ TC_num = 0; \ int _TC_base = 1; \ int _TC_i = 0; \ while(true){ \ if(((signed char *)(TC_buf))[_TC_i] >= 0){ \ TC_num += ((signed char *)(TC_buf))[_TC_i] * _TC_base; \ break; \ } \ TC_num += _TC_base * (((signed char *)(TC_buf))[_TC_i] + 1) * -1; \ _TC_base <<= 7; \ _TC_i++; \ } \ (TC_step) = _TC_i + 1; \ } while(false) /* read a variable length buffer */ #define TCREADVNUMBUF64(TC_buf, TC_num, TC_step) \ do { \ TC_num = 0; \ long long int _TC_base = 1; \ int _TC_i = 0; \ while(true){ \ if(((signed char *)(TC_buf))[_TC_i] >= 0){ \ TC_num += ((signed char *)(TC_buf))[_TC_i] * _TC_base; \ break; \ } \ TC_num += _TC_base * (((signed char *)(TC_buf))[_TC_i] + 1) * -1; \ _TC_base <<= 7; \ _TC_i++; \ } \ (TC_step) = _TC_i + 1; \ } while(false) /* calculate the size of a buffer for a variable length number */ #define TCCALCVNUMSIZE(TC_num) \ ((TC_num) < 0x80 ? 1 : (TC_num) < 0x4000 ? 2 : (TC_num) < 0x200000 ? 3 : \ (TC_num) < 0x10000000 ? 4 : 5) /* compare keys of two records by lexical order */ #define TCCMPLEXICAL(TC_rv, TC_aptr, TC_asiz, TC_bptr, TC_bsiz) \ do { \ (TC_rv) = 0; \ int _TC_min = (TC_asiz) < (TC_bsiz) ? (TC_asiz) : (TC_bsiz); \ for(int _TC_i = 0; _TC_i < _TC_min; _TC_i++){ \ if(((unsigned char *)(TC_aptr))[_TC_i] != ((unsigned char *)(TC_bptr))[_TC_i]){ \ (TC_rv) = ((unsigned char *)(TC_aptr))[_TC_i] - ((unsigned char *)(TC_bptr))[_TC_i]; \ break; \ } \ } \ if((TC_rv) == 0) (TC_rv) = (TC_asiz) - (TC_bsiz); \ } while(false) #endif // duplication check // END OF FILE tokyocabinet-1.4.48/tokyocabinet.idl0000644000175000017500000002741612013574446016512 0ustar mikiomikio/************************************************************************************************* * IDL for bindings of scripting languages * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ /** * namespace of Tokyo Cabinet */ module tokyocabinet { //---------------------------------------------------------------- // list of strings (substituted for by the native mechanism) //---------------------------------------------------------------- interface List { string get(in long index); }; //---------------------------------------------------------------- // map of strings (substituted for by the native mechanism) //---------------------------------------------------------------- interface Map { string get(in string key); }; //---------------------------------------------------------------- // the error codes //---------------------------------------------------------------- interface ECODE { const long ESUCCESS = 0; const long ETHREAD = 1; const long EINVALID = 2; const long ENOFILE = 3; const long ENOPERM = 4; const long EMETA = 5; const long ERHEAD = 6; const long EOPEN = 7; const long ECLOSE = 8; const long ETRUNC = 9; const long ESYNC = 10; const long ESTAT = 11; const long ESEEK = 12; const long EREAD = 13; const long EWRITE = 14; const long EMMAP = 15; const long ELOCK = 16; const long EUNLINK = 17; const long ERENAME = 18; const long EMKDIR = 19; const long ERMDIR = 20; const long EKEEP = 21; const long ENOREC = 22; const long EMISC = 9999; long ecode(); string errmsg(in long ecode); }; //---------------------------------------------------------------- // the hash database API //---------------------------------------------------------------- interface HDB :ECODE { const long TLARGE = 1 << 0; const long TDEFLATE = 1 << 1; const long TBZIP = 1 << 2; const long TTCBS = 1 << 3; const long OREADER = 1 << 0; const long OWRITER = 1 << 1; const long OCREAT = 1 << 2; const long OTRUNC = 1 << 3; const long ONOLCK = 1 << 4; const long OLCKNB = 1 << 5; const long OTSYNC = 1 << 6; boolean tune(in long long bnum, in long apow, in long fpow, in long opts); boolean setcache(in long rcnum); boolean setxmsiz(in long long xmsiz); boolean setdfunit(in long dfunit); boolean open(in string path, in long omode); boolean close(); boolean put(in string key, in string value); boolean putkeep(in string key, in string value); boolean putcat(in string key, in string value); boolean putasync(in string key, in string value); boolean out(in string key); string get(in string key); long vsiz(in string key); boolean iterinit(); string iternext(); List fwmkeys(in string prefix, in long max); long addint(in string key, in long num); double adddouble(in string key, in double num); boolean sync(); boolean optimize(in long long bnum, in long apow, in long fpow, in long opts); boolean vanish(); boolean copy(in string path); boolean tranbegin(); boolean trancommit(); boolean tranabort(); string path(); long long rnum(); long long fsiz(); }; //---------------------------------------------------------------- // the B+ tree database API //---------------------------------------------------------------- interface BDB :ECODE { const long TLARGE = 1 << 0; const long TDEFLATE = 1 << 1; const long TBZIP = 1 << 2; const long TTCBS = 1 << 3; const long OREADER = 1 << 0; const long OWRITER = 1 << 1; const long OCREAT = 1 << 2; const long OTRUNC = 1 << 3; const long ONOLCK = 1 << 4; const long OLCKNB = 1 << 5; const long OTSYNC = 1 << 6; boolean tune(in long lmemb, in long nmemb, in long long bnum, in long apow, in long fpow, in long opts); boolean setcache(in long lcnum, in long ncnum); boolean setxmsiz(in long long xmsiz); boolean setdfunit(in long dfunit); boolean open(in string path, in long omode); boolean close(); boolean put(in string key, in string value); boolean putkeep(in string key, in string value); boolean putcat(in string key, in string value); boolean putdup(in string key, in string value); boolean putlist(in string key, in List values); boolean out(in string key); boolean outlist(in string key); string get(in string key); List getlist(in string key); long vnum(in string key); long vsiz(in string key); List range(in string bkey, in boolean binc, in string ekey, in boolean einc, in long max); List fwmkeys(in string prefix, in long max); long addint(in string key, in long num); double adddouble(in string key, in double num); boolean sync(); boolean optimize(in long lmemb, in long nmemb, in long long bnum, in long apow, in long fpow, in long opts); boolean vanish(); boolean copy(in string path); boolean tranbegin(); boolean trancommit(); boolean tranabort(); string path(); long long rnum(); long long fsiz(); }; //---------------------------------------------------------------- // the B+ tree cursor API //---------------------------------------------------------------- interface BDBCUR { const long CPCURRENT = 0; const long CPBEFORE = 1; const long CPAFTER = 2; boolean first(); boolean last(); boolean jump(in string key); boolean prev(); boolean next(); boolean put(in string value, in long cpmode); boolean out(); string key(); string val(); }; //---------------------------------------------------------------- // the fixed-length database API //---------------------------------------------------------------- interface FDB :ECODE { const long OREADER = 1 << 0; const long OWRITER = 1 << 1; const long OCREAT = 1 << 2; const long OTRUNC = 1 << 3; const long ONOLCK = 1 << 4; const long OLCKNB = 1 << 5; const long OTSYNC = 1 << 6; boolean tune(in long width, in long long limsiz); boolean open(in string path, in long omode); boolean close(); boolean put(in string key, in string value); boolean putkeep(in string key, in string value); boolean putcat(in string key, in string value); boolean out(in string key); string get(in string key); long vsiz(in string key); boolean iterinit(); string iternext(); List range(in string interval, in long max); long addint(in string key, in long num); double adddouble(in string key, in double num); boolean sync(); boolean optimize(in long width, in long long limsiz); boolean vanish(); boolean copy(in string path); boolean tranbegin(); boolean trancommit(); boolean tranabort(); string path(); long long rnum(); long long fsiz(); }; //---------------------------------------------------------------- // the table database API //---------------------------------------------------------------- interface TDB :ECODE { const long TLARGE = 1 << 0; const long TDEFLATE = 1 << 1; const long TBZIP = 1 << 2; const long TTCBS = 1 << 3; const long OREADER = 1 << 0; const long OWRITER = 1 << 1; const long OCREAT = 1 << 2; const long OTRUNC = 1 << 3; const long ONOLCK = 1 << 4; const long OLCKNB = 1 << 5; const long OTSYNC = 1 << 6; const long ITLEXICAL = 0; const long ITDECIMAL = 1; const long ITTOKEN = 2; const long ITQGRAM = 3; const long ITOPT = 9998; const long ITVOID = 9999; const long ITKEEP = 1 << 24; boolean tune(in long long bnum, in long apow, in long fpow, in long opts); boolean setcache(in long rcnum, in long lcnum, in long ncnum); boolean setxmsiz(in long long xmsiz); boolean setdfunit(in long dfunit); boolean open(in string path, in long omode); boolean close(); boolean put(in string pkey, in Map cols); boolean putkeep(in string pkey, in Map cols); boolean putcat(in string pkey, in Map cols); boolean out(in string pkey); Map get(in string pkey); long vsiz(in string pkey); boolean iterinit(); string iternext(); List fwmkeys(in string prefix, in long max); long addint(in string pkey, in long num); double adddouble(in string pkey, in double num); boolean sync(); boolean optimize(in long long bnum, in long apow, in long fpow, in long opts); boolean vanish(); boolean copy(in string path); boolean tranbegin(); boolean trancommit(); boolean tranabort(); string path(); long long rnum(); long long fsiz(); boolean setindex(in string name, in long type); long long genuid(); }; //---------------------------------------------------------------- // the table query API //---------------------------------------------------------------- interface TDBQRY { const long QCSTREQ = 0; const long QCSTRINC = 1; const long QCSTRBW = 2; const long QCSTREW = 3; const long QCSTRAND = 4; const long QCSTROR = 5; const long QCSTROREQ = 6; const long QCSTRRX = 7; const long QCNUMEQ = 8; const long QCNUMGT = 9; const long QCNUMGE = 10; const long QCNUMLT = 11; const long QCNUMLE = 12; const long QCNUMBT = 13; const long QCNUMOREQ = 14; const long QCFTSPH = 15; const long QCFTSAND = 16; const long QCFTSOR = 17; const long QCFTSEX = 18; const long QCNEGATE = 1 << 24; const long QCNOIDX = 1 << 25; const long QOSTRASC = 0; const long QOSTRDESC = 1; const long QONUMASC = 2; const long QONUMDESC = 3; const long MSUNION = 0; const long MSISECT = 1; const long MSDIFF = 2; void addcond(in string name, in long op, in string expr); void setorder(in string name, in long type); void setlimit(in long max, in long skip); List search(); boolean searchout(); string hint(); typedef sequence QueryList; List metasearch(in QueryList qrys, in long type); }; //---------------------------------------------------------------- // the abstract database API //---------------------------------------------------------------- interface ADB { boolean open(in string name); boolean close(); boolean put(in string key, in string value); boolean putkeep(in string key, in string value); boolean putcat(in string key, in string value); boolean out(in string key); string get(in string key); long vsiz(in string key); boolean iterinit(); string iternext(); List fwmkeys(in string prefix, in long max); long addint(in string key, in long num); double adddouble(in string key, in double num); boolean sync(); boolean optimize(in string params); boolean vanish(); boolean copy(in string path); boolean tranbegin(); boolean trancommit(); boolean tranabort(); string path(); long long rnum(); long long size(); List misc(in string name, in List args); }; }; /* END OF FILE */ tokyocabinet-1.4.48/man/0000755000175000017500000000000012013574114014056 5ustar mikiomikiotokyocabinet-1.4.48/man/tcfmgr.10000644000175000017500000000576712013574114015441 0ustar mikiomikio.TH "TCFMGR" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcfmgr \- the command line utility of the fixed-length database API .SH DESCRIPTION .PP The command `\fBtcfmgr\fR' is a utility for test and debugging of the fixed\-length database API and its applications. `\fIpath\fR' specifies the path of a database file. `\fIwidth\fR' specifies the width of the value of each record. `\fIlimsiz\fR' specifies the limit size of the database file. `\fIkey\fR' specifies the key of a record. `\fIvalue\fR' specifies the value of a record. `\fIfile\fR' specifies the input file. .PP .RS .br \fBtcfmgr create \fIpath\fB \fR[\fB\fIwidth\fB \fR[\fB\fIlimsiz\fB\fR]\fB\fR]\fB\fR .RS Create a database file. .RE .br \fBtcfmgr inform \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB\fR .RS Print miscellaneous information to the standard output. .RE .br \fBtcfmgr put \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sx\fR]\fB \fR[\fB\-dk\fR|\fB\-dc\fR|\fB\-dai\fR|\fB\-dad\fR]\fB \fIpath\fB \fIkey\fB \fIvalue\fB\fR .RS Store a record. .RE .br \fBtcfmgr out \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sx\fR]\fB \fIpath\fB \fIkey\fB\fR .RS Remove a record. .RE .br \fBtcfmgr get \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sx\fR]\fB \fR[\fB\-px\fR]\fB \fR[\fB\-pz\fR]\fB \fIpath\fB \fIkey\fB\fR .RS Print the value of a record. .RE .br \fBtcfmgr list \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-m \fInum\fB\fR]\fB \fR[\fB\-pv\fR]\fB \fR[\fB\-px\fR]\fB \fR[\fB\-rb \fIlkey\fB \fIukey\fB\fR]\fB \fR[\fB\-ri \fIstr\fB\fR]\fB \fIpath\fB\fR .RS Print keys of all records, separated by line feeds. .RE .br \fBtcfmgr optimize \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB \fR[\fB\fIwidth\fB \fR[\fB\fIlimsiz\fB\fR]\fB\fR]\fB\fR .RS Optimize a database file. .RE .br \fBtcfmgr importtsv \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sc\fR]\fB \fIpath\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Store records of TSV in each line of a file. .RE .br \fBtcfmgr version\fR .RS Print the version information of Tokyo Cabinet. .RE .RE .PP Options feature the following. .PP .RS \fB\-nl\fR : enable the option `FDBNOLCK'. .br \fB\-nb\fR : enable the option `FDBLCKNB'. .br \fB\-sx\fR : the input data is evaluated as a hexadecimal data string. .br \fB\-dk\fR : use the function `tcfdbputkeep' instead of `tcfdbput'. .br \fB\-dc\fR : use the function `tcfdbputcat' instead of `tcfdbput'. .br \fB\-dai\fR : use the function `tcfdbaddint' instead of `tcfdbput'. .br \fB\-dad\fR : use the function `tcfdbadddouble' instead of `tcfdbput'. .br \fB\-px\fR : the output data is converted into a hexadecimal data string. .br \fB\-pz\fR : do not append line feed at the end of the output. .br \fB\-m \fInum\fR\fR : specify the maximum number of the output. .br \fB\-pv\fR : print values of records also. .br \fB\-rb \fIlkey\fR \fIukey\fR\fR : specify the range of keys. .br \fB\-ri \fIstr\fR\fR : specify the interval notation of keys. .br \fB\-sc\fR : normalize keys as lower cases. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tcftest (1), .BR tcfmttest (1), .BR tcfdb (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tcbdb.30000644000175000017500000013145112013574114015225 0ustar mikiomikio.TH "TCBDB" 3 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcbdb \- the B+ tree database API .SH DESCRIPTION .PP B+ tree database is a file containing a B+ tree and is handled with the B+ tree database API. .PP To use the B+ tree database API, include `\fBtcutil.h\fR', `\fBtcbdb.h\fR', and related standard header files. Usually, write the following description near the front of a source file. .PP .RS .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .RE .PP Objects whose type is pointer to `\fBTCBDB\fR' are used to handle B+ tree databases. A B+ tree database object is created with the function `\fBtcbdbnew\fR' and is deleted with the function `\fBtcbdbdel\fR'. To avoid memory leak, it is important to delete every object when it is no longer in use. .PP 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 `\fBtcbdbopen\fR' is used to open a database file and the function `\fBtcbdbclose\fR' 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. .SH API .PP The function `tcbdberrmsg' is used in order to get the message string corresponding to an error code. .PP .RS .br \fBconst char *tcbdberrmsg(int \fIecode\fB);\fR .RS `\fIecode\fR' specifies the error code. .RE .RS The return value is the message string of the error code. .RE .RE .PP The function `tcbdbnew' is used in order to create a B+ tree database object. .PP .RS .br \fBTCBDB *tcbdbnew(void);\fR .RS The return value is the new B+ tree database object. .RE .RE .PP The function `tcbdbdel' is used in order to delete a B+ tree database object. .PP .RS .br \fBvoid tcbdbdel(TCBDB *\fIbdb\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS If the database is not closed, it is closed implicitly. Note that the deleted object and its derivatives can not be used anymore. .RE .RE .PP The function `tcbdbecode' is used in order to get the last happened error code of a B+ tree database object. .PP .RS .br \fBint tcbdbecode(TCBDB *\fIbdb\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS The return value is the last happened error code. .RE .RS 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. .RE .RE .PP The function `tcbdbsetmutex' is used in order to set mutual exclusion control of a B+ tree database object for threading. .PP .RS .br \fBbool tcbdbsetmutex(TCBDB *\fIbdb\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object which is not opened. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the mutual exclusion control of the database should be set before the database is opened. .RE .RE .PP The function `tcbdbsetcmpfunc' is used in order to set the custom comparison function of a B+ tree database object. .PP .RS .br \fBbool tcbdbsetcmpfunc(TCBDB *\fIbdb\fB, TCCMP \fIcmp\fB, void *\fIcmpop\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object which is not opened. .RE .RS `\fIcmp\fR' 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. .RE .RS `\fIcmpop\fR' specifies an arbitrary pointer to be given as a parameter of the comparison function. If it is not needed, `NULL' can be specified. .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tcbdbtune' is used in order to set the tuning parameters of a B+ tree database object. .PP .RS .br \fBbool tcbdbtune(TCBDB *\fIbdb\fB, int32_t \fIlmemb\fB, int32_t \fInmemb\fB, int64_t \fIbnum\fB, int8_t \fIapow\fB, int8_t \fIfpow\fB, uint8_t \fIopts\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object which is not opened. .RE .RS `\fIlmemb\fR' 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. .RE .RS `\fInmemb\fR' 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. .RE .RS `\fIbnum\fR' 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 16381. Suggested size of the bucket array is about from 1 to 4 times of the number of all pages to be stored. .RE .RS `\fIapow\fR' 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. .RE .RS `\fIfpow\fR' 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. .RE .RS `\fIopts\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the tuning parameters should be set before the database is opened. .RE .RE .PP The function `tcbdbsetcache' is used in order to set the caching parameters of a B+ tree database object. .PP .RS .br \fBbool tcbdbsetcache(TCBDB *\fIbdb\fB, int32_t \fIlcnum\fB, int32_t \fIncnum\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object which is not opened. .RE .RS `\fIlcnum\fR' 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. .RE .RS `\fIncnum\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the caching parameters should be set before the database is opened. .RE .RE .PP The function `tcbdbsetxmsiz' is used in order to set the size of the extra mapped memory of a B+ tree database object. .PP .RS .br \fBbool tcbdbsetxmsiz(TCBDB *\fIbdb\fB, int64_t \fIxmsiz\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object which is not opened. .RE .RS `\fIxmsiz\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the mapping parameters should be set before the database is opened. .RE .RE .PP The function `tcbdbsetdfunit' is used in order to set the unit step number of auto defragmentation of a B+ tree database object. .PP .RS .br \fBbool tcbdbsetdfunit(TCBDB *\fIbdb\fB, int32_t \fIdfunit\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object which is not opened. .RE .RS `\fIdfunit\fR' specifie the unit step number. If it is not more than 0, the auto defragmentation is disabled. It is disabled by default. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the defragmentation parameter should be set before the database is opened. .RE .RE .PP The function `tcbdbopen' is used in order to open a database file and connect a B+ tree database object. .PP .RS .br \fBbool tcbdbopen(TCBDB *\fIbdb\fB, const char *\fIpath\fB, int \fIomode\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object which is not opened. .RE .RS `\fIpath\fR' specifies the path of the database file. .RE .RS `\fIomode\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcbdbclose' is used in order to close a B+ tree database object. .PP .RS .br \fBbool tcbdbclose(TCBDB *\fIbdb\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tcbdbput' is used in order to store a record into a B+ tree database object. .PP .RS .br \fBbool tcbdbput(TCBDB *\fIbdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcbdbput2' is used in order to store a string record into a B+ tree database object. .PP .RS .br \fBbool tcbdbput2(TCBDB *\fIbdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcbdbputkeep' is used in order to store a new record into a B+ tree database object. .PP .RS .br \fBbool tcbdbputkeep(TCBDB *\fIbdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tcbdbputkeep2' is used in order to store a new string record into a B+ tree database object. .PP .RS .br \fBbool tcbdbputkeep2(TCBDB *\fIbdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tcbdbputcat' is used in order to concatenate a value at the end of the existing record in a B+ tree database object. .PP .RS .br \fBbool tcbdbputcat(TCBDB *\fIbdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP 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. .PP .RS .br \fBbool tcbdbputcat2(TCBDB *\fIbdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcbdbputdup' is used in order to store a record into a B+ tree database object with allowing duplication of keys. .PP .RS .br \fBbool tcbdbputdup(TCBDB *\fIbdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, the new record is placed after the existing one. .RE .RE .PP The function `tcbdbputdup2' is used in order to store a string record into a B+ tree database object with allowing duplication of keys. .PP .RS .br \fBbool tcbdbputdup2(TCBDB *\fIbdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, the new record is placed after the existing one. .RE .RE .PP The function `tcbdbputdup3' is used in order to store records into a B+ tree database object with allowing duplication of keys. .PP .RS .br \fBbool tcbdbputdup3(TCBDB *\fIbdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const TCLIST *\fIvals\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the common key. .RE .RS `\fIksiz\fR' specifies the size of the region of the common key. .RE .RS `\fIvals\fR' specifies a list object containing values. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, the new records are placed after the existing one. .RE .RE .PP The function `tcbdbout' is used in order to remove a record of a B+ tree database object. .PP .RS .br \fBbool tcbdbout(TCBDB *\fIbdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If the key of duplicated records is specified, the first one is selected. .RE .RE .PP The function `tcbdbout2' is used in order to remove a string record of a B+ tree database object. .PP .RS .br \fBbool tcbdbout2(TCBDB *\fIbdb\fB, const char *\fIkstr\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If the key of duplicated records is specified, the first one is selected. .RE .RE .PP The function `tcbdbout3' is used in order to remove records of a B+ tree database object. .PP .RS .br \fBbool tcbdbout3(TCBDB *\fIbdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If the key of duplicated records is specified, all of them are removed. .RE .RE .PP The function `tcbdbget' is used in order to retrieve a record in a B+ tree database object. .PP .RS .br \fBvoid *tcbdbget(TCBDB *\fIbdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int *\fIsp\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcbdbget2' is used in order to retrieve a string record in a B+ tree database object. .PP .RS .br \fBchar *tcbdbget2(TCBDB *\fIbdb\fB, const char *\fIkstr\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is the string of the value of the corresponding record. `NULL' is returned if no record corresponds. .RE .RS 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. .RE .RE .PP The function `tcbdbget3' is used in order to retrieve a record in a B+ tree database object as a volatile buffer. .PP .RS .br \fBconst void *tcbdbget3(TCBDB *\fIbdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int *\fIsp\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcbdbget4' is used in order to retrieve records in a B+ tree database object. .PP .RS .br \fBTCLIST *tcbdbget4(TCBDB *\fIbdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is a list object of the values of the corresponding records. `NULL' is returned if no record corresponds. .RE .RS 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. .RE .RE .PP The function `tcbdbvnum' is used in order to get the number of records corresponding a key in a B+ tree database object. .PP .RS .br \fBint tcbdbvnum(TCBDB *\fIbdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is the number of the corresponding records, else, it is 0. .RE .RE .PP The function `tcbdbvnum2' is used in order to get the number of records corresponding a string key in a B+ tree database object. .PP .RS .br \fBint tcbdbvnum2(TCBDB *\fIbdb\fB, const char *\fIkstr\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is the number of the corresponding records, else, it is 0. .RE .RE .PP The function `tcbdbvsiz' is used in order to get the size of the value of a record in a B+ tree database object. .PP .RS .br \fBint tcbdbvsiz(TCBDB *\fIbdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RS If the key of duplicated records is specified, the first one is selected. .RE .RE .PP The function `tcbdbvsiz2' is used in order to get the size of the value of a string record in a B+ tree database object. .PP .RS .br \fBint tcbdbvsiz2(TCBDB *\fIbdb\fB, const char *\fIkstr\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RS If the key of duplicated records is specified, the first one is selected. .RE .RE .PP The function `tcbdbrange' is used in order to get keys of ranged records in a B+ tree database object. .PP .RS .br \fBTCLIST *tcbdbrange(TCBDB *\fIbdb\fB, const void *\fIbkbuf\fB, int \fIbksiz\fB, bool \fIbinc\fB, const void *\fIekbuf\fB, int \fIeksiz\fB, bool \fIeinc\fB, int \fImax\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS `\fIbkbuf\fR' specifies the pointer to the region of the key of the beginning border. If it is `NULL', the first record is specified. .RE .RS `\fIbksiz\fR' specifies the size of the region of the beginning key. .RE .RS `\fIbinc\fR' specifies whether the beginning border is inclusive or not. .RE .RS `\fIekbuf\fR' specifies the pointer to the region of the key of the ending border. If it is `NULL', the last record is specified. .RE .RS `\fIeksiz\fR' specifies the size of the region of the ending key. .RE .RS `\fIeinc\fR' specifies whether the ending border is inclusive or not. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcbdbrange2' is used in order to get string keys of ranged records in a B+ tree database object. .PP .RS .br \fBTCLIST *tcbdbrange2(TCBDB *\fIbdb\fB, const char *\fIbkstr\fB, bool \fIbinc\fB, const char *\fIekstr\fB, bool \fIeinc\fB, int \fImax\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS `\fIbkstr\fR' specifies the string of the key of the beginning border. If it is `NULL', the first record is specified. .RE .RS `\fIbinc\fR' specifies whether the beginning border is inclusive or not. .RE .RS `\fIekstr\fR' specifies the string of the key of the ending border. If it is `NULL', the last record is specified. .RE .RS `\fIeinc\fR' specifies whether the ending border is inclusive or not. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcbdbfwmkeys' is used in order to get forward matching keys in a B+ tree database object. .PP .RS .br \fBTCLIST *tcbdbfwmkeys(TCBDB *\fIbdb\fB, const void *\fIpbuf\fB, int \fIpsiz\fB, int \fImax\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS `\fIpbuf\fR' specifies the pointer to the region of the prefix. .RE .RS `\fIpsiz\fR' specifies the size of the region of the prefix. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcbdbfwmkeys2' is used in order to get forward matching string keys in a B+ tree database object. .PP .RS .br \fBTCLIST *tcbdbfwmkeys2(TCBDB *\fIbdb\fB, const char *\fIpstr\fB, int \fImax\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS `\fIpstr\fR' specifies the string of the prefix. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcbdbaddint' is used in order to add an integer to a record in a B+ tree database object. .PP .RS .br \fBint tcbdbaddint(TCBDB *\fIbdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int \fInum\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS If successful, the return value is the summation value, else, it is `INT_MIN'. .RE .RS 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. .RE .RE .PP The function `tcbdbadddouble' is used in order to add a real number to a record in a B+ tree database object. .PP .RS .br \fBdouble tcbdbadddouble(TCBDB *\fIbdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, double \fInum\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS If successful, the return value is the summation value, else, it is Not-a-Number. .RE .RS 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. .RE .RE .PP The function `tcbdbsync' is used in order to synchronize updated contents of a B+ tree database object with the file and the device. .PP .RS .br \fBbool tcbdbsync(TCBDB *\fIbdb\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS This function is useful when another process connects to the same database file. .RE .RE .PP The function `tcbdboptimize' is used in order to optimize the file of a B+ tree database object. .PP .RS .br \fBbool tcbdboptimize(TCBDB *\fIbdb\fB, int32_t \fIlmemb\fB, int32_t \fInmemb\fB, int64_t \fIbnum\fB, int8_t \fIapow\fB, int8_t \fIfpow\fB, uint8_t \fIopts\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS `\fIlmemb\fR' specifies the number of members in each leaf page. If it is not more than 0, the current setting is not changed. .RE .RS `\fInmemb\fR' specifies the number of members in each non\-leaf page. If it is not more than 0, the current setting is not changed. .RE .RS `\fIbnum\fR' 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. .RE .RS `\fIapow\fR' specifies the size of record alignment by power of 2. If it is negative, the current setting is not changed. .RE .RS `\fIfpow\fR' 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. .RE .RS `\fIopts\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS This function is useful to reduce the size of the database file with data fragmentation by successive updating. .RE .RE .PP The function `tcbdbvanish' is used in order to remove all records of a B+ tree database object. .PP .RS .br \fBbool tcbdbvanish(TCBDB *\fIbdb\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcbdbcopy' is used in order to copy the database file of a B+ tree database object. .PP .RS .br \fBbool tcbdbcopy(TCBDB *\fIbdb\fB, const char *\fIpath\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS `\fIpath\fR' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line. .RE .RS If successful, the return value is true, else, it is false. False is returned if the executed command returns non\-zero code. .RE .RS 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. .RE .RE .PP The function `tcbdbtranbegin' is used in order to begin the transaction of a B+ tree database object. .PP .RS .br \fBbool tcbdbtranbegin(TCBDB *\fIbdb\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tcbdbtrancommit' is used in order to commit the transaction of a B+ tree database object. .PP .RS .br \fBbool tcbdbtrancommit(TCBDB *\fIbdb\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Update in the transaction is fixed when it is committed successfully. .RE .RE .PP The function `tcbdbtranabort' is used in order to abort the transaction of a B+ tree database object. .PP .RS .br \fBbool tcbdbtranabort(TCBDB *\fIbdb\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Update in the transaction is discarded when it is aborted. The state of the database is rollbacked to before transaction. .RE .RE .PP The function `tcbdbpath' is used in order to get the file path of a B+ tree database object. .PP .RS .br \fBconst char *tcbdbpath(TCBDB *\fIbdb\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS The return value is the path of the database file or `NULL' if the object does not connect to any database file. .RE .RE .PP The function `tcbdbrnum' is used in order to get the number of records of a B+ tree database object. .PP .RS .br \fBuint64_t tcbdbrnum(TCBDB *\fIbdb\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS The return value is the number of records or 0 if the object does not connect to any database file. .RE .RE .PP The function `tcbdbfsiz' is used in order to get the size of the database file of a B+ tree database object. .PP .RS .br \fBuint64_t tcbdbfsiz(TCBDB *\fIbdb\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS The return value is the size of the database file or 0 if the object does not connect to any database file. .RE .RE .PP The function `tcbdbcurnew' is used in order to create a cursor object. .PP .RS .br \fBBDBCUR *tcbdbcurnew(TCBDB *\fIbdb\fB);\fR .RS `\fIbdb\fR' specifies the B+ tree database object. .RE .RS The return value is the new cursor object. .RE .RS 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. .RE .RE .PP The function `tcbdbcurdel' is used in order to delete a cursor object. .PP .RS .br \fBvoid tcbdbcurdel(BDBCUR *\fIcur\fB);\fR .RS `\fIcur\fR' specifies the cursor object. .RE .RE .PP The function `tcbdbcurfirst' is used in order to move a cursor object to the first record. .PP .RS .br \fBbool tcbdbcurfirst(BDBCUR *\fIcur\fB);\fR .RS `\fIcur\fR' specifies the cursor object. .RE .RS If successful, the return value is true, else, it is false. False is returned if there is no record in the database. .RE .RE .PP The function `tcbdbcurlast' is used in order to move a cursor object to the last record. .PP .RS .br \fBbool tcbdbcurlast(BDBCUR *\fIcur\fB);\fR .RS `\fIcur\fR' specifies the cursor object. .RE .RS If successful, the return value is true, else, it is false. False is returned if there is no record in the database. .RE .RE .PP The function `tcbdbcurjump' is used in order to move a cursor object to the front of records corresponding a key. .PP .RS .br \fBbool tcbdbcurjump(BDBCUR *\fIcur\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIcur\fR' specifies the cursor object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is true, else, it is false. False is returned if there is no record corresponding the condition. .RE .RS The cursor is set to the first record corresponding the key or the next substitute if completely matching record does not exist. .RE .RE .PP The function `tcbdbcurjump2' is used in order to move a cursor object to the front of records corresponding a key string. .PP .RS .br \fBbool tcbdbcurjump2(BDBCUR *\fIcur\fB, const char *\fIkstr\fB);\fR .RS `\fIcur\fR' specifies the cursor object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is true, else, it is false. False is returned if there is no record corresponding the condition. .RE .RS The cursor is set to the first record corresponding the key or the next substitute if completely matching record does not exist. .RE .RE .PP The function `tcbdbcurprev' is used in order to move a cursor object to the previous record. .PP .RS .br \fBbool tcbdbcurprev(BDBCUR *\fIcur\fB);\fR .RS `\fIcur\fR' specifies the cursor object. .RE .RS If successful, the return value is true, else, it is false. False is returned if there is no previous record. .RE .RE .PP The function `tcbdbcurnext' is used in order to move a cursor object to the next record. .PP .RS .br \fBbool tcbdbcurnext(BDBCUR *\fIcur\fB);\fR .RS `\fIcur\fR' specifies the cursor object. .RE .RS If successful, the return value is true, else, it is false. False is returned if there is no next record. .RE .RE .PP The function `tcbdbcurput' is used in order to insert a record around a cursor object. .PP .RS .br \fBbool tcbdbcurput(BDBCUR *\fIcur\fB, const void *\fIvbuf\fB, int \fIvsiz\fB, int \fIcpmode\fB);\fR .RS `\fIcur\fR' specifies the cursor object of writer connection. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS `\fIcpmode\fR' 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. .RE .RS If successful, the return value is true, else, it is false. False is returned when the cursor is at invalid position. .RE .RS After insertion, the cursor is moved to the inserted record. .RE .RE .PP The function `tcbdbcurput2' is used in order to insert a string record around a cursor object. .PP .RS .br \fBbool tcbdbcurput2(BDBCUR *\fIcur\fB, const char *\fIvstr\fB, int \fIcpmode\fB);\fR .RS `\fIcur\fR' specifies the cursor object of writer connection. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS `\fIcpmode\fR' 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. .RE .RS If successful, the return value is true, else, it is false. False is returned when the cursor is at invalid position. .RE .RS After insertion, the cursor is moved to the inserted record. .RE .RE .PP The function `tcbdbcurout' is used in order to remove the record where a cursor object is. .PP .RS .br \fBbool tcbdbcurout(BDBCUR *\fIcur\fB);\fR .RS `\fIcur\fR' specifies the cursor object of writer connection. .RE .RS If successful, the return value is true, else, it is false. False is returned when the cursor is at invalid position. .RE .RS After deletion, the cursor is moved to the next record if possible. .RE .RE .PP The function `tcbdbcurkey' is used in order to get the key of the record where the cursor object is. .PP .RS .br \fBchar *tcbdbcurkey(BDBCUR *\fIcur\fB, int *\fIsp\fB);\fR .RS `\fIcur\fR' specifies the cursor object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcbdbcurkey2' is used in order to get the key string of the record where the cursor object is. .PP .RS .br \fBchar *tcbdbcurkey2(BDBCUR *\fIcur\fB);\fR .RS `\fIcur\fR' specifies the cursor object. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcbdbcurkey3' is used in order to get the key of the record where the cursor object is, as a volatile buffer. .PP .RS .br \fBconst char *tcbdbcurkey3(BDBCUR *\fIcur\fB, int *\fIsp\fB);\fR .RS `\fIcur\fR' specifies the cursor object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcbdbcurval' is used in order to get the value of the record where the cursor object is. .PP .RS .br \fBchar *tcbdbcurval(BDBCUR *\fIcur\fB, int *\fIsp\fB);\fR .RS `\fIcur\fR' specifies the cursor object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcbdbcurval2' is used in order to get the value string of the record where the cursor object is. .PP .RS .br \fBchar *tcbdbcurval2(BDBCUR *\fIcur\fB);\fR .RS `\fIcur\fR' specifies the cursor object. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcbdbcurval3' is used in order to get the value of the record where the cursor object is, as a volatile buffer. .PP .RS .br \fBconst char *tcbdbcurval3(BDBCUR *\fIcur\fB, int *\fIsp\fB);\fR .RS `\fIcur\fR' specifies the cursor object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcbdbcurrec' is used in order to get the key and the value of the record where the cursor object is. .PP .RS .br \fBbool tcbdbcurrec(BDBCUR *\fIcur\fB, TCXSTR *\fIkxstr\fB, TCXSTR *\fIvxstr\fB);\fR .RS `\fIcur\fR' specifies the cursor object. .RE .RS `\fIkxstr\fR' specifies the object into which the key is wrote down. .RE .RS `\fIvxstr\fR' specifies the object into which the value is wrote down. .RE .RS If successful, the return value is true, else, it is false. False is returned when the cursor is at invalid position. .RE .RE .SH SEE ALSO .PP .BR tcbtest (1), .BR tcbmttest (1), .BR tcbmgr (1), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tcutest.10000644000175000017500000000447112013574114015641 0ustar mikiomikio.TH "TCUTEST" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcutest \- test cases of the utility API .SH DESCRIPTION .PP The command `\fBtcutest\fR' is a utility for facility test and performance test. This command is used in the following format. `\fIrnum\fR' specifies the number of iterations. `\fIanum\fR' specifies the initial number of elements of array. `\fIbnum\fR' specifies the number of buckets. .PP .RS .br \fBtcutest xstr \fIrnum\fB\fR .RS Perform test of extensible string. .RE .br \fBtcutest list \fR[\fB\-rd\fR]\fB \fIrnum\fB \fR[\fB\fIanum\fB\fR]\fB\fR .RS Perform test of array list. .RE .br \fBtcutest map \fR[\fB\-rd\fR]\fB \fR[\fB\-tr\fR]\fB \fR[\fB\-rnd\fR]\fB \fR[\fB\-dk\fR|\fB\-dc\fR|\fB\-dai\fR|\fB\-dad\fR|\fB\-dpr\fR]\fB \fIrnum\fB \fR[\fB\fIbnum\fB\fR]\fB\fR .RS Perform test of hash map. .RE .br \fBtcutest tree \fR[\fB\-rd\fR]\fB \fR[\fB\-tr\fR]\fB \fR[\fB\-rnd\fR]\fB \fR[\fB\-dk\fR|\fB\-dc\fR|\fB\-dai\fR|\fB\-dad\fR|\fB\-dpr\fR]\fB \fIrnum\fB\fR .RS Perform test of ordered tree. .RE .br \fBtcutest mdb \fR[\fB\-rd\fR]\fB \fR[\fB\-tr\fR]\fB \fR[\fB\-rnd\fR]\fB \fR[\fB\-dk\fR|\fB\-dc\fR|\fB\-dai\fR|\fB\-dad\fR|\fB\-dpr\fR]\fB \fIrnum\fB \fR[\fB\fIbnum\fB\fR]\fB\fR .RS Perform test of on\-memory hash database. .RE .br \fBtcutest ndb \fR[\fB\-rd\fR]\fB \fR[\fB\-tr\fR]\fB \fR[\fB\-rnd\fR]\fB \fR[\fB\-dk\fR|\fB\-dc\fR|\fB\-dai\fR|\fB\-dad\fR|\fB\-dpr\fR]\fB \fIrnum\fB\fR .RS Perform test of on\-memory tree database. .RE .br \fBtcutest misc \fIrnum\fB\fR .RS Perform test of miscellaneous routines. .RE .br \fBtcutest wicked \fIrnum\fB\fR .RS Perform updating operations of list and map selected at random. .RE .RE .PP Options feature the following. .PP .RS \fB\-rd\fR : perform the reading test also. .br \fB\-tr\fR : perform the iterator test also. .br \fB\-rnd\fR : select keys at random. .br \fB\-dk\fR : use the function `tcxxxputkeep' instead of `tcxxxput'. .br \fB\-dc\fR : use the function `tcxxxputcat' instead of `tcxxxput'. .br \fB\-dai\fR : use the function `tcxxxaddint' instead of `tcxxxput'. .br \fB\-dad\fR : use the function `tcxxxadddouble' instead of `tcxxxput'. .br \fB\-dpr\fR : use the function `tcxxxputproc' instead of `tcxxxput'. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tcumttest (1), .BR tcucodec (1), .BR tcutil (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tchmttest.10000644000175000017500000000676612013574114016176 0ustar mikiomikio.TH "TCHMTTEST" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tchmttest \- test cases of the hash database API .SH DESCRIPTION .PP The command `\fBtchmttest\fR' is a utility for facility test under multi\-thread situation. This command is used in the following format. `\fIpath\fR' specifies the path of a database file. `\fItnum\fR' specifies the number of running threads. `\fIrnum\fR' specifies the number of iterations. `\fIbnum\fR' specifies the number of buckets. `\fIapow\fR' specifies the power of the alignment. `\fIfpow\fR' specifies the power of the free block pool. .PP .RS .br \fBtchmttest write \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-as\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fItnum\fB \fIrnum\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Store records with keys of 8 bytes. They change as `00000001', `00000002'... .RE .br \fBtchmttest read \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-wb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fItnum\fB\fR .RS Retrieve all records of the database above. .RE .br \fBtchmttest remove \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fItnum\fB\fR .RS Remove all records of the database above. .RE .br \fBtchmttest wicked \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-nc\fR]\fB \fIpath\fB \fItnum\fB \fIrnum\fB\fR .RS Perform updating operations selected at random. .RE .br \fBtchmttest typical \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-nc\fR]\fB \fR[\fB\-rr \fInum\fB\fR]\fB \fIpath\fB \fItnum\fB \fIrnum\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR .RS Perform typical operations selected at random. .RE .br \fBtchmttest race \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB \fItnum\fB \fIrnum\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR .RS Perform race condition test. .RE .RE .PP Options feature the following. .PP .RS \fB\-tl\fR : enable the option `HDBTLARGE'. .br \fB\-td\fR : enable the option `HDBTDEFLATE'. .br \fB\-tb\fR : enable the option `HDBTBZIP'. .br \fB\-tt\fR : enable the option `HDBTTCBS'. .br \fB\-tx\fR : enable the option `HDBTEXCODEC'. .br \fB\-rc \fInum\fR\fR : specify the number of cached records. .br \fB\-xm \fInum\fR\fR : specify the size of the extra mapped memory. .br \fB\-df \fInum\fR\fR : specify the unit step number of auto defragmentation. .br \fB\-nl\fR : enable the option `HDBNOLCK'. .br \fB\-nb\fR : enable the option `HDBLCKNB'. .br \fB\-as\fR : use the function `tchdbputasync' instead of `tchdbput'. .br \fB\-rnd\fR : select keys at random. .br \fB\-wb\fR : use the function `tchdbget3' instead of `tchdbget'. .br \fB\-nc\fR : omit the comparison test. .br \fB\-rr \fInum\fR\fR : specify the ratio of reading operation by percentage. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tchtest (1), .BR tchmgr (1), .BR tchdb (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tcutil.30000644000175000017500000037657412013574114015474 0ustar mikiomikio.TH "TCUTIL" 3 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcutil \- the utility API .SH DESCRIPTION .PP 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. .PP To use the utility API, include `\fBtcutil.h\fR' and related standard header files. Usually, write the following description near the front of a source file. .PP .RS .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .RE .PP Objects whose type is pointer to `\fBTCXSTR\fR' are used for extensible string. An extensible string object is created with the function `\fBtcxstrnew\fR' and is deleted with the function `\fBtcxstrdel\fR'. Objects whose type is pointer to `\fBTCLIST\fR' are used for array list. A list object is created with the function `\fBtclistnew\fR' and is deleted with the function `\fBtclistdel\fR'. Objects whose type is pointer to `\fBTCMAP\fR' are used for hash map. A map object is created with the function `\fBtcmapnew\fR' and is deleted with the function `\fBtcmapdel\fR'. Objects whose type is pointer to `\fBTCTREE\fR' are used for ordered tree. A tree object is created with the function `\fBtctreenew\fR' and is deleted with the function `\fBtctreedel\fR'. To avoid memory leak, it is important to delete every object when it is no longer in use. .SH API OF BASIC UTILITIES .PP The constant `tcversion' is the string containing the version information. .PP .RS .br \fBextern const char *tcversion;\fR .RE .PP The variable `tcfatalfunc' is the pointer to the call back function for handling a fatal error. .PP .RS .br \fBextern void (*tcfatalfunc)(const char *);\fR .RS The argument specifies the error message. .RE .RS 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. .RE .RE .PP The function `tcmalloc' is used in order to allocate a region on memory. .PP .RS .br \fBvoid *tcmalloc(size_t \fIsize\fB);\fR .RS `\fIsize\fR' specifies the size of the region. .RE .RS The return value is the pointer to the allocated region. .RE .RS 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. .RE .RE .PP The function `tccalloc' is used in order to allocate a nullified region on memory. .PP .RS .br \fBvoid *tccalloc(size_t \fInmemb\fB, size_t \fIsize\fB);\fR .RS `\fInmemb\fR' specifies the number of elements. .RE .RS `\fIsize\fR' specifies the size of each element. .RE .RS The return value is the pointer to the allocated nullified region. .RE .RS 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. .RE .RE .PP The function `tcrealloc' is used in order to re\-allocate a region on memory. .PP .RS .br \fBvoid *tcrealloc(void *\fIptr\fB, size_t \fIsize\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS The return value is the pointer to the re\-allocated region. .RE .RS 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. .RE .RE .PP The function `tcmemdup' is used in order to duplicate a region on memory. .PP .RS .br \fBvoid *tcmemdup(const void *\fIptr\fB, size_t \fIsize\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS The return value is the pointer to the allocated region of the duplicate. .RE .RS 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. .RE .RE .PP The function `tcstrdup' is used in order to duplicate a string on memory. .PP .RS .br \fBchar *tcstrdup(const void *\fIstr\fB);\fR .RS `\fIstr\fR' specifies the string. .RE .RS The return value is the allocated string equivalent to the specified string. .RE .RS 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. .RE .RE .PP The function `tcfree' is used in order to free a region on memory. .PP .RS .br \fBvoid tcfree(void *\fIptr\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. If it is `NULL', this function has no effect. .RE .RS Although this function is just a wrapper of `free' call, this is useful in applications using another package of the `malloc' series. .RE .RE .SH API OF EXTENSIBLE STRING .PP The function `tcxstrnew' is used in order to create an extensible string object. .PP .RS .br \fBTCXSTR *tcxstrnew(void);\fR .RS The return value is the new extensible string object. .RE .RE .PP The function `tcxstrnew2' is used in order to create an extensible string object from a character string. .PP .RS .br \fBTCXSTR *tcxstrnew2(const char *\fIstr\fB);\fR .RS `\fIstr\fR' specifies the string of the initial content. .RE .RS The return value is the new extensible string object containing the specified string. .RE .RE .PP The function `tcxstrnew3' is used in order to create an extensible string object with the initial allocation size. .PP .RS .br \fBTCXSTR *tcxstrnew3(int \fIasiz\fB);\fR .RS `\fIasiz\fR' specifies the initial allocation size. .RE .RS The return value is the new extensible string object. .RE .RE .PP The function `tcxstrdup' is used in order to copy an extensible string object. .PP .RS .br \fBTCXSTR *tcxstrdup(const TCXSTR *\fIxstr\fB);\fR .RS `\fIxstr\fR' specifies the extensible string object. .RE .RS The return value is the new extensible string object equivalent to the specified object. .RE .RE .PP The function `tcxstrdel' is used in order to delete an extensible string object. .PP .RS .br \fBvoid tcxstrdel(TCXSTR *\fIxstr\fB);\fR .RS `\fIxstr\fR' specifies the extensible string object. .RE .RS Note that the deleted object and its derivatives can not be used anymore. .RE .RE .PP The function `tcxstrcat' is used in order to concatenate a region to the end of an extensible string object. .PP .RS .br \fBvoid tcxstrcat(TCXSTR *\fIxstr\fB, const void *\fIptr\fB, int \fIsize\fB);\fR .RS `\fIxstr\fR' specifies the extensible string object. .RE .RS `\fIptr\fR' specifies the pointer to the region to be appended. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RE .PP The function `tcxstrcat2' is used in order to concatenate a character string to the end of an extensible string object. .PP .RS .br \fBvoid tcxstrcat2(TCXSTR *\fIxstr\fB, const char *\fIstr\fB);\fR .RS `\fIxstr\fR' specifies the extensible string object. .RE .RS `\fIstr\fR' specifies the string to be appended. .RE .RE .PP The function `tcxstrptr' is used in order to get the pointer of the region of an extensible string object. .PP .RS .br \fBconst void *tcxstrptr(const TCXSTR *\fIxstr\fB);\fR .RS `\fIxstr\fR' specifies the extensible string object. .RE .RS The return value is the pointer of the region of the object. .RE .RS 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. .RE .RE .PP The function `tcxstrsize' is used in order to get the size of the region of an extensible string object. .PP .RS .br \fBint tcxstrsize(const TCXSTR *\fIxstr\fB);\fR .RS `\fIxstr\fR' specifies the extensible string object. .RE .RS The return value is the size of the region of the object. .RE .RE .PP The function `tcxstrclear' is used in order to clear an extensible string object. .PP .RS .br \fBvoid tcxstrclear(TCXSTR *\fIxstr\fB);\fR .RS `\fIxstr\fR' specifies the extensible string object. .RE .RS The internal buffer of the object is cleared and the size is set zero. .RE .RE .PP The function `tcxstrprintf' is used in order to perform formatted output into an extensible string object. .PP .RS .br \fBvoid tcxstrprintf(TCXSTR *\fIxstr\fB, const char *\fIformat\fB, ...);\fR .RS `\fIxstr\fR' specifies the extensible string object. .RE .RS `\fIformat\fR' 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. .RE .RS The other arguments are used according to the format string. .RE .RE .PP The function `tcsprintf' is used in order to allocate a formatted string on memory. .PP .RS .br \fBchar *tcsprintf(const char *\fIformat\fB, ...);\fR .RS `\fIformat\fR' 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. .RE .RS The other arguments are used according to the format string. .RE .RS The return value is the pointer to the region of the result string. .RE .RS 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. .RE .RE .SH API OF ARRAY LIST .PP The function `tclistnew' is used in order to create a list object. .PP .RS .br \fBTCLIST *tclistnew(void);\fR .RS The return value is the new list object. .RE .RE .PP The function `tclistnew2' is used in order to create a list object with expecting the number of elements. .PP .RS .br \fBTCLIST *tclistnew2(int \fIanum\fB);\fR .RS `\fIanum\fR' specifies the number of elements expected to be stored in the list. .RE .RS The return value is the new list object. .RE .RE .PP The function `tclistnew3' is used in order to create a list object with initial string elements. .PP .RS .br \fBTCLIST *tclistnew3(const char *\fIstr\fB, ...);\fR .RS `\fIstr\fR' specifies the string of the first element. .RE .RS The other arguments are other elements. They should be trailed by a `NULL' argument. .RE .RS The return value is the new list object. .RE .RE .PP The function `tclistdup' is used in order to copy a list object. .PP .RS .br \fBTCLIST *tclistdup(const TCLIST *\fIlist\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS The return value is the new list object equivalent to the specified object. .RE .RE .PP The function `tclistdel' is used in order to delete a list object. .PP .RS .br \fBvoid tclistdel(TCLIST *\fIlist\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS Note that the deleted object and its derivatives can not be used anymore. .RE .RE .PP The function `tclistnum' is used in order to get the number of elements of a list object. .PP .RS .br \fBint tclistnum(const TCLIST *\fIlist\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS The return value is the number of elements of the list. .RE .RE .PP The function `tclistval' is used in order to get the pointer to the region of an element of a list object. .PP .RS .br \fBconst void *tclistval(const TCLIST *\fIlist\fB, int \fIindex\fB, int *\fIsp\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIindex\fR' specifies the index of the element. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS The return value is the pointer to the region of the value. .RE .RS 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'. .RE .RE .PP The function `tclistval2' is used in order to get the string of an element of a list object. .PP .RS .br \fBconst char *tclistval2(const TCLIST *\fIlist\fB, int \fIindex\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIindex\fR' specifies the index of the element. .RE .RS The return value is the string of the value. .RE .RS If `index' is equal to or more than the number of elements, the return value is `NULL'. .RE .RE .PP The function `tclistpush' is used in order to add an element at the end of a list object. .PP .RS .br \fBvoid tclistpush(TCLIST *\fIlist\fB, const void *\fIptr\fB, int \fIsize\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIptr\fR' specifies the pointer to the region of the new element. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RE .PP The function `tclistpush2' is used in order to add a string element at the end of a list object. .PP .RS .br \fBvoid tclistpush2(TCLIST *\fIlist\fB, const char *\fIstr\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIstr\fR' specifies the string of the new element. .RE .RE .PP The function `tclistpop' is used in order to remove an element of the end of a list object. .PP .RS .br \fBvoid *tclistpop(TCLIST *\fIlist\fB, int *\fIsp\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS The return value is the pointer to the region of the removed element. .RE .RS 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'. .RE .RE .PP The function `tclistpop2' is used in order to remove a string element of the end of a list object. .PP .RS .br \fBchar *tclistpop2(TCLIST *\fIlist\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS The return value is the string of the removed element. .RE .RS 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'. .RE .RE .PP The function `tclistunshift' is used in order to add an element at the top of a list object. .PP .RS .br \fBvoid tclistunshift(TCLIST *\fIlist\fB, const void *\fIptr\fB, int \fIsize\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIptr\fR' specifies the pointer to the region of the new element. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RE .PP The function `tclistunshift2' is used in order to add a string element at the top of a list object. .PP .RS .br \fBvoid tclistunshift2(TCLIST *\fIlist\fB, const char *\fIstr\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIstr\fR' specifies the string of the new element. .RE .RE .PP The function `tclistshift' is used in order to remove an element of the top of a list object. .PP .RS .br \fBvoid *tclistshift(TCLIST *\fIlist\fB, int *\fIsp\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS The return value is the pointer to the region of the removed element. .RE .RS 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'. .RE .RE .PP The function `tclistshift2' is used in order to remove a string element of the top of a list object. .PP .RS .br \fBchar *tclistshift2(TCLIST *\fIlist\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS The return value is the string of the removed element. .RE .RS 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'. .RE .RE .PP The function `tclistinsert' is used in order to add an element at the specified location of a list object. .PP .RS .br \fBvoid tclistinsert(TCLIST *\fIlist\fB, int \fIindex\fB, const void *\fIptr\fB, int \fIsize\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIindex\fR' specifies the index of the new element. .RE .RS `\fIptr\fR' specifies the pointer to the region of the new element. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS If `index' is equal to or more than the number of elements, this function has no effect. .RE .RE .PP The function `tclistinsert2' is used in order to add a string element at the specified location of a list object. .PP .RS .br \fBvoid tclistinsert2(TCLIST *\fIlist\fB, int \fIindex\fB, const char *\fIstr\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIindex\fR' specifies the index of the new element. .RE .RS `\fIstr\fR' specifies the string of the new element. .RE .RS If `index' is equal to or more than the number of elements, this function has no effect. .RE .RE .PP The function `tclistremove' is used in order to remove an element at the specified location of a list object. .PP .RS .br \fBvoid *tclistremove(TCLIST *\fIlist\fB, int \fIindex\fB, int *\fIsp\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIindex\fR' specifies the index of the element to be removed. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS The return value is the pointer to the region of the removed element. .RE .RS 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'. .RE .RE .PP The function `tclistremove2' is used in order to remove a string element at the specified location of a list object. .PP .RS .br \fBchar *tclistremove2(TCLIST *\fIlist\fB, int \fIindex\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIindex\fR' specifies the index of the element to be removed. .RE .RS The return value is the string of the removed element. .RE .RS 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'. .RE .RE .PP The function `tclistover' is used in order to overwrite an element at the specified location of a list object. .PP .RS .br \fBvoid tclistover(TCLIST *\fIlist\fB, int \fIindex\fB, const void *\fIptr\fB, int \fIsize\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIindex\fR' specifies the index of the element to be overwritten. .RE .RS `\fIptr\fR' specifies the pointer to the region of the new content. .RE .RS `\fIsize\fR' specifies the size of the new content. .RE .RS If `index' is equal to or more than the number of elements, this function has no effect. .RE .RE .PP The function `tclistover2' is used in order to overwrite a string element at the specified location of a list object. .PP .RS .br \fBvoid tclistover2(TCLIST *\fIlist\fB, int \fIindex\fB, const char *\fIstr\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIindex\fR' specifies the index of the element to be overwritten. .RE .RS `\fIstr\fR' specifies the string of the new content. .RE .RS If `index' is equal to or more than the number of elements, this function has no effect. .RE .RE .PP The function `tclistsort' is used in order to sort elements of a list object in lexical order. .PP .RS .br \fBvoid tclistsort(TCLIST *\fIlist\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RE .PP The function `tclistlsearch' is used in order to search a list object for an element using liner search. .PP .RS .br \fBint tclistlsearch(const TCLIST *\fIlist\fB, const void *\fIptr\fB, int \fIsize\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIptr\fR' specifies the pointer to the region of the key. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS The return value is the index of a corresponding element or \-1 if there is no corresponding element. .RE .RS If two or more elements correspond, the former returns. .RE .RE .PP The function `tclistbsearch' is used in order to search a list object for an element using binary search. .PP .RS .br \fBint tclistbsearch(const TCLIST *\fIlist\fB, const void *\fIptr\fB, int \fIsize\fB);\fR .RS `\fIlist\fR' specifies the list object. It should be sorted in lexical order. .RE .RS `\fIptr\fR' specifies the pointer to the region of the key. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS The return value is the index of a corresponding element or \-1 if there is no corresponding element. .RE .RS If two or more elements correspond, which returns is not defined. .RE .RE .PP The function `tclistclear' is used in order to clear a list object. .PP .RS .br \fBvoid tclistclear(TCLIST *\fIlist\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS All elements are removed. .RE .RE .PP The function `tclistdump' is used in order to serialize a list object into a byte array. .PP .RS .br \fBvoid *tclistdump(const TCLIST *\fIlist\fB, int *\fIsp\fB);\fR .RS `\fIlist\fR' specifies the list object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS The return value is the pointer to the region of the result serial region. .RE .RS 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. .RE .RE .PP The function `tclistload' is used in order to create a list object from a serialized byte array. .PP .RS .br \fBTCLIST *tclistload(const void *\fIptr\fB, int \fIsize\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region of serialized byte array. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS The return value is a new list object. .RE .RS 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. .RE .RE .SH API OF HASH MAP .PP The function `tcmapnew' is used in order to create a map object. .PP .RS .br \fBTCMAP *tcmapnew(void);\fR .RS The return value is the new map object. .RE .RE .PP The function `tcmapnew2' is used in order to create a map object with specifying the number of the buckets. .PP .RS .br \fBTCMAP *tcmapnew2(uint32_t \fIbnum\fB);\fR .RS `\fIbnum\fR' specifies the number of the buckets. .RE .RS The return value is the new map object. .RE .RE .PP The function `tcmapnew3' is used in order to create a map object with initial string elements. .PP .RS .br \fBTCMAP *tcmapnew3(const char *\fIstr\fB, ...);\fR .RS `\fIstr\fR' specifies the string of the first element. .RE .RS The other arguments are other elements. They should be trailed by a `NULL' argument. .RE .RS The return value is the new map object. .RE .RS The key and the value of each record are situated one after the other. .RE .RE .PP The function `tcmapdup' is used in order to copy a map object. .PP .RS .br \fBTCMAP *tcmapdup(const TCMAP *\fImap\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS The return value is the new map object equivalent to the specified object. .RE .RE .PP The function `tcmapdel' is used in order to delete a map object. .PP .RS .br \fBvoid tcmapdel(TCMAP *\fImap\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS Note that the deleted object and its derivatives can not be used anymore. .RE .RE .PP The function `tcmapput' is used in order to store a record into a map object. .PP .RS .br \fBvoid tcmapput(TCMAP *\fImap\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If a record with the same key exists in the map, it is overwritten. .RE .RE .PP The function `tcmapput2' is used in order to store a string record into a map object. .PP .RS .br \fBvoid tcmapput2(TCMAP *\fImap\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If a record with the same key exists in the map, it is overwritten. .RE .RE .PP The function `tcmapputkeep' is used in order to store a new record into a map object. .PP .RS .br \fBbool tcmapputkeep(TCMAP *\fImap\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the map, this function has no effect. .RE .RE .PP The function `tcmapputkeep2' is used in order to store a new string record into a map object. .PP .RS .br \fBbool tcmapputkeep2(TCMAP *\fImap\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the map, this function has no effect. .RE .RE .PP 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. .PP .RS .br \fBvoid tcmapputcat(TCMAP *\fImap\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP 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. .PP .RS .br \fBvoid tcmapputcat2(TCMAP *\fImap\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcmapout' is used in order to remove a record of a map object. .PP .RS .br \fBbool tcmapout(TCMAP *\fImap\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is true. False is returned when no record corresponds to the specified key. .RE .RE .PP The function `tcmapout2' is used in order to remove a string record of a map object. .PP .RS .br \fBbool tcmapout2(TCMAP *\fImap\fB, const char *\fIkstr\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is true. False is returned when no record corresponds to the specified key. .RE .RE .PP The function `tcmapget' is used in order to retrieve a record in a map object. .PP .RS .br \fBconst void *tcmapget(const TCMAP *\fImap\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int *\fIsp\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcmapget2' is used in order to retrieve a string record in a map object. .PP .RS .br \fBconst char *tcmapget2(const TCMAP *\fImap\fB, const char *\fIkstr\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is the string of the value of the corresponding record. `NULL' is returned when no record corresponds. .RE .RE .PP The function `tcmapmove' is used in order to move a record to the edge of a map object. .PP .RS .br \fBbool tcmapmove(TCMAP *\fImap\fB, const void *\fIkbuf\fB, int \fIksiz\fB, bool \fIhead\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of a key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIhead\fR' specifies the destination which is the head if it is true or the tail if else. .RE .RS If successful, the return value is true. False is returned when no record corresponds to the specified key. .RE .RE .PP The function `tcmapmove2' is used in order to move a string record to the edge of a map object. .PP .RS .br \fBbool tcmapmove2(TCMAP *\fImap\fB, const char *\fIkstr\fB, bool \fIhead\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIkstr\fR' specifies the string of a key. .RE .RS `\fIhead\fR' specifies the destination which is the head if it is true or the tail if else. .RE .RS If successful, the return value is true. False is returned when no record corresponds to the specified key. .RE .RE .PP The function `tcmapiterinit' is used in order to initialize the iterator of a map object. .PP .RS .br \fBvoid tcmapiterinit(TCMAP *\fImap\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS The iterator is used in order to access the key of every record stored in the map object. .RE .RE .PP The function `tcmapiternext' is used in order to get the next key of the iterator of a map object. .PP .RS .br \fBconst void *tcmapiternext(TCMAP *\fImap\fB, int *\fIsp\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcmapiternext2' is used in order to get the next key string of the iterator of a map object. .PP .RS .br \fBconst char *tcmapiternext2(TCMAP *\fImap\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS 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. .RE .RS The order of iteration is assured to be the same as the stored order. .RE .RE .PP The function `tcmaprnum' is used in order to get the number of records stored in a map object. .PP .RS .br \fBuint64_t tcmaprnum(const TCMAP *\fImap\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS The return value is the number of the records stored in the map object. .RE .RE .PP The function `tcmapmsiz' is used in order to get the total size of memory used in a map object. .PP .RS .br \fBuint64_t tcmapmsiz(const TCMAP *\fImap\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS The return value is the total size of memory used in a map object. .RE .RE .PP The function `tcmapkeys' is used in order to create a list object containing all keys in a map object. .PP .RS .br \fBTCLIST *tcmapkeys(const TCMAP *\fImap\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS The return value is the new list object containing all keys in the map object. .RE .RS 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. .RE .RE .PP The function `tcmapvals' is used in order to create a list object containing all values in a map object. .PP .RS .br \fBTCLIST *tcmapvals(const TCMAP *\fImap\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS The return value is the new list object containing all values in the map object. .RE .RS 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. .RE .RE .PP The function `tcmapaddint' is used in order to add an integer to a record in a map object. .PP .RS .br \fBint tcmapaddint(TCMAP *\fImap\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int \fInum\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS The return value is the summation value. .RE .RS 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. .RE .RE .PP The function `tcmapadddouble' is used in order to add a real number to a record in a map object. .PP .RS .br \fBdouble tcmapadddouble(TCMAP *\fImap\fB, const void *\fIkbuf\fB, int \fIksiz\fB, double \fInum\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS The return value is the summation value. .RE .RS 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. .RE .RE .PP The function `tcmapclear' is used in order to clear a map object. .PP .RS .br \fBvoid tcmapclear(TCMAP *\fImap\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS All records are removed. .RE .RE .PP The function `tcmapcutfront' is used in order to remove front records of a map object. .PP .RS .br \fBvoid tcmapcutfront(TCMAP *\fImap\fB, int \fInum\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fInum\fR' specifies the number of records to be removed. .RE .RE .PP The function `tcmapdump' is used in order to serialize a map object into a byte array. .PP .RS .br \fBvoid *tcmapdump(const TCMAP *\fImap\fB, int *\fIsp\fB);\fR .RS `\fImap\fR' specifies the map object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS The return value is the pointer to the region of the result serial region. .RE .RS 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. .RE .RE .PP The function `tcmapload' is used in order to create a map object from a serialized byte array. .PP .RS .br \fBTCMAP *tcmapload(const void *\fIptr\fB, int \fIsize\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region of serialized byte array. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS The return value is a new map object. .RE .RS 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. .RE .RE .SH API OF ORDERED TREE .PP The function `tctreenew' is used in order to create a tree object. .PP .RS .br \fBTCTREE *tctreenew(void);\fR .RS The return value is the new tree object. .RE .RE .PP The function `tctreenew2' is used in order to create a tree object with specifying the custom comparison function. .PP .RS .br \fBTCTREE *tctreenew2(TCCMP \fIcmp\fB, void *\fIcmpop\fB);\fR .RS `\fIcmp\fR' 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. .RE .RS `\fIcmpop\fR' specifies an arbitrary pointer to be given as a parameter of the comparison function. If it is not needed, `NULL' can be specified. .RE .RS The return value is the new tree object. .RE .RS The default comparison function compares keys of two records by lexical order. The functions `tccmplexical' (dafault), `tccmpdecimal', `tccmpint32', and `tccmpint64' are built\-in. .RE .RE .PP The function `tctreedup' is used in order to copy a tree object. .PP .RS .br \fBTCTREE *tctreedup(const TCTREE *\fItree\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS The return value is the new tree object equivalent to the specified object. .RE .RE .PP The function `tctreedel' is used in order to delete a tree object. .PP .RS .br \fBvoid tctreedel(TCTREE *\fItree\fB);\fR .RS `tree' specifies the tree object. .RE .RS Note that the deleted object and its derivatives can not be used anymore. .RE .RE .PP The function `tctreeput' is used in order to store a record into a tree object. .PP .RS .br \fBvoid tctreeput(TCTREE *\fItree\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If a record with the same key exists in the tree, it is overwritten. .RE .RE .PP The function `tctreeput2' is used in order to store a string record into a tree object. .PP .RS .br \fBvoid tctreeput2(TCTREE *\fItree\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If a record with the same key exists in the tree, it is overwritten. .RE .RE .PP The function `tctreeputkeep' is used in order to store a new record into a tree object. .PP .RS .br \fBbool tctreeputkeep(TCTREE *\fItree\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the tree, this function has no effect. .RE .RE .PP The function `tctreeputkeep2' is used in order to store a new string record into a tree object. .PP .RS .br \fBbool tctreeputkeep2(TCTREE *\fItree\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the tree, this function has no effect. .RE .RE .PP 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. .PP .RS .br \fBvoid tctreeputcat(TCTREE *\fItree\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP 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. .PP .RS .br \fBvoid tctreeputcat2(TCTREE *\fItree\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tctreeout' is used in order to remove a record of a tree object. .PP .RS .br \fBbool tctreeout(TCTREE *\fItree\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is true. False is returned when no record corresponds to the specified key. .RE .RE .PP The function `tctreeout2' is used in order to remove a string record of a tree object. .PP .RS .br \fBbool tctreeout2(TCTREE *\fItree\fB, const char *\fIkstr\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is true. False is returned when no record corresponds to the specified key. .RE .RE .PP The function `tctreeget' is used in order to retrieve a record in a tree object. .PP .RS .br \fBconst void *tctreeget(TCTREE *\fItree\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int *\fIsp\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tctreeget2' is used in order to retrieve a string record in a tree object. .PP .RS .br \fBconst char *tctreeget2(TCTREE *\fItree\fB, const char *\fIkstr\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is the string of the value of the corresponding record. `NULL' is returned when no record corresponds. .RE .RE .PP The function `tctreeiterinit' is used in order to initialize the iterator of a tree object. .PP .RS .br \fBvoid tctreeiterinit(TCTREE *\fItree\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS The iterator is used in order to access the key of every record stored in the tree object. .RE .RE .PP The function `tctreeiternext' is used in order to get the next key of the iterator of a tree object. .PP .RS .br \fBconst void *tctreeiternext(TCTREE *\fItree\fB, int *\fIsp\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tctreeiternext2' is used in order to get the next key string of the iterator of a tree object. .PP .RS .br \fBconst char *tctreeiternext2(TCTREE *\fItree\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS 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. .RE .RS The order of iteration is assured to be ascending of the keys. .RE .RE .PP The function `tctreernum' is used in order to get the number of records stored in a tree object. .PP .RS .br \fBuint64_t tctreernum(const TCTREE *\fItree\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS The return value is the number of the records stored in the tree object. .RE .RE .PP The function `tctreemsiz' is used in order to get the total size of memory used in a tree object. .PP .RS .br \fBuint64_t tctreemsiz(const TCTREE *\fItree\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS The return value is the total size of memory used in a tree object. .RE .RE .PP The function `tctreekeys' is used in order to create a list object containing all keys in a tree object. .PP .RS .br \fBTCLIST *tctreekeys(const TCTREE *\fItree\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS The return value is the new list object containing all keys in the tree object. .RE .RS 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. .RE .RE .PP The function `tctreevals' is used in order to create a list object containing all values in a tree object. .PP .RS .br \fBTCLIST *tctreevals(const TCTREE *\fItree\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS The return value is the new list object containing all values in the tree object. .RE .RS 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. .RE .RE .PP The function `tctreeaddint' is used in order to add an integer to a record in a tree object. .PP .RS .br \fBint tctreeaddint(TCTREE *\fItree\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int \fInum\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS The return value is the summation value. .RE .RS 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. .RE .RE .PP The function `tctreeadddouble' is used in order to add a real number to a record in a tree object. .PP .RS .br \fBdouble tctreeadddouble(TCTREE *\fItree\fB, const void *\fIkbuf\fB, int \fIksiz\fB, double \fInum\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS The return value is the summation value. .RE .RS 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. .RE .RE .PP The function `tctreeclear' is used in order to clear a tree object. .PP .RS .br \fBvoid tctreeclear(TCTREE *\fItree\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS All records are removed. .RE .RE .PP The function `tctreecutfringe' is used in order to remove fringe records of a tree object. .PP .RS .br \fBvoid tctreecutfringe(TCTREE *\fItree\fB, int \fInum\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS `\fInum\fR' specifies the number of records to be removed. .RE .RE .PP The function `tctreedump' is used in order to serialize a tree object into a byte array. .PP .RS .br \fBvoid *tctreedump(const TCTREE *\fItree\fB, int *\fIsp\fB);\fR .RS `\fItree\fR' specifies the tree object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS The return value is the pointer to the region of the result serial region. .RE .RS 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. .RE .RE .PP The function `tctreeload' is used in order to create a tree object from a serialized byte array. .PP .RS .br \fBTCTREE *tctreeload(const void *\fIptr\fB, int \fIsize\fB, TCCMP \fIcmp\fB, void *\fIcmpop\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region of serialized byte array. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fIcmp\fR' specifies the pointer to the custom comparison function. .RE .RS `\fIcmpop\fR' specifies an arbitrary pointer to be given as a parameter of the comparison function. .RE .RS If it is not needed, `NULL' can be specified. .RE .RS The return value is a new tree object. .RE .RS 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. .RE .RE .SH API OF ON\-MEMORY HASH DATABASE .PP The function `tcmdbnew' is used in order to create an on\-memory hash database object. .PP .RS .br \fBTCMDB *tcmdbnew(void);\fR .RS The return value is the new on\-memory hash database object. .RE .RS The object can be shared by plural threads because of the internal mutex. .RE .RE .PP The function `tcmdbnew2' is used in order to create an on\-memory hash database object with specifying the number of the buckets. .PP .RS .br \fBTCMDB *tcmdbnew2(uint32_t \fIbnum\fB);\fR .RS `\fIbnum\fR' specifies the number of the buckets. .RE .RS The return value is the new on\-memory hash database object. .RE .RS The object can be shared by plural threads because of the internal mutex. .RE .RE .PP The function `tcmdbdel' is used in order to delete an on\-memory hash database object. .PP .RS .br \fBvoid tcmdbdel(TCMDB *\fImdb\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RE .PP The function `tcmdbput' is used in order to store a record into an on\-memory hash database object. .PP .RS .br \fBvoid tcmdbput(TCMDB *\fImdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcmdbput2' is used in order to store a string record into an on\-memory hash database object. .PP .RS .br \fBvoid tcmdbput2(TCMDB *\fImdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcmdbputkeep' is used in order to store a new record into an on\-memory hash database object. .PP .RS .br \fBbool tcmdbputkeep(TCMDB *\fImdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tcmdbputkeep2' is used in order to store a new string record into an on\-memory hash database object. .PP .RS .br \fBbool tcmdbputkeep2(TCMDB *\fImdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tcmdbputcat' is used in order to concatenate a value at the end of the existing record in an on\-memory hash database. .PP .RS .br \fBvoid tcmdbputcat(TCMDB *\fImdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcmdbputcat2' is used in order to concatenate a string at the end of the existing record in an on\-memory hash database. .PP .RS .br \fBvoid tcmdbputcat2(TCMDB *\fImdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcmdbout' is used in order to remove a record of an on\-memory hash database object. .PP .RS .br \fBbool tcmdbout(TCMDB *\fImdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is true. False is returned when no record corresponds to the specified key. .RE .RE .PP The function `tcmdbout2' is used in order to remove a string record of an on\-memory hash database object. .PP .RS .br \fBbool tcmdbout2(TCMDB *\fImdb\fB, const char *\fIkstr\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is true. False is returned when no record corresponds to the specified key. .RE .RE .PP The function `tcmdbget' is used in order to retrieve a record in an on\-memory hash database object. .PP .RS .br \fBvoid *tcmdbget(TCMDB *\fImdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int *\fIsp\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcmdbget2' is used in order to retrieve a string record in an on\-memory hash database object. .PP .RS .br \fBchar *tcmdbget2(TCMDB *\fImdb\fB, const char *\fIkstr\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is the string of the value of the corresponding record. `NULL' is returned when no record corresponds. .RE .RS 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. .RE .RE .PP The function `tcmdbvsiz' is used in order to get the size of the value of a record in an on\-memory hash database object. .PP .RS .br \fBint tcmdbvsiz(TCMDB *\fImdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RE .PP 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. .PP .RS .br \fBint tcmdbvsiz2(TCMDB *\fImdb\fB, const char *\fIkstr\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RE .PP The function `tcmdbiterinit' is used in order to initialize the iterator of an on\-memory hash database object. .PP .RS .br \fBvoid tcmdbiterinit(TCMDB *\fImdb\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS The iterator is used in order to access the key of every record stored in the on\-memory hash database. .RE .RE .PP The function `tcmdbiternext' is used in order to get the next key of the iterator of an on\-memory hash database object. .PP .RS .br \fBvoid *tcmdbiternext(TCMDB *\fImdb\fB, int *\fIsp\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return .RE .RS value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcmdbiternext2' is used in order to get the next key string of the iterator of an on\-memory hash database object. .PP .RS .br \fBchar *tcmdbiternext2(TCMDB *\fImdb\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcmdbfwmkeys' is used in order to get forward matching keys in an on\-memory hash database object. .PP .RS .br \fBTCLIST *tcmdbfwmkeys(TCMDB *\fImdb\fB, const void *\fIpbuf\fB, int \fIpsiz\fB, int \fImax\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIpbuf\fR' specifies the pointer to the region of the prefix. .RE .RS `\fIpsiz\fR' specifies the size of the region of the prefix. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcmdbfwmkeys2' is used in order to get forward matching string keys in an on\-memory hash database object. .PP .RS .br \fBTCLIST *tcmdbfwmkeys2(TCMDB *\fImdb\fB, const char *\fIpstr\fB, int \fImax\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIpstr\fR' specifies the string of the prefix. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcmdbrnum' is used in order to get the number of records stored in an on\-memory hash database object. .PP .RS .br \fBuint64_t tcmdbrnum(TCMDB *\fImdb\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS The return value is the number of the records stored in the database. .RE .RE .PP The function `tcmdbmsiz' is used in order to get the total size of memory used in an on\-memory hash database object. .PP .RS .br \fBuint64_t tcmdbmsiz(TCMDB *\fImdb\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS The return value is the total size of memory used in the database. .RE .RE .PP The function `tcmdbaddint' is used in order to add an integer to a record in an on\-memory hash database object. .PP .RS .br \fBint tcmdbaddint(TCMDB *\fImdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int \fInum\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS The return value is the summation value. .RE .RS 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. .RE .RE .PP The function `tcmdbadddouble' is used in order to add a real number to a record in an on\-memory hash database object. .PP .RS .br \fBdouble tcmdbadddouble(TCMDB *\fImdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, double \fInum\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS The return value is the summation value. .RE .RS 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. .RE .RE .PP The function `tcmdbvanish' is used in order to clear an on\-memory hash database object. .PP .RS .br \fBvoid tcmdbvanish(TCMDB *\fImdb\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS All records are removed. .RE .RE .PP The function `tcmdbcutfront' is used in order to remove front records of an on\-memory hash database object. .PP .RS .br \fBvoid tcmdbcutfront(TCMDB *\fImdb\fB, int \fInum\fB);\fR .RS `\fImdb\fR' specifies the on\-memory hash database object. .RE .RS `\fInum\fR' specifies the number of records to be removed. .RE .RE .SH API OF ON\-MEMORY TREE DATABASE .PP The function `tcndbnew' is used in order to create an on\-memory tree database object. .PP .RS .br \fBTCNDB *tcndbnew(void);\fR .RS The return value is the new on\-memory tree database object. .RE .RS The object can be shared by plural threads because of the internal mutex. .RE .RE .PP The function `tcndbnew2' is used in order to create an on\-memory tree database object with specifying the custom comparison function. .PP .RS .br \fBTCNDB *tcndbnew2(TCCMP \fIcmp\fB, void *\fIcmpop\fB);\fR .RS `\fIcmp\fR' specifies the pointer to the custom comparison function. .RE .RS `\fIcmpop\fR' specifies an arbitrary pointer to be given as a parameter of the comparison function. If it is not needed, `NULL' can be specified. .RE .RS The return value is the new on\-memory tree database object. .RE .RS 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. .RE .RE .PP The function `tcndbdel' is used in order to delete an on\-memory tree database object. .PP .RS .br \fBvoid tcndbdel(TCNDB *\fIndb\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RE .PP The function `tcndbput' is used in order to store a record into an on\-memory tree database object. .PP .RS .br \fBvoid tcndbput(TCNDB *\fIndb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcndbput2' is used in order to store a string record into an on\-memory tree database object. .PP .RS .br \fBvoid tcndbput2(TCNDB *\fIndb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcndbputkeep' is used in order to store a new record into an on\-memory tree database object. .PP .RS .br \fBbool tcndbputkeep(TCNDB *\fIndb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tcndbputkeep2' is used in order to store a new string record into an on\-memory tree database object. .PP .RS .br \fBbool tcndbputkeep2(TCNDB *\fIndb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tcndbputcat' is used in order to concatenate a value at the end of the existing record in an on\-memory tree database. .PP .RS .br \fBvoid tcndbputcat(TCNDB *\fIndb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcndbputcat2' is used in order to concatenate a string at the end of the existing record in an on\-memory tree database. .PP .RS .br \fBvoid tcndbputcat2(TCNDB *\fIndb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcndbout' is used in order to remove a record of an on\-memory tree database object. .PP .RS .br \fBbool tcndbout(TCNDB *\fIndb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is true. False is returned when no record corresponds to the specified key. .RE .RE .PP The function `tcndbout2' is used in order to remove a string record of an on\-memory tree database object. .PP .RS .br \fBbool tcndbout2(TCNDB *\fIndb\fB, const char *\fIkstr\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is true. False is returned when no record corresponds to the specified key. .RE .RE .PP The function `tcndbget' is used in order to retrieve a record in an on\-memory tree database object. .PP .RS .br \fBvoid *tcndbget(TCNDB *\fIndb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int *\fIsp\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcndbget2' is used in order to retrieve a string record in an on\-memory tree database object. .PP .RS .br \fBchar *tcndbget2(TCNDB *\fIndb\fB, const char *\fIkstr\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is the string of the value of the corresponding record. `NULL' is returned when no record corresponds. .RE .RS 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. .RE .RE .PP The function `tcndbvsiz' is used in order to get the size of the value of a record in an on\-memory tree database object. .PP .RS .br \fBint tcndbvsiz(TCNDB *\fIndb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RE .PP 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. .PP .RS .br \fBint tcndbvsiz2(TCNDB *\fIndb\fB, const char *\fIkstr\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RE .PP The function `tcndbiterinit' is used in order to initialize the iterator of an on\-memory tree database object. .PP .RS .br \fBvoid tcndbiterinit(TCNDB *\fIndb\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS The iterator is used in order to access the key of every record stored in the on\-memory database. .RE .RE .PP The function `tcndbiternext' is used in order to get the next key of the iterator of an on\-memory tree database object. .PP .RS .br \fBvoid *tcndbiternext(TCNDB *\fIndb\fB, int *\fIsp\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcndbiternext2' is used in order to get the next key string of the iterator of an on\-memory tree database object. .PP .RS .br \fBchar *tcndbiternext2(TCNDB *\fIndb\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcndbfwmkeys' is used in order to get forward matching keys in an on\-memory tree database object. .PP .RS .br \fBTCLIST *tcndbfwmkeys(TCNDB *\fIndb\fB, const void *\fIpbuf\fB, int \fIpsiz\fB, int \fImax\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIpbuf\fR' specifies the pointer to the region of the prefix. .RE .RS `\fIpsiz\fR' specifies the size of the region of the prefix. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcndbfwmkeys2' is used in order to get forward matching string keys in an on\-memory tree database object. .PP .RS .br \fBTCLIST *tcndbfwmkeys2(TCNDB *\fIndb\fB, const char *\fIpstr\fB, int \fImax\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIpstr\fR' specifies the string of the prefix. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcndbrnum' is used in order to get the number of records stored in an on\-memory tree database object. .PP .RS .br \fBuint64_t tcndbrnum(TCNDB *\fIndb\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS The return value is the number of the records stored in the database. .RE .RE .PP The function `tcndbmsiz' is used in order to get the total size of memory used in an on\-memory tree database object. .PP .RS .br \fBuint64_t tcndbmsiz(TCNDB *\fIndb\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS The return value is the total size of memory used in the database. .RE .RE .PP The function `tcndbaddint' is used in order to add an integer to a record in an on\-memory tree database object. .PP .RS .br \fBint tcndbaddint(TCNDB *\fIndb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int \fInum\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS The return value is the summation value. .RE .RS 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. .RE .RE .PP The function `tcndbadddouble' is used in order to add a real number to a record in an on\-memory tree database object. .PP .RS .br \fBdouble tcndbadddouble(TCNDB *\fIndb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, double \fInum\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS The return value is the summation value. .RE .RS 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. .RE .RE .PP The function `tcndbvanish' is used in order to clear an on\-memory tree database object. .PP .RS .br \fBvoid tcndbvanish(TCNDB *\fIndb\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS All records are removed. .RE .RE .PP The function `tcndbcutfringe' is used in order to remove fringe records of an on\-memory tree database object. .PP .RS .br \fBvoid tcndbcutfringe(TCNDB *\fIndb\fB, int \fInum\fB);\fR .RS `\fIndb\fR' specifies the on\-memory tree database object. .RE .RS `\fInum\fR' specifies the number of records to be removed. .RE .RE .SH API OF MEMORY POOL .PP The function `tcmpoolnew' is used in order to create a memory pool object. .PP .RS .br \fBTCMPOOL *tcmpoolnew(void);\fR .RS The return value is the new memory pool object. .RE .RE .PP The function `tcmpooldel' is used in order to delete a memory pool object. .PP .RS .br \fBvoid tcmpooldel(TCMPOOL *\fImpool\fB);\fR .RS `\fImpool\fR' specifies the memory pool object. .RE .RS Note that the deleted object and its derivatives can not be used anymore. .RE .RE .PP The function `tcmpoolpush' is used in order to relegate an arbitrary object to a memory pool object. .PP .RS .br \fBvoid *tcmpoolpush(TCMPOOL *\fImpool\fB, void *\fIptr\fB, void (*\fIdel\fB)(void *));\fR .RS `\fImpool\fR' specifies the memory pool object. .RE .RS `\fIptr\fR' specifies the pointer to the object to be relegated. If it is `NULL', this function has no effect. .RE .RS `\fIdel\fR' specifies the pointer to the function to delete the object. .RE .RS The return value is the pointer to the given object. .RE .RS This function assures that the specified object is deleted when the memory pool object is deleted. .RE .RE .PP The function `tcmpoolpushptr' is used in order to relegate an allocated region to a memory pool object. .PP .RS .br \fBvoid *tcmpoolpushptr(TCMPOOL *\fImpool\fB, void *\fIptr\fB);\fR .RS `\fImpool\fR' specifies the memory pool object. .RE .RS `\fIptr\fR' specifies the pointer to the region to be relegated. If it is `NULL', this function has no effect. .RE .RS The return value is the pointer to the given object. .RE .RS This function assures that the specified region is released when the memory pool object is deleted. .RE .RE .PP The function `tcmpoolpushxstr' is used in order to relegate an extensible string object to a memory pool object. .PP .RS .br \fBTCXSTR *tcmpoolpushxstr(TCMPOOL *\fImpool\fB, TCXSTR *\fIxstr\fB);\fR .RS `\fImpool\fR' specifies the memory pool object. .RE .RS `\fIxstr\fR' specifies the extensible string object. If it is `NULL', this function has no effect. .RE .RS The return value is the pointer to the given object. .RE .RS This function assures that the specified object is deleted when the memory pool object is deleted. .RE .RE .PP The function `tcmpoolpushlist' is used in order to relegate a list object to a memory pool object. .PP .RS .br \fBTCLIST *tcmpoolpushlist(TCMPOOL *\fImpool\fB, TCLIST *\fIlist\fB);\fR .RS `\fImpool\fR' specifies the memory pool object. .RE .RS `\fIlist\fR' specifies the list object. If it is `NULL', this function has no effect. .RE .RS The return value is the pointer to the given object. .RE .RS This function assures that the specified object is deleted when the memory pool object is deleted. .RE .RE .PP The function `tcmpoolpushmap' is used in order to relegate a map object to a memory pool object. .PP .RS .br \fBTCMAP *tcmpoolpushmap(TCMPOOL *\fImpool\fB, TCMAP *\fImap\fB);\fR .RS `\fImpool\fR' specifies the memory pool object. .RE .RS `\fImap\fR' specifies the map object. If it is `NULL', this function has no effect. .RE .RS The return value is the pointer to the given object. .RE .RS This function assures that the specified object is deleted when the memory pool object is deleted. .RE .RE .PP The function `tcmpoolpushtree' is used in order to relegate a tree object to a memory pool object. .PP .RS .br \fBTCTREE *tcmpoolpushtree(TCMPOOL *\fImpool\fB, TCTREE *\fItree\fB);\fR .RS `\fImpool\fR' specifies the memory pool object. .RE .RS `\fItree\fR' specifies the tree object. If it is `NULL', this function has no effect. .RE .RS The return value is the pointer to the given object. .RE .RS This function assures that the specified object is deleted when the memory pool object is deleted. .RE .RE .PP The function `tcmpoolmalloc' is used in order to allocate a region relegated to a memory pool object. .PP .RS .br \fBvoid *tcmpoolmalloc(TCMPOOL *\fImpool\fB, size_t \fIsize\fB);\fR .RS `\fImpool\fR' specifies the memory pool object. .RE .RS The return value is the pointer to the allocated region under the memory pool. .RE .RE .PP The function `tcmpoolxstrnew' is used in order to create an extensible string object relegated to a memory pool object. .PP .RS .br \fBTCXSTR *tcmpoolxstrnew(TCMPOOL *\fImpool\fB);\fR .RS The return value is the new extensible string object under the memory pool. .RE .RE .PP The function `tcmpoollistnew' is used in order to create a list object relegated to a memory pool object. .PP .RS .br \fBTCLIST *tcmpoollistnew(TCMPOOL *\fImpool\fB);\fR .RS The return value is the new list object under the memory pool. .RE .RE .PP The function `tcmpoolmapnew' is used in order to create a map object relegated to a memory pool object. .PP .RS .br \fBTCMAP *tcmpoolmapnew(TCMPOOL *\fImpool\fB);\fR .RS The return value is the new map object under the memory pool. .RE .RE .PP The function `tcmpooltreenew' is used in order to create a tree object relegated to a memory pool object. .PP .RS .br \fBTCTREE *tcmpooltreenew(TCMPOOL *\fImpool\fB);\fR .RS The return value is the new tree object under the memory pool. .RE .RE .PP The function `tcmpoolpop' is used in order to remove the most recently installed cleanup handler of a memory pool object. .PP .RS .br \fBvoid tcmpoolpop(TCMPOOL *\fImpool\fB, bool \fIexe\fB);\fR .RS `\fImpool\fR' specifies the memory pool object. .RE .RS `\fIexe\fR' specifies whether to execute the destructor of the removed handler. .RE .RE .PP The function `tcmpoolclear' is used in order to remove all cleanup handler of a memory pool object. .PP .RS .br \fBvoid tcmpoolclear(TCMPOOL *\fImpool\fB, bool \fIexe\fB);\fR .RS `\fImpool\fR' specifies the memory pool object. .RE .RS `\fIexe\fR' specifies whether to execute the destructors of the removed handlers. .RE .RE .PP The function `tcmpoolglobal' is used in order to get the global memory pool object. .PP .RS .br \fBTCMPOOL *tcmpoolglobal(void);\fR .RS The return value is the global memory pool object. .RE .RS The global memory pool object is a singleton and assured to be deleted when the process is terminating normally. .RE .RE .SH API OF MISCELLANEOUS UTILITIES .PP The function `tclmax' is used in order to get the larger value of two integers. .PP .RS .br \fBlong tclmax(long \fIa\fB, long \fIb\fB);\fR .RS `\fIa\fR' specifies an integer. .RE .RS `\fIb\fR' specifies the other integer. .RE .RS The return value is the larger value of the two. .RE .RE .PP The function `tclmin' is used in order to get the lesser value of two integers. .PP .RS .br \fBlong tclmin(long \fIa\fB, long \fIb\fB);\fR .RS `\fIa\fR' specifies an integer. .RE .RS `\fIb\fR' specifies the other integer. .RE .RS The return value is the lesser value of the two. .RE .RE .PP The function `tclrand' is used in order to get a random number as long integer based on uniform distribution. .PP .RS .br \fBunsigned long tclrand(void);\fR .RS The return value is the random number between 0 and `ULONG_MAX'. .RE .RS This function uses the random number source device and generates a real random number if possible. .RE .RE .PP The function `tcdrand' is used in order to get a random number as double decimal based on uniform distribution. .PP .RS .br \fBdouble tcdrand(void);\fR .RS The return value is the random number equal to or greater than 0, and less than 1.0. .RE .RS This function uses the random number source device and generates a real random number if possible. .RE .RE .PP The function `tcdrandnd' is used in order to get a random number as double decimal based on normal distribution. .PP .RS .br \fBdouble tcdrandnd(double \fIavg\fB, double \fIsd\fB);\fR .RS `\fIavg\fR' specifies the average. .RE .RS `\fIsd\fR' specifies the standard deviation. .RE .RS The return value is the random number. .RE .RS This function uses the random number source device and generates a real random number if possible. .RE .RE .PP The function `tcstricmp' is used in order to compare two strings with case insensitive evaluation. .PP .RS .br \fBint tcstricmp(const char *\fIastr\fB, const char *\fIbstr\fB);\fR .RS `\fIastr\fR' specifies a string. .RE .RS `\fIbstr\fR' specifies of the other string. .RE .RS The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. .RE .RE .PP The function `tcstrfwm' is used in order to check whether a string begins with a key. .PP .RS .br \fBbool tcstrfwm(const char *\fIstr\fB, const char *\fIkey\fB);\fR .RS `\fIstr\fR' specifies the target string. .RE .RS `\fIkey\fR' specifies the forward matching key string. .RE .RS The return value is true if the target string begins with the key, else, it is false. .RE .RE .PP The function `tcstrifwm' is used in order to check whether a string begins with a key with case insensitive evaluation. .PP .RS .br \fBbool tcstrifwm(const char *\fIstr\fB, const char *\fIkey\fB);\fR .RS `\fIstr\fR' specifies the target string. .RE .RS `\fIkey\fR' specifies the forward matching key string. .RE .RS The return value is true if the target string begins with the key, else, it is false. .RE .RE .PP The function `tcstrbwm' is used in order to check whether a string ends with a key. .PP .RS .br \fBbool tcstrbwm(const char *\fIstr\fB, const char *\fIkey\fB);\fR .RS `\fIstr\fR' specifies the target string. .RE .RS `\fIkey\fR' specifies the backward matching key string. .RE .RS The return value is true if the target string ends with the key, else, it is false. .RE .RE .PP The function `tcstribwm' is used in order to check whether a string ends with a key with case insensitive evaluation. .PP .RS .br \fBbool tcstribwm(const char *\fIstr\fB, const char *\fIkey\fB);\fR .RS `\fIstr\fR' specifies the target string. .RE .RS `\fIkey\fR' specifies the backward matching key string. .RE .RS The return value is true if the target string ends with the key, else, it is false. .RE .RE .PP The function `tcstrdist' is used in order to calculate the edit distance of two strings. .PP .RS .br \fBint tcstrdist(const char *\fIastr\fB, const char *\fIbstr\fB);\fR .RS `\fIastr\fR' specifies a string. .RE .RS `\fIbstr\fR' specifies of the other string. .RE .RS The return value is the edit distance which is known as the Levenshtein distance. The cost is calculated by byte. .RE .RE .PP The function `tcstrdistutf' is used in order to calculate the edit distance of two UTF\-8 strings. .PP .RS .br \fBint tcstrdistutf(const char *\fIastr\fB, const char *\fIbstr\fB);\fR .RS `\fIastr\fR' specifies a string. .RE .RS `\fIbstr\fR' specifies of the other string. .RE .RS The return value is the edit distance which is known as the Levenshtein distance. The cost is calculated by Unicode character. .RE .RE .PP The function `tcstrtoupper' is used in order to convert the letters of a string into upper case. .PP .RS .br \fBchar *tcstrtoupper(char *\fIstr\fB);\fR .RS `\fIstr\fR' specifies the string to be converted. .RE .RS The return value is the string itself. .RE .RE .PP The function `tcstrtolower' is used in order to convert the letters of a string into lower case. .PP .RS .br \fBchar *tcstrtolower(char *\fIstr\fB);\fR .RS `\fIstr\fR' specifies the string to be converted. .RE .RS The return value is the string itself. .RE .RE .PP The function `tcstrtrim' is used in order to cut space characters at head or tail of a string. .PP .RS .br \fBchar *tcstrtrim(char *\fIstr\fB);\fR .RS `\fIstr\fR' specifies the string to be converted. .RE .RS The return value is the string itself. .RE .RE .PP The function `tcstrsqzspc' is used in order to squeeze space characters in a string and trim it. .PP .RS .br \fBchar *tcstrsqzspc(char *\fIstr\fB);\fR .RS `\fIstr\fR' specifies the string to be converted. .RE .RS The return value is the string itself. .RE .RE .PP The function `tcstrsubchr' is used in order to substitute characters in a string. .PP .RS .br \fBchar *tcstrsubchr(char *\fIstr\fB, const char *\fIrstr\fB, const char *\fIsstr\fB);\fR .RS `\fIstr\fR' specifies the string to be converted. .RE .RS `\fIrstr\fR' specifies the string containing characters to be replaced. .RE .RS `\fIsstr\fR' specifies the string containing characters to be substituted. .RE .RS If the substitute string is shorter then the replacement string, corresponding characters are removed. .RE .RE .PP The function `tcstrcntutf' is used in order to count the number of characters in a string of UTF\-8. .PP .RS .br \fBint tcstrcntutf(const char *\fIstr\fB);\fR .RS `\fIstr\fR' specifies the string of UTF\-8. .RE .RS The return value is the number of characters in the string. .RE .RE .PP The function `tcstrcututf' is used in order to cut a string of UTF\-8 at the specified number of characters. .PP .RS .br \fBchar *tcstrcututf(char *\fIstr\fB, int \fInum\fB);\fR .RS `\fIstr\fR' specifies the string of UTF\-8. .RE .RS `\fInum\fR' specifies the number of characters to be kept. .RE .RS The return value is the string itself. .RE .RE .PP The function `tcstrutftoucs' is used in order to convert a UTF\-8 string into a UCS\-2 array. .PP .RS .br \fBvoid tcstrutftoucs(const char *\fIstr\fB, uint16_t *\fIary\fB, int *\fInp\fB);\fR .RS `\fIstr\fR' specifies the UTF\-8 string. .RE .RS `\fIary\fR' specifies the pointer to the region into which the result UCS\-2 codes are written. The size of the buffer should be sufficient. .RE .RS `\fInp\fR' specifies the pointer to a variable into which the number of elements of the result array is assigned. .RE .RE .PP The function `tcstrucstoutf' is used in order to convert a UCS\-2 array into a UTF\-8 string. .PP .RS .br \fBint tcstrucstoutf(const uint16_t *\fIary\fB, int \fInum\fB, char *\fIstr\fB);\fR .RS `\fIary\fR' specifies the array of UCS\-2 codes. .RE .RS `\fInum\fR' specifies the number of the array. .RE .RS `\fIstr\fR' specifies the pointer to the region into which the result UTF\-8 string is written. The size of the buffer should be sufficient. .RE .RS The return value is the length of the result string. .RE .RE .PP The function `tcstrsplit' is used in order to create a list object by splitting a string. .PP .RS .br \fBTCLIST *tcstrsplit(const char *\fIstr\fB, const char *\fIdelims\fB);\fR .RS `\fIstr\fR' specifies the source string. .RE .RS `\fIdelims\fR' specifies a string containing delimiting characters. .RE .RS The return value is a list object of the split elements. .RE .RS 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. .RE .RE .PP The function `tcstrjoin' is used in order to create a string by joining all elements of a list object. .PP .RS .br \fBchar *tcstrjoin(const TCLIST *\fIlist\fB, char \fIdelim\fB);\fR .RS `\fIlist\fR' specifies a list object. .RE .RS `\fIdelim\fR' specifies a delimiting character. .RE .RS The return value is the result string. .RE .RS 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. .RE .RE .PP The function `tcatoi' is used in order to convert a string to an integer. .PP .RS .br \fBint64_t tcatoi(const char *\fIstr\fB);\fR .RS `\fIstr\fR' specifies the string. .RE .RS The return value is the integer. If the string does not contain numeric expression, 0 is returned. .RE .RS This function is equivalent to `atoll' except that it does not depend on the locale. .RE .RE .PP The function `tcatoix' is used in order to convert a string with a metric prefix to an integer. .PP .RS .br \fBint64_t tcatoix(const char *\fIstr\fB);\fR .RS `\fIstr\fR' 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. .RE .RS 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. .RE .RE .PP The function `tcatof' is used in order to convert a string to a real number. .PP .RS .br \fBdouble tcatof(const char *\fIstr\fB);\fR .RS `\fIstr\fR' specifies the string. .RE .RS The return value is the real number. If the string does not contain numeric expression, 0.0 is returned. .RE .RS This function is equivalent to `atof' except that it does not depend on the locale. .RE .RE .PP The function `tcregexmatch' is used in order to check whether a string matches a regular expression. .PP .RS .br \fBbool tcregexmatch(const char *\fIstr\fB, const char *\fIregex\fB);\fR .RS `\fIstr\fR' specifies the target string. .RE .RS `\fIregex\fR' specifies the regular expression string. If it begins with `*', the trailing substring is used as a case\-insensitive regular expression. .RE .RS The return value is true if matching is success, else, it is false. .RE .RE .PP The function `tcregexreplace' is used in order to replace each substring matching a regular expression string. .PP .RS .br \fBchar *tcregexreplace(const char *\fIstr\fB, const char *\fIregex\fB, const char *\fIalt\fB);\fR .RS `\fIstr\fR' specifies the target string. .RE .RS `\fIregex\fR' specifies the regular expression string for substrings. If it begins with `*', the trailing substring is used as a case\-insensitive regular expression. .RE .RS `\fIalt\fR' 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. .RE .RS The return value is a new converted string. Even if the regular expression is invalid, a copy of the original string is returned. .RE .RS 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. .RE .RE .PP The function `tcmd5hash' is used in order to get the MD5 hash value of a serial object. .PP .RS .br \fBvoid tcmd5hash(const void *\fIptr\fB, int \fIsize\fB, char *\fIbuf\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fIbuf\fR' 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. .RE .RE .PP The function `tcarccipher' is used in order to cipher or decipher a serial object with the Arcfour stream cipher. .PP .RS .br \fBvoid tcarccipher(const void *\fIptr\fB, int \fIsize\fB, const void *\fIkbuf\fB, int \fIksiz\fB, void *\fIobuf\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the cipher key. .RE .RS `\fIksiz\fR' specifies the size of the region of the cipher key. .RE .RS `\fIobuf\fR' 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. .RE .RE .PP The function `tctime' is used in order to get the time of day in seconds. .PP .RS .br \fBdouble tctime(void);\fR .RS The return value is the time of day in seconds. The accuracy is in microseconds. .RE .RE .PP The function `tccalendar' is used in order to get the Gregorian calendar of a time. .PP .RS .br \fBvoid tccalendar(int64_t \fIt\fB, int \fIjl\fB, int *\fIyearp\fB, int *\fImonp\fB, int *\fIdayp\fB, int *\fIhourp\fB, int *\fIminp\fB, int *\fIsecp\fB);\fR .RS `\fIt\fR' specifies the source time in seconds from the epoch. If it is `INT64_MAX', the current time is specified. .RE .RS `\fIjl\fR' specifies the jet lag of a location in seconds. If it is `INT_MAX', the local jet lag is specified. .RE .RS `\fIyearp\fR' specifies the pointer to a variable to which the year is assigned. If it is `NULL', it is not used. .RE .RS `\fImonp\fR' 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. .RE .RS `\fIdayp\fR' specifies the pointer to a variable to which the day of the month is assigned. If it is `NULL', it is not used. .RE .RS `\fIhourp\fR' specifies the pointer to a variable to which the hours is assigned. If it is `NULL', it is not used. .RE .RS `\fIminp\fR' specifies the pointer to a variable to which the minutes is assigned. If it is `NULL', it is not used. .RE .RS `\fIsecp\fR' specifies the pointer to a variable to which the seconds is assigned. If it is `NULL', it is not used. .RE .RE .PP The function `tcdatestrwww' is used in order to format a date as a string in W3CDTF. .PP .RS .br \fBvoid tcdatestrwww(int64_t \fIt\fB, int \fIjl\fB, char *\fIbuf\fB);\fR .RS `\fIt\fR' specifies the source time in seconds from the epoch. If it is `INT64_MAX', the current time is specified. .RE .RS `\fIjl\fR' specifies the jet lag of a location in seconds. If it is `INT_MAX', the local jet lag is specified. .RE .RS `\fIbuf\fR' 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. .RE .RS W3CDTF represents a date as "YYYY\-MM\-DDThh:mm:ddTZD". .RE .RE .PP The function `tcdatestrhttp' is used in order to format a date as a string in RFC 1123 format. .PP .RS .br \fBvoid tcdatestrhttp(int64_t \fIt\fB, int \fIjl\fB, char *\fIbuf\fB);\fR .RS `\fIt\fR' specifies the source time in seconds from the epoch. If it is `INT64_MAX', the current time is specified. .RE .RS `\fIjl\fR' specifies the jet lag of a location in seconds. If it is `INT_MAX', the local jet lag is specified. .RE .RS `\fIbuf\fR' 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. .RE .RS RFC 1123 format represents a date as "Wdy, DD\-Mon\-YYYY hh:mm:dd TZD". .RE .RE .PP The function `tcstrmktime' is used in order to get the time value of a date string. .PP .RS .br \fBint64_t tcstrmktime(const char *\fIstr\fB);\fR .RS `\fIstr\fR' 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. .RE .RS The return value is the time value of the date or `INT64_MIN' if the format is invalid. .RE .RE .PP The function `tcjetlag' is used in order to get the jet lag of the local time. .PP .RS .br \fBint tcjetlag(void);\fR .RS The return value is the jet lag of the local time in seconds. .RE .RE .PP The function `tcdayofweek' is used in order to get the day of week of a date. .PP .RS .br \fBint tcdayofweek(int \fIyear\fB, int \fImon\fB, int \fIday\fB);\fR .RS `\fIyear\fR' specifies the year of a date. .RE .RS `\fImon\fR' specifies the month of the date. .RE .RS `\fIday\fR' specifies the day of the date. .RE .RS The return value is the day of week of the date. 0 means Sunday and 6 means Saturday. .RE .RE .SH API OF FILESYSTEM UTILITIES .PP The function `tcrealpath' is used in order to get the canonicalized absolute path of a file. .PP .RS .br \fBchar *tcrealpath(const char *\fIpath\fB);\fR .RS `\fIpath\fR' specifies the path of the file. .RE .RS The return value is the canonicalized absolute path of a file, or `NULL' if the path is invalid. .RE .RS 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. .RE .RE .PP The function `tcstatfile' is used in order to get the status information of a file. .PP .RS .br \fBbool tcstatfile(const char *\fIpath\fB, bool *\fIisdirp\fB, int64_t *\fIsizep\fB, int64_t *\fImtimep\fB);\fR .RS `\fIpath\fR' specifies the path of the file. .RE .RS `\fIisdirp\fR' specifies the pointer to a variable into which whether the file is a directory is assigned. If it is `NULL', it is ignored. .RE .RS `\fIsizep\fR' specifies the pointer to a variable into which the size of the file is assigned. If it is `NULL', it is ignored. .RE .RS `\fIntimep\fR' specifies the pointer to a variable into which the size of the file is assigned. If it is `NULL', it is ignored. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcreadfile' is used in order to read whole data of a file. .PP .RS .br \fBvoid *tcreadfile(const char *\fIpath\fB, int \fIlimit\fB, int *\fIsp\fB);\fR .RS `\fIpath\fR' specifies the path of the file. If it is `NULL', the standard input is specified. .RE .RS `\fIlimit\fR' specifies the limiting size of reading data. If it is not more than 0, the limitation is not specified. .RE .RS `\fIsp\fR' 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. .RE .RS The return value is the pointer to the allocated region of the read data, or `NULL' if the file could not be opened. .RE .RS 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. .RE .RE .PP The function `tcreadfilelines' is used in order to read every line of a file. .PP .RS .br \fBTCLIST *tcreadfilelines(const char *\fIpath\fB);\fR .RS `\fIpath\fR' specifies the path of the file. If it is `NULL', the standard input is specified. .RE .RS The return value is a list object of every lines if successful, else it is `NULL'. .RE .RS 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. .RE .RE .PP The function `tcwritefile' is used in order to write data into a file. .PP .RS .br \fBbool tcwritefile(const char *\fIpath\fB, const void *\fIptr\fB, int \fIsize\fB);\fR .RS `\fIpath\fR' specifies the path of the file. If it is `NULL', the standard output is specified. .RE .RS `\fIptr\fR' specifies the pointer to the data region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tccopyfile' is used in order to copy a file. .PP .RS .br \fBbool tccopyfile(const char *\fIsrc\fB, const char *\fIdest\fB);\fR .RS `\fIsrc\fR' specifies the path of the source file. .RE .RS `\fIdest\fR' specifies the path of the destination file. .RE .RS The return value is true if successful, else, it is false. .RE .RS If the destination file exists, it is overwritten. .RE .RE .PP The function `tcreaddir' is used in order to read names of files in a directory. .PP .RS .br \fBTCLIST *tcreaddir(const char *\fIpath\fB);\fR .RS `\fIpath\fR' specifies the path of the directory. .RE .RS The return value is a list object of names if successful, else it is `NULL'. .RE .RS Links to the directory itself and to the parent directory are ignored. .RE .RS 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. .RE .RE .PP The function `tcglobpat' is used in order to expand a pattern into a list of matched paths. .PP .RS .br \fBTCLIST *tcglobpat(const char *\fIpattern\fB);\fR .RS `\fIpattern\fR' specifies the matching pattern. .RE .RS The return value is a list object of matched paths. If no path is matched, an empty list is returned. .RE .RS 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. .RE .RE .PP The function `tcremovelink' is used in order to remove a file or a directory and its sub ones recursively. .PP .RS .br \fBbool tcremovelink(const char *\fIpath\fB);\fR .RS `\fIpath\fR' specifies the path of the link. .RE .RS 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. .RE .RE .PP The function `tcwrite' is used in order to write data into a file. .PP .RS .br \fBbool tcwrite(int \fIfd\fB, const void *\fIbuf\fB, size_t \fIsize\fB);\fR .RS `\fIfd\fR' specifies the file descriptor. .RE .RS `\fIbuf\fR' specifies the buffer to be written. .RE .RS `\fIsize\fR' specifies the size of the buffer. .RE .RS The return value is true if successful, else, it is false. .RE .RE .PP The function `tcread' is used in order to read data from a file. .PP .RS .br \fBbool tcread(int \fIfd\fB, void *\fIbuf\fB, size_t \fIsize\fB);\fR .RS `\fIfd\fR' specifies the file descriptor. .RE .RS `\fIbuf\fR' specifies the buffer to store into. .RE .RS `\fIsize\fR' specifies the size of the buffer. .RE .RS The return value is true if successful, else, it is false. .RE .RE .PP The function `tclock' is used in order to lock a file. .PP .RS .br \fBbool tclock(int \fIfd\fB, bool \fIex\fB, bool \fInb\fB);\fR .RS `\fIfd\fR' specifies the file descriptor. .RE .RS `\fIex\fR' specifies whether an exclusive lock or a shared lock is performed. .RE .RS `\fInb\fR' specifies whether to request with non\-blocking. .RE .RS The return value is true if successful, else, it is false. .RE .RE .PP The function `tcunlock' is used in order to unlock a file. .PP .RS .br \fBbool tcunlock(int \fIfd\fB);\fR .RS `\fIfd\fR' specifies the file descriptor. .RE .RS The return value is true if successful, else, it is false. .RE .RE .PP The function `tcsystem' is used in order to execute a shell command. .PP .RS .br \fBint tcsystem(const char **\fIargs\fB, int \fIanum\fB);\fR .RS `\fIargs\fR' specifies an array of the command name and its arguments. .RE .RS `\fIanum\fR' specifies the number of elements of the array. .RE .RS The return value is the exit code of the command or `INT_MAX' on failure. .RE .RS The command name and the arguments are quoted and meta characters are escaped. .RE .RE .SH API OF ENCODING UTILITIES .PP The function `tcurlencode' is used in order to encode a serial object with URL encoding. .PP .RS .br \fBchar *tcurlencode(const char *\fIptr\fB, int \fIsize\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS The return value is the result string. .RE .RS 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. .RE .RE .PP The function `tcurldecode' is used in order to decode a string encoded with URL encoding. .PP .RS .br \fBchar *tcurldecode(const char *\fIstr\fB, int *\fIsp\fB);\fR .RS `\fIstr\fR' specifies the encoded string. .RE .RS `\fIsp\fR' specifies the pointer to a variable into which the size of the region of the return value is assigned. .RE .RS The return value is the pointer to the region of the result. .RE .RS 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. .RE .RE .PP The function `tcurlbreak' is used in order to break up a URL into elements. .PP .RS .br \fBTCMAP *tcurlbreak(const char *\fIstr\fB);\fR .RS `\fIstr\fR' specifies the URL string. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcurlresolve' is used in order to resolve a relative URL with an absolute URL. .PP .RS .br \fBchar *tcurlresolve(const char *\fIbase\fB, const char *\fItarget\fB);\fR .RS `\fIbase\fR' specifies the absolute URL of the base location. .RE .RS `\fItarget\fR' specifies the URL to be resolved. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcbaseencode' is used in order to encode a serial object with Base64 encoding. .PP .RS .br \fBchar *tcbaseencode(const char *\fIptr\fB, int \fIsize\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS The return value is the result string. .RE .RS 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. .RE .RE .PP The function `tcbasedecode' is used in order to decode a string encoded with Base64 encoding. .PP .RS .br \fBchar *tcbasedecode(const char *\fIstr\fB, int *\fIsp\fB);\fR .RS `\fIstr\fR' specifies the encoded string. .RE .RS `\fIsp\fR' specifies the pointer to a variable into which the size of the region of the return value is assigned. .RE .RS The return value is the pointer to the region of the result. .RE .RS 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. .RE .RE .PP The function `tcquoteencode' is used in order to encode a serial object with Quoted\-printable encoding. .PP .RS .br \fBchar *tcquoteencode(const char *\fIptr\fB, int \fIsize\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS The return value is the result string. .RE .RS 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. .RE .RE .PP The function `tcquotedecode' is used in order to decode a string encoded with Quoted\-printable encoding. .PP .RS .br \fBchar *tcquotedecode(const char *\fIstr\fB, int *\fIsp\fB);\fR .RS `\fIstr\fR' specifies the encoded string. .RE .RS `\fIsp\fR' specifies the pointer to a variable into which the size of the region of the return value is assigned. .RE .RS The return value is the pointer to the region of the result. .RE .RS 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. .RE .RE .PP The function `tcmimeencode' is used in order to encode a string with MIME encoding. .PP .RS .br \fBchar *tcmimeencode(const char *\fIstr\fB, const char *\fIencname\fB, bool \fIbase\fB);\fR .RS `\fIstr\fR' specifies the string. .RE .RS `\fIencname\fR' specifies the string of the name of the character encoding. .RE .RS `\fIbase\fR' specifies whether to use Base64 encoding. If it is false, Quoted\-printable is used. .RE .RS The return value is the result string. .RE .RS 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. .RE .RE .PP The function `tcmimedecode' is used in order to decode a string encoded with MIME encoding. .PP .RS .br \fBchar *tcmimedecode(const char *\fIstr\fB, char *\fIenp\fB);\fR .RS `\fIstr\fR' specifies the encoded string. .RE .RS `\fIenp\fR' 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. .RE .RS The return value is the result string. .RE .RS 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. .RE .RE .PP The function `tcmimebreak' is used in order to split a string of MIME into headers and the body. .PP .RS .br \fBchar *tcmimebreak(const char *\fIptr\fB, int \fIsize\fB, TCMAP *\fIheaders\fB, int *\fIsp\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region of MIME data. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fIheaders\fR' 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. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS The return value is the pointer to the region of the body data. .RE .RS 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. .RE .RE .PP The function `tcmimeparts' is used in order to split multipart data of MIME into its parts. .PP .RS .br \fBTCLIST *tcmimeparts(const char *\fIptr\fB, int \fIsize\fB, const char *\fIboundary\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region of multipart data of MIME. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fIboundary\fR' specifies the boundary string. .RE .RS The return value is a list object. Each element of the list is the data of a part. .RE .RS 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. .RE .RE .PP The function `tchexencode' is used in order to encode a serial object with hexadecimal encoding. .PP .RS .br \fBchar *tchexencode(const char *\fIptr\fB, int \fIsize\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS The return value is the result string. .RE .RS 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. .RE .RE .PP The function `tchexdecode' is used in order to decode a string encoded with hexadecimal encoding. .PP .RS .br \fBchar *tchexdecode(const char *\fIstr\fB, int *\fIsp\fB);\fR .RS `\fIstr\fR' specifies the encoded string. .RE .RS `\fIsp\fR' specifies the pointer to a variable into which the size of the region of the return .RE .RS value is assigned. .RE .RS The return value is the pointer to the region of the result. .RE .RS 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. .RE .RE .PP The function `tcpackencode' is used in order to compress a serial object with Packbits encoding. .PP .RS .br \fBchar *tcpackencode(const char *\fIptr\fB, int \fIsize\fB, int *\fIsp\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS If successful, the return value is the pointer to the result object, else, it is `NULL'. .RE .RS 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. .RE .RE .PP The function `tcpackdecode' is used in order to decompress a serial object compressed with Packbits encoding. .PP .RS .br \fBchar *tcpackdecode(const char *\fIptr\fB, int \fIsize\fB, int *\fIsp\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fIsp\fR' specifies the pointer to a variable into which the size of the region of the return value is assigned. .RE .RS If successful, the return value is the pointer to the result object, else, it is `NULL'. .RE .RS 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. .RE .RE .PP The function `tcbsencode' is used in order to compress a serial object with TCBS encoding. .PP .RS .br \fBchar *tcbsencode(const char *\fIptr\fB, int \fIsize\fB, int *\fIsp\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS If successful, the return value is the pointer to the result object, else, it is `NULL'. .RE .RS 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. .RE .RE .PP The function `tcbsdecode' is used in order to decompress a serial object compressed with TCBS encoding. .PP .RS .br \fBchar *tcbsdecode(const char *\fIptr\fB, int \fIsize\fB, int *\fIsp\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fIsp\fR' specifies the pointer to a variable into which the size of the region of the return value is assigned. .RE .RS If successful, the return value is the pointer to the result object, else, it is `NULL'. .RE .RS 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. .RE .RE .PP The function `tcdeflate' is used in order to compress a serial object with Deflate encoding. .PP .RS .br \fBchar *tcdeflate(const char *\fIptr\fB, int \fIsize\fB, int *\fIsp\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS If successful, the return value is the pointer to the result object, else, it is `NULL'. .RE .RS 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. .RE .RE .PP The function `tcinflate' is used in order to decompress a serial object compressed with Deflate encoding. .PP .RS .br \fBchar *tcinflate(const char *\fIptr\fB, int \fIsize\fB, int *\fIsp\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fIsp\fR' specifies the pointer to a variable into which the size of the region of the return value is assigned. .RE .RS If successful, the return value is the pointer to the result object, else, it is `NULL'. .RE .RS 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. .RE .RE .PP The function `tcgzipencode' is used in order to compress a serial object with GZIP encoding. .PP .RS .br \fBchar *tcgzipencode(const char *\fIptr\fB, int \fIsize\fB, int *\fIsp\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS If successful, the return value is the pointer to the result object, else, it is `NULL'. .RE .RS 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. .RE .RE .PP The function `tcgzipdecode' is used in order to decompress a serial object compressed with GZIP encoding. .PP .RS .br \fBchar *tcgzipdecode(const char *\fIptr\fB, int \fIsize\fB, int *\fIsp\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fIsp\fR' specifies the pointer to a variable into which the size of the region of the return value is assigned. .RE .RS If successful, the return value is the pointer to the result object, else, it is `NULL'. .RE .RS 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. .RE .RE .PP The function `tcgetcrc' is used in order to get the CRC32 checksum of a serial object. .PP .RS .br \fBunsigned int tcgetcrc(const char *\fIptr\fB, int \fIsize\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS The return value is the CRC32 checksum of the object. .RE .RE .PP The function `tcbzipencode' is used in order to compress a serial object with BZIP2 encoding. .PP .RS .br \fBchar *tcbzipencode(const char *\fIptr\fB, int \fIsize\fB, int *\fIsp\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS If successful, the return value is the pointer to the result object, else, it is `NULL'. .RE .RS 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. .RE .RE .PP The function `tcbzipdecode' is used in order to decompress a serial object compressed with BZIP2 encoding. .PP .RS .br \fBchar *tcbzipdecode(const char *\fIptr\fB, int \fIsize\fB, int *\fIsp\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fIsp\fR' specifies the pointer to a variable into which the size of the region of the return value is assigned. .RE .RS If successful, the return value is the pointer to the result object, else, it is `NULL'. .RE .RS 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. .RE .RE .PP The function `tcberencode' is used in order to encode an array of nonnegative integers with BER encoding. .PP .RS .br \fBchar *tcberencode(const unsigned int *\fIary\fB, int \fIanum\fB, int *\fIsp\fB);\fR .RS `\fIary\fR' specifies the pointer to the array of nonnegative integers. .RE .RS `\fIanum\fR' specifies the size of the array. .RE .RS `\fIsp\fR' specifies the pointer to a variable into which the size of the region of the return value is assigned. .RE .RS The return value is the pointer to the region of the result. .RE .RS 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. .RE .RE .PP The function `tcberdecode' is used in order to decode a serial object encoded with BER encoding. .PP .RS .br \fBunsigned int *tcberdecode(const char *\fIptr\fB, int \fIsize\fB, int *\fInp\fB);\fR .RS `\fIptr\fR' specifies the pointer to the region. .RE .RS `\fIsize\fR' specifies the size of the region. .RE .RS `\fInp\fR' specifies the pointer to a variable into which the number of elements of the return value is assigned. .RE .RS The return value is the pointer to the array of the result. .RE .RS 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. .RE .RE .PP The function `tcxmlescape' is used in order to escape meta characters in a string with the entity references of XML. .PP .RS .br \fBchar *tcxmlescape(const char *\fIstr\fB);\fR .RS `\fIstr\fR' specifies the string. .RE .RS The return value is the pointer to the escaped string. .RE .RS 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. .RE .RE .PP The function `tcxmlunescape' is used in order to unescape entity references in a string of XML. .PP .RS .br \fBchar *tcxmlunescape(const char *\fIstr\fB);\fR .RS `\fIstr\fR' specifies the string. .RE .RS The return value is the unescaped string. .RE .RS This function restores 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. .RE .RE .SH SEE ALSO .PP .BR tcutest (1), .BR tcucodec (1), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tcatest.10000644000175000017500000000246012013574114015611 0ustar mikiomikio.TH "TCATEST" 3 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcatest \- test cases of the abstract database API .SH DESCRIPTION .PP The command `\fBtcatest\fR' is a utility for facility test and performance test. This command is used in the following format. `\fIname\fR' specifies the database name. `\fIrnum\fR' specifies the number of iterations. `\fItnum\fR' specifies the number of transactions. .PP .RS .br \fBtcatest write \fIname\fB \fIrnum\fB\fR .RS Store records with keys of 8 bytes. They change as `00000001', `00000002'... .RE .br \fBtcatest read \fIname\fB\fR .RS Retrieve all records of the database above. .RE .br \fBtcatest remove \fIname\fB\fR .RS Remove all records of the database above. .RE .br \fBtcatest rcat \fIname\fB \fIrnum\fB\fR .RS Store records with partway duplicated keys using concatenate mode. .RE .br \fBtcatest misc \fIname\fB \fIrnum\fB\fR .RS Perform miscellaneous test of various operations. .RE .br \fBtcatest wicked \fIname\fB \fIrnum\fB\fR .RS Perform updating operations of list and map selected at random. .RE .br \fBtcatest compare \fIname\fB \fItnum\fB \fIrnum\fB\fR .RS Perform comparison test of database schema. .RE .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tcamttest (1), .BR tcamgr (1), .BR tcadb (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tchdb.30000644000175000017500000007250312013574114015235 0ustar mikiomikio.TH "TCHDB" 3 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tchdb \- the hash database API .SH DESCRIPTION .PP Hash database is a file containing a hash table and is handled with the hash database API. .PP To use the hash database API, include `\fBtcutil.h\fR', `\fBtchdb.h\fR', and related standard header files. Usually, write the following description near the front of a source file. .PP .RS .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .RE .PP Objects whose type is pointer to `\fBTCHDB\fR' are used to handle hash databases. A hash database object is created with the function `\fBtchdbnew\fR' and is deleted with the function `\fBtchdbdel\fR'. To avoid memory leak, it is important to delete every object when it is no longer in use. .PP 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 `\fBtchdbopen\fR' is used to open a database file and the function `\fBtchdbclose\fR' 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. .SH API .PP The function `tchdberrmsg' is used in order to get the message string corresponding to an error code. .PP .RS .br \fBconst char *tchdberrmsg(int \fIecode\fB);\fR .RS `\fIecode\fR' specifies the error code. .RE .RS The return value is the message string of the error code. .RE .RE .PP The function `tchdbnew' is used in order to create a hash database object. .PP .RS .br \fBTCHDB *tchdbnew(void);\fR .RS The return value is the new hash database object. .RE .RE .PP The function `tchdbdel' is used in order to delete a hash database object. .PP .RS .br \fBvoid tchdbdel(TCHDB *\fIhdb\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS If the database is not closed, it is closed implicitly. Note that the deleted object and its derivatives can not be used anymore. .RE .RE .PP The function `tchdbecode' is used in order to get the last happened error code of a hash database object. .PP .RS .br \fBint tchdbecode(TCHDB *\fIhdb\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS The return value is the last happened error code. .RE .RS 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. .RE .RE .PP The function `tchdbsetmutex' is used in order to set mutual exclusion control of a hash database object for threading. .PP .RS .br \fBbool tchdbsetmutex(TCHDB *\fIhdb\fB);\fR .RS `\fIhdb\fR' specifies the hash database object which is not opened. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the mutual exclusion control of the database should be set before the database is opened. .RE .RE .PP The function `tchdbtune' is used in order to set the tuning parameters of a hash database object. .PP .RS .br \fBbool tchdbtune(TCHDB *\fIhdb\fB, int64_t \fIbnum\fB, int8_t \fIapow\fB, int8_t \fIfpow\fB, uint8_t \fIopts\fB);\fR .RS `\fIhdb\fR' specifies the hash database object which is not opened. .RE .RS `\fIbnum\fR' 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 16381. Suggested size of the bucket array is about from 0.5 to 4 times of the number of all records to be stored. .RE .RS `\fIapow\fR' 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. .RE .RS `\fIfpow\fR' 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. .RE .RS `\fIopts\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the tuning parameters should be set before the database is opened. .RE .RE .PP The function `tchdbsetcache' is used in order to set the caching parameters of a hash database object. .PP .RS .br \fBbool tchdbsetcache(TCHDB *\fIhdb\fB, int32_t \fIrcnum\fB);\fR .RS `\fIhdb\fR' specifies the hash database object which is not opened. .RE .RS `\fIrcnum\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the caching parameters should be set before the database is opened. .RE .RE .PP The function `tchdbsetxmsiz' is used in order to set the size of the extra mapped memory of a hash database object. .PP .RS .br \fBbool tchdbsetxmsiz(TCHDB *\fIhdb\fB, int64_t \fIxmsiz\fB);\fR .RS `\fIhdb\fR' specifies the hash database object which is not opened. .RE .RS `\fIxmsiz\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the mapping parameters should be set before the database is opened. .RE .RE .PP The function `tchdbsetdfunit' is used in order to set the unit step number of auto defragmentation of a hash database object. .PP .RS .br \fBbool tchdbsetdfunit(TCHDB *\fIhdb\fB, int32_t \fIdfunit\fB);\fR .RS `\fIhdb\fR' specifies the hash database object which is not opened. .RE .RS `\fIdfunit\fR' specifie the unit step number. If it is not more than 0, the auto defragmentation is disabled. It is disabled by default. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the defragmentation parameters should be set before the database is opened. .RE .RE .PP The function `tchdbopen' is used in order to open a database file and connect a hash database object. .PP .RS .br \fBbool tchdbopen(TCHDB *\fIhdb\fB, const char *\fIpath\fB, int \fIomode\fB);\fR .RS `\fIhdb\fR' specifies the hash database object which is not opened. .RE .RS `\fIpath\fR' specifies the path of the database file. .RE .RS `\fIomode\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tchdbclose' is used in order to close a hash database object. .PP .RS .br \fBbool tchdbclose(TCHDB *\fIhdb\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tchdbput' is used in order to store a record into a hash database object. .PP .RS .br \fBbool tchdbput(TCHDB *\fIhdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tchdbput2' is used in order to store a string record into a hash database object. .PP .RS .br \fBbool tchdbput2(TCHDB *\fIhdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tchdbputkeep' is used in order to store a new record into a hash database object. .PP .RS .br \fBbool tchdbputkeep(TCHDB *\fIhdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tchdbputkeep2' is used in order to store a new string record into a hash database object. .PP .RS .br \fBbool tchdbputkeep2(TCHDB *\fIhdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tchdbputcat' is used in order to concatenate a value at the end of the existing record in a hash database object. .PP .RS .br \fBbool tchdbputcat(TCHDB *\fIhdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tchdbputcat2' is used in order to concatenate a string value at the end of the existing record in a hash database object. .PP .RS .br \fBbool tchdbputcat2(TCHDB *\fIhdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tchdbputasync' is used in order to store a record into a hash database object in asynchronous fashion. .PP .RS .br \fBbool tchdbputasync(TCHDB *\fIhdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tchdbputasync2' is used in order to store a string record into a hash database object in asynchronous fashion. .PP .RS .br \fBbool tchdbputasync2(TCHDB *\fIhdb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tchdbout' is used in order to remove a record of a hash database object. .PP .RS .br \fBbool tchdbout(TCHDB *\fIhdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tchdbout2' is used in order to remove a string record of a hash database object. .PP .RS .br \fBbool tchdbout2(TCHDB *\fIhdb\fB, const char *\fIkstr\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tchdbget' is used in order to retrieve a record in a hash database object. .PP .RS .br \fBvoid *tchdbget(TCHDB *\fIhdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int *\fIsp\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tchdbget2' is used in order to retrieve a string record in a hash database object. .PP .RS .br \fBchar *tchdbget2(TCHDB *\fIhdb\fB, const char *\fIkstr\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is the string of the value of the corresponding record. `NULL' is returned if no record corresponds. .RE .RS 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. .RE .RE .PP The function `tchdbget3' is used in order to retrieve a record in a hash database object and write the value into a buffer. .PP .RS .br \fBint tchdbget3(TCHDB *\fIhdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, void *\fIvbuf\fB, int \fImax\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the buffer into which the value of the corresponding record is written. .RE .RS `\fImax\fR' specifies the size of the buffer. .RE .RS 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. .RE .RS Note that an additional zero code is not appended at the end of the region of the writing buffer. .RE .RE .PP The function `tchdbvsiz' is used in order to get the size of the value of a record in a hash database object. .PP .RS .br \fBint tchdbvsiz(TCHDB *\fIhdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RE .PP The function `tchdbvsiz2' is used in order to get the size of the value of a string record in a hash database object. .PP .RS .br \fBint tchdbvsiz2(TCHDB *\fIhdb\fB, const char *\fIkstr\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RE .PP The function `tchdbiterinit' is used in order to initialize the iterator of a hash database object. .PP .RS .br \fBbool tchdbiterinit(TCHDB *\fIhdb\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RS The iterator is used in order to access the key of every record stored in a database. .RE .RE .PP The function `tchdbiternext' is used in order to get the next key of the iterator of a hash database object. .PP .RS .br \fBvoid *tchdbiternext(TCHDB *\fIhdb\fB, int *\fIsp\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tchdbiternext2' is used in order to get the next key string of the iterator of a hash database object. .PP .RS .br \fBchar *tchdbiternext2(TCHDB *\fIhdb\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tchdbiternext3' is used in order to get the next extensible objects of the iterator of a hash database object. .PP .RS .br \fBbool tchdbiternext3(TCHDB *\fIhdb\fB, TCXSTR *\fIkxstr\fB, TCXSTR *\fIvxstr\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS `\fIkxstr\fR' specifies the object into which the next key is wrote down. .RE .RS `\fIvxstr\fR' specifies the object into which the next value is wrote down. .RE .RS 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. .RE .RE .PP The function `tchdbfwmkeys' is used in order to get forward matching keys in a hash database object. .PP .RS .br \fBTCLIST *tchdbfwmkeys(TCHDB *\fIhdb\fB, const void *\fIpbuf\fB, int \fIpsiz\fB, int \fImax\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS `\fIpbuf\fR' specifies the pointer to the region of the prefix. .RE .RS `\fIpsiz\fR' specifies the size of the region of the prefix. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tchdbfwmkeys2' is used in order to get forward matching string keys in a hash database object. .PP .RS .br \fBTCLIST *tchdbfwmkeys2(TCHDB *\fIhdb\fB, const char *\fIpstr\fB, int \fImax\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS `\fIpstr\fR' specifies the string of the prefix. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tchdbaddint' is used in order to add an integer to a record in a hash database object. .PP .RS .br \fBint tchdbaddint(TCHDB *\fIhdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int \fInum\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS If successful, the return value is the summation value, else, it is `INT_MIN'. .RE .RS 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. .RE .RE .PP The function `tchdbdbadddouble' is used in order to add a real number to a record in a hash database object. .PP .RS .br \fBdouble tchdbadddouble(TCHDB *\fIhdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, double \fInum\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS If successful, the return value is the summation value, else, it is Not-a-Number. .RE .RS 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. .RE .RE .PP The function `tchdbsync' is used in order to synchronize updated contents of a hash database object with the file and the device. .PP .RS .br \fBbool tchdbsync(TCHDB *\fIhdb\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS This function is useful when another process connects to the same database file. .RE .RE .PP The function `tchdboptimize' is used in order to optimize the file of a hash database object. .PP .RS .br \fBbool tchdboptimize(TCHDB *\fIhdb\fB, int64_t \fIbnum\fB, int8_t \fIapow\fB, int8_t \fIfpow\fB, uint8_t \fIopts\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS `\fIbnum\fR' 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. .RE .RS `\fIapow\fR' specifies the size of record alignment by power of 2. If it is negative, the current setting is not changed. .RE .RS `\fIfpow\fR' 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. .RE .RS `\fIopts\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS This function is useful to reduce the size of the database file with data fragmentation by successive updating. .RE .RE .PP The function `tchdbvanish' is used in order to remove all records of a hash database object. .PP .RS .br \fBbool tchdbvanish(TCHDB *\fIhdb\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tchdbcopy' is used in order to copy the database file of a hash database object. .PP .RS .br \fBbool tchdbcopy(TCHDB *\fIhdb\fB, const char *\fIpath\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS `\fIpath\fR' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line. .RE .RS If successful, the return value is true, else, it is false. False is returned if the executed command returns non\-zero code. .RE .RS 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. .RE .RE .PP The function `tchdbtranbegin' is used in order to begin the transaction of a hash database object. .PP .RS .br \fBbool tchdbtranbegin(TCHDB *\fIhdb\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tchdbtrancommit' is used in order to commit the transaction of a hash database object. .PP .RS .br \fBbool tchdbtrancommit(TCHDB *\fIhdb\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Update in the transaction is fixed when it is committed successfully. .RE .RE .PP The function `tchdbtranabort' is used in order to abort the transaction of a hash database object. .PP .RS .br \fBbool tchdbtranabort(TCHDB *\fIhdb\fB);\fR .RS `\fIhdb\fR' specifies the hash database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Update in the transaction is discarded when it is aborted. The state of the database is rollbacked to before transaction. .RE .RE .PP The function `tchdbpath' is used in order to get the file path of a hash database object. .PP .RS .br \fBconst char *tchdbpath(TCHDB *\fIhdb\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS The return value is the path of the database file or `NULL' if the object does not connect to any database file. .RE .RE .PP The function `tchdbrnum' is used in order to get the number of records of a hash database object. .PP .RS .br \fBuint64_t tchdbrnum(TCHDB *\fIhdb\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS The return value is the number of records or 0 if the object does not connect to any database file. .RE .RE .PP The function `tchdbfsiz' is used in order to get the size of the database file of a hash database object. .PP .RS .br \fBuint64_t tchdbfsiz(TCHDB *\fIhdb\fB);\fR .RS `\fIhdb\fR' specifies the hash database object. .RE .RS The return value is the size of the database file or 0 if the object does not connect to any database file. .RE .RE .SH SEE ALSO .PP .BR tchtest (1), .BR tchmttest (1), .BR tchmgr (1), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/htmltoman0000755000175000017500000000453311246746363016032 0ustar mikiomikio#! /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") } / *
    ]*> *$/ { printf(".PP\n") printf(".RS\n") } / *<\/ul> *$/ { printf(".RE\n") } / *
  • ]*>.*<\/li> *$/ { 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("%s\n", text) printf(".br\n") } END { printf("\n") printf(".SH SEE ALSO\n") printf(".PP\n") printf(".BR tcutil (3),\n") printf(".BR tchdb (3),\n") printf(".BR tcbdb (3),\n") printf(".BR tcfdb (3),\n") printf(".BR tctdb (3),\n") printf(".BR tcadb (3)\n") printf(".PP\n") printf("Please see\n") printf(".I http://1978th.net/tokyocabinet/\n") printf("for detail.\n") } tokyocabinet-1.4.48/man/tctmttest.10000644000175000017500000000747712013574114016212 0ustar mikiomikio.TH "TCTMTTEST" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tctmttest \- test cases of the table database API .SH DESCRIPTION .PP The command `\fBtctmttest\fR' is a utility for facility test under multi\-thread situation. This command is used in the following format. `\fIpath\fR' specifies the path of a database file. `\fItnum\fR' specifies the number of running threads. `\fIrnum\fR' specifies the number of iterations. `\fIbnum\fR' specifies the number of buckets. `\fIapow\fR' specifies the power of the alignment. `\fIfpow\fR' specifies the power of the free block pool. .PP .RS .br \fBtctmttest write \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-lc \fInum\fB\fR]\fB \fR[\fB\-nc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-ip\fR]\fB \fR[\fB\-is\fR]\fB \fR[\fB\-in\fR]\fB \fR[\fB\-it\fR]\fB \fR[\fB\-if\fR]\fB \fR[\fB\-ix\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fItnum\fB \fIrnum\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Store records with columns "str", "num", "type", and "flag". .RE .br \fBtctmttest read \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-lc \fInum\fB\fR]\fB \fR[\fB\-nc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fItnum\fB\fR .RS Retrieve all records of the database above. .RE .br \fBtctmttest remove \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-lc \fInum\fB\fR]\fB \fR[\fB\-nc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fItnum\fB\fR .RS Remove all records of the database above. .RE .br \fBtctmttest wicked \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB \fItnum\fB \fIrnum\fB\fR .RS Perform updating operations selected at random. .RE .br \fBtctmttest typical \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-lc \fInum\fB\fR]\fB \fR[\fB\-nc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rr \fInum\fB\fR]\fB \fIpath\fB \fItnum\fB \fIrnum\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR .RS Perform typical operations selected at random. .RE .RE .PP Options feature the following. .PP .RS \fB\-tl\fR : enable the option `TDBTLARGE'. .br \fB\-td\fR : enable the option `TDBTDEFLATE'. .br \fB\-tb\fR : enable the option `TDBTBZIP'. .br \fB\-tt\fR : enable the option `TDBTTCBS'. .br \fB\-tx\fR : enable the option `TDBTEXCODEC'. .br \fB\-rc \fInum\fR\fR : specify the number of cached records. .br \fB\-lc \fInum\fR\fR : specify the number of cached leaf pages. .br \fB\-nc \fInum\fR\fR : specify the number of cached non\-leaf pages. .br \fB\-xm \fInum\fR\fR : specify the size of the extra mapped memory. .br \fB\-df \fInum\fR\fR : specify the unit step number of auto defragmentation. .br \fB\-ip\fR : create the number index for the primary key. .br \fB\-is\fR : create the string index for the column "str". .br \fB\-in\fR : create the number index for the column "num". .br \fB\-it\fR : create the string index for the column "type". .br \fB\-if\fR : create the token inverted index for the column "flag". .br \fB\-ix\fR : create the q\-gram inverted index for the column "text". .br \fB\-nl\fR : enable the option `TDBNOLCK'. .br \fB\-nb\fR : enable the option `TDBLCKNB'. .br \fB\-rnd\fR : select keys at random. .br \fB\-nc\fR : omit the comparison test. .br \fB\-rr \fInum\fR\fR : specify the ratio of reading operation by percentage. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tcttest (1), .BR tctmgr (1), .BR tctdb (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tctree.30000644000175000017500000000002212013574114015422 0ustar mikiomikio.so man3/tcutil.3 tokyocabinet-1.4.48/man/tcmdb.30000644000175000017500000000002212013574114015225 0ustar mikiomikio.so man3/tcutil.3 tokyocabinet-1.4.48/man/tcttest.10000644000175000017500000001065612013574114015642 0ustar mikiomikio.TH "TCTTEST" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcttest \- test cases of the table database API .SH DESCRIPTION .PP The command `\fBtcttest\fR' is a utility for facility test and performance test. This command is used in the following format. `\fIpath\fR' specifies the path of a database file. `\fIrnum\fR' specifies the number of iterations. `\fIbnum\fR' specifies the number of buckets. `\fIapow\fR' specifies the power of the alignment. `\fIfpow\fR' specifies the power of the free block pool. .PP .RS .br \fBtcttest write \fR[\fB\-mt\fR]\fB \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-lc \fInum\fB\fR]\fB \fR[\fB\-nc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-ip\fR]\fB \fR[\fB\-is\fR]\fB \fR[\fB\-in\fR]\fB \fR[\fB\-it\fR]\fB \fR[\fB\-if\fR]\fB \fR[\fB\-ix\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fIrnum\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Store records with columns "str", "num", "type", and "flag". .RE .br \fBtcttest read \fR[\fB\-mt\fR]\fB \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-lc \fInum\fB\fR]\fB \fR[\fB\-nc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB\fR .RS Retrieve all records of the database above. .RE .br \fBtcttest remove \fR[\fB\-mt\fR]\fB \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-lc \fInum\fB\fR]\fB \fR[\fB\-nc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB\fR .RS Remove all records of the database above. .RE .br \fBtcttest rcat \fR[\fB\-mt\fR]\fB \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-lc \fInum\fB\fR]\fB \fR[\fB\-nc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-ip\fR]\fB \fR[\fB\-is\fR]\fB \fR[\fB\-in\fR]\fB \fR[\fB\-it\fR]\fB \fR[\fB\-if\fR]\fB \fR[\fB\-ix\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-pn \fInum\fB\fR]\fB \fR[\fB\-dai\fR|\fB\-dad\fR|\fB\-rl\fR|\fB\-ru\fR]\fB \fIpath\fB \fIrnum\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Store records with partway duplicated keys using concatenate mode. .RE .br \fBtcttest misc \fR[\fB\-mt\fR]\fB \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB \fIrnum\fB\fR .RS Perform miscellaneous test of various operations. .RE .br \fBtcttest wicked \fR[\fB\-mt\fR]\fB \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB \fIrnum\fB\fR .RS Perform updating operations selected at random. .RE .RE .PP Options feature the following. .PP .RS \fB\-mt\fR : call the function `tctdbsetmutex'. .br \fB\-tl\fR : enable the option `TDBTLARGE'. .br \fB\-td\fR : enable the option `TDBTDEFLATE'. .br \fB\-tb\fR : enable the option `TDBTBZIP'. .br \fB\-tt\fR : enable the option `TDBTTCBS'. .br \fB\-tx\fR : enable the option `TDBTEXCODEC'. .br \fB\-rc \fInum\fR\fR : specify the number of cached records. .br \fB\-lc \fInum\fR\fR : specify the number of cached leaf pages. .br \fB\-nc \fInum\fR\fR : specify the number of cached non\-leaf pages. .br \fB\-xm \fInum\fR\fR : specify the size of the extra mapped memory. .br \fB\-df \fInum\fR\fR : specify the unit step number of auto defragmentation. .br \fB\-ip\fR : create the number index for the primary key. .br \fB\-is\fR : create the string index for the column "str". .br \fB\-in\fR : create the number index for the column "num". .br \fB\-it\fR : create the string index for the column "type". .br \fB\-if\fR : create the token inverted index for the column "flag". .br \fB\-ix\fR : create the q\-gram inverted index for the column "text". .br \fB\-nl\fR : enable the option `TDBNOLCK'. .br \fB\-nb\fR : enable the option `TDBLCKNB'. .br \fB\-rnd\fR : select keys at random. .br \fB\-pn \fInum\fR\fR : specify the number of patterns. .br \fB\-dai\fR : use the function `tctdbaddint' instead of `tctdbputcat'. .br \fB\-dad\fR : use the function `tctdbadddouble' instead of `tctdbputcat'. .br \fB\-rl\fR : set the length of values at random. .br \fB\-ru\fR : select update operations at random. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tctmttest (1), .BR tctmgr (1), .BR tctdb (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tcfdb.30000644000175000017500000011576212013574114015240 0ustar mikiomikio.TH "TCFDB" 3 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcfdb \- the fixed-length database API .SH DESCRIPTION .PP Fixed\-length database is a file containing an array of fixed\-length elements and is handled with the fixed\-length database API. .PP To use the fixed\-length database API, include `\fBtcutil.h\fR', `\fBtcfdb.h\fR', and related standard header files. Usually, write the following description near the front of a source file. .PP .RS .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .RE .PP Objects whose type is pointer to `\fBTCFDB\fR' are used to handle fixed\-length databases. A fixed\-length database object is created with the function `\fBtcfdbnew\fR' and is deleted with the function `\fBtcfdbdel\fR'. To avoid memory leak, it is important to delete every object when it is no longer in use. .PP 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 `\fBtcfdbopen\fR' is used to open a database file and the function `\fBtcfdbclose\fR' 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. .SH API .PP The function `tcfdberrmsg' is used in order to get the message string corresponding to an error code. .PP .RS .br \fBconst char *tcfdberrmsg(int \fIecode\fB);\fR .RS `\fIecode\fR' specifies the error code. .RE .RS The return value is the message string of the error code. .RE .RE .PP The function `tcfdbnew' is used in order to create a fixed\-length database object. .PP .RS .br \fBTCFDB *tcfdbnew(void);\fR .RS The return value is the new fixed\-length database object. .RE .RE .PP The function `tcfdbdel' is used in order to delete a fixed\-length database object. .PP .RS .br \fBvoid tcfdbdel(TCFDB *\fIfdb\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS If the database is not closed, it is closed implicitly. Note that the deleted object and its derivatives can not be used anymore. .RE .RE .PP The function `tcfdbecode' is used in order to get the last happened error code of a fixed\-length database object. .PP .RS .br \fBint tcfdbecode(TCFDB *\fIfdb\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS The return value is the last happened error code. .RE .RS 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. .RE .RE .PP The function `tcfdbsetmutex' is used in order to set mutual exclusion control of a fixed\-length database object for threading. .PP .RS .br \fBbool tcfdbsetmutex(TCFDB *\fIfdb\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object which is not opened. .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tcfdbtune' is used in order to set the tuning parameters of a fixed\-length database object. .PP .RS .br \fBbool tcfdbtune(TCFDB *\fIfdb\fB, int32_t \fIwidth\fB, int64_t \fIlimsiz\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object which is not opened. .RE .RS `\fIwidth\fR' 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. .RE .RS `\fIlimsiz\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the tuning parameters should be set before the database is opened. .RE .RE .PP The function `tcfdbopen' is used in order to open a database file and connect a fixed\-length database object. .PP .RS .br \fBbool tcfdbopen(TCFDB *\fIfdb\fB, const char *\fIpath\fB, int \fIomode\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object which is not opened. .RE .RS `\fIpath\fR' specifies the path of the database file. .RE .RS `\fIomode\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcfdbclose' is used in order to close a fixed\-length database object. .PP .RS .br \fBbool tcfdbclose(TCFDB *\fIfdb\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tcfdbput' is used in order to store a record into a fixed\-length database object. .PP .RS .br \fBbool tcfdbput(TCFDB *\fIfdb\fB, int64_t \fIid\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS `\fIid\fR' 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. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcfdbput2' is used in order to store a record with a decimal key into a fixed\-length database object. .PP .RS .br \fBbool tcfdbput2(TCFDB *\fIfdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS `\fIkbuf\fR' 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. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcfdbput3' is used in order to store a string record with a decimal key into a fixed\-length database object. .PP .RS .br \fBbool tcfdbput3(TCFDB *\fIfdb\fB, const char *\fIkstr\fB, const void *\fIvstr\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS `\fIkstr\fR' 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. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcfdbputkeep' is used in order to store a new record into a fixed\-length database object. .PP .RS .br \fBbool tcfdbputkeep(TCFDB *\fIfdb\fB, int64_t \fIid\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS `\fIid\fR' 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. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tcfdbputkeep2' is used in order to store a new record with a decimal key into a fixed\-length database object. .PP .RS .br \fBbool tcfdbputkeep2(TCFDB *\fIfdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS `\fIkbuf\fR' 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. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tcfdbputkeep3' is used in order to store a new string record with a decimal key into a fixed\-length database object. .PP .RS .br \fBbool tcfdbputkeep3(TCFDB *\fIfdb\fB, const char *\fIkstr\fB, const void *\fIvstr\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS `\fIkstr\fR' 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. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tcfdbputcat' is used in order to concatenate a value at the end of the existing record in a fixed\-length database object. .PP .RS .br \fBbool tcfdbputcat(TCFDB *\fIfdb\fB, int64_t \fIid\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS `\fIid\fR' 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. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcfdbputcat2' is used in order to concatenate a value with a decimal key in a fixed\-length database object. .PP .RS .br \fBbool tcfdbputcat2(TCFDB *\fIfdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS `\fIkbuf\fR' 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. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcfdbputcat3' is used in order to concatenate a string value with a decimal key in a fixed\-length database object. .PP .RS .br \fBbool tcfdbputcat3(TCFDB *\fIfdb\fB, const char *\fIkstr\fB, const void *\fIvstr\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS `\fIkstr\fR' 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. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcfdbout' is used in order to remove a record of a fixed\-length database object. .PP .RS .br \fBbool tcfdbout(TCFDB *\fIfdb\fB, int64_t \fIid\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS `\fIid\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcfdbout2' is used in order to remove a record with a decimal key of a fixed\-length database object. .PP .RS .br \fBbool tcfdbout2(TCFDB *\fIfdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS `\fIkbuf\fR' 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. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcfdbout3' is used in order to remove a string record with a decimal key of a fixed\-length database object. .PP .RS .br \fBbool tcfdbout3(TCFDB *\fIfdb\fB, const char *\fIkstr\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS `\fIkstr\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcfdbget' is used in order to retrieve a record in a fixed\-length database object. .PP .RS .br \fBvoid *tcfdbget(TCFDB *\fIfdb\fB, int64_t \fIid\fB, int *\fIsp\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS `\fIid\fR' 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. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcfdbget2' is used in order to retrieve a record with a decimal key in a fixed\-length database object. .PP .RS .br \fBvoid *tcfdbget2(TCFDB *\fIfdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int *\fIsp\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS `\fIkbuf\fR' 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. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcfdbget3' is used in order to retrieve a string record with a decimal key in a fixed\-length database object. .PP .RS .br \fBchar *tcfdbget3(TCFDB *\fIfdb\fB, const char *\fIkstr\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS `\fIkstr\fR' 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. .RE .RS If successful, the return value is the string of the value of the corresponding record. `NULL' is returned if no record corresponds. .RE .RS 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. .RE .RE .PP The function `tcfdbget4' is used in order to retrieve a record in a fixed\-length database object and write the value into a buffer. .PP .RS .br \fBint tcfdbget4(TCFDB *\fIfdb\fB, int64_t \fIid\fB, void *\fIvbuf\fB, int \fImax\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS `\fIid\fR' 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. .RE .RS `\fIvbuf\fR' specifies the pointer to the buffer into which the value of the corresponding record is written. .RE .RS `\fImax\fR' specifies the size of the buffer. .RE .RS 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. .RE .RS Note that an additional zero code is not appended at the end of the region of the writing buffer. .RE .RE .PP The function `tcfdbvsiz' is used in order to get the size of the value of a record in a fixed\-length database object. .PP .RS .br \fBint tcfdbvsiz(TCFDB *\fIfdb\fB, int64_t \fIid\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS `\fIid\fR' 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. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RE .PP The function `tcfdbvsiz2' is used in order to get the size of the value with a decimal key in a fixed\-length database object. .PP .RS .br \fBint tcfdbvsiz2(TCFDB *\fIfdb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS `\fIkbuf\fR' 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. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RE .PP 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. .PP .RS .br \fBint tcfdbvsiz3(TCFDB *\fIfdb\fB, const char *\fIkstr\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS `\fIkstr\fR' 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. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RE .PP The function `tcfdbiterinit' is used in order to initialize the iterator of a fixed\-length database object. .PP .RS .br \fBbool tcfdbiterinit(TCFDB *\fIfdb\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RS The iterator is used in order to access the key of every record stored in a database. .RE .RE .PP The function `tcfdbiternext' is used in order to get the next ID number of the iterator of a fixed\-length database object. .PP .RS .br \fBuint64_t tcfdbiternext(TCFDB *\fIfdb\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcfdbiternext2' is used in order to get the next decimay key of the iterator of a fixed\-length database object. .PP .RS .br \fBvoid *tcfdbiternext2(TCFDB *\fIfdb\fB, int *\fIsp\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcfdbiternext3' is used in order to get the next decimay key string of the iterator of a fixed\-length database object. .PP .RS .br \fBchar *tcfdbiternext3(TCFDB *\fIfdb\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcfdbrange' is used in order to get range matching ID numbers in a fixed\-length database object. .PP .RS .br \fBuint64_t *tcfdbrange(TCFDB *\fIfdb\fB, int64_t \fIlower\fB, int64_t \fIupper\fB, int \fImax\fB, int *\fInp\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS `\fIlower\fR' specifies the lower limit of the range. If it is `FDBIDMIN', the minimum ID is specified. .RE .RS `\fIupper\fR' specifies the upper limit of the range. If it is `FDBIDMAX', the maximum ID is specified. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS `\fInp\fR' specifies the pointer to the variable into which the number of elements of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcfdbrange2' is used in order to get range matching decimal keys in a fixed\-length database object. .PP .RS .br \fBTCLIST *tcfdbrange2(TCFDB *\fIfdb\fB, const void *\fIlbuf\fB, int \fIlsiz\fB, const void *\fIubuf\fB, int \fIusiz\fB, int \fImax\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS `\fIlbuf\fR' specifies the pointer to the region of the lower key. If it is "min", the minimum ID number of existing records is specified. .RE .RS `\fIlsiz\fR' specifies the size of the region of the lower key. .RE .RS `\fIubuf\fR' specifies the pointer to the region of the upper key. If it is "max", the maximum ID number of existing records is specified. .RE .RS `\fIusiz\fR' specifies the size of the region of the upper key. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcfdbrange3' is used in order to get range matching decimal keys with strings in a fixed\-length database object. .PP .RS .br \fBTCLIST *tcfdbrange3(TCFDB *\fIfdb\fB, const char *\fIlstr\fB, const char *\fIustr\fB, int \fImax\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS `\fIlstr\fR' specifies the string of the lower key. If it is "min", the minimum ID number of existing records is specified. .RE .RS `\fIustr\fR' specifies the string of the upper key. If it is "max", the maximum ID number of existing records is specified. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcfdbrange4' is used in order to get keys with an interval notation in a fixed\-length database object. .PP .RS .br \fBTCLIST *tcfdbrange4(TCFDB *\fIfdb\fB, const void *\fIibuf\fB, int \fIisiz\fB, int \fImax\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS `\fIibuf\fR' specifies the pointer to the region of the interval notation. .RE .RS `\fIisiz\fR' specifies the size of the region of the interval notation. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcfdbrange5' is used in order to get keys with an interval notation string in a fixed\-length database object. .PP .RS .br \fBTCLIST *tcfdbrange5(TCFDB *\fIfdb\fB, const void *\fIistr\fB, int \fImax\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS `\fIistr\fR' specifies the pointer to the region of the interval notation string. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcfdbaddint' is used in order to add an integer to a record in a fixed\-length database object. .PP .RS .br \fBint tcfdbaddint(TCFDB *\fIfdb\fB, int64_t \fIid\fB, int \fInum\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS `\fIid\fR' 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. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS If successful, the return value is the summation value, else, it is `INT_MIN'. .RE .RS 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. .RE .RE .PP The function `tcfdbadddouble' is used in order to add a real number to a record in a fixed\-length database object. .PP .RS .br \fBdouble tcfdbadddouble(TCFDB *\fIfdb\fB, int64_t \fIid\fB, double \fInum\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS `\fIid\fR' 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. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS If successful, the return value is the summation value, else, it is Not\-a\-Number. .RE .RS 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. .RE .RE .PP The function `tcfdbsync' is used in order to synchronize updated contents of a fixed\-length database object with the file and the device. .PP .RS .br \fBbool tcfdbsync(TCFDB *\fIfdb\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS This function is useful when another process connects to the same database file. .RE .RE .PP The function `tcfdboptimize' is used in order to optimize the file of a fixed\-length database object. .PP .RS .br \fBbool tcfdboptimize(TCFDB *\fIfdb\fB, int32_t \fIwidth\fB, int64_t \fIlimsiz\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS `\fIwidth\fR' specifies the width of the value of each record. If it is not more than 0, the current setting is not changed. .RE .RS `\fIlimsiz\fR' specifies the limit size of the database file. If it is not more than 0, the current setting is not changed. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcfdbvanish' is used in order to remove all records of a fixed\-length database object. .PP .RS .br \fBbool tcfdbvanish(TCFDB *\fIfdb\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcfdbcopy' is used in order to copy the database file of a fixed\-length database object. .PP .RS .br \fBbool tcfdbcopy(TCFDB *\fIfdb\fB, const char *\fIpath\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS `\fIpath\fR' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line. .RE .RS If successful, the return value is true, else, it is false. False is returned if the executed command returns non\-zero code. .RE .RS 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. .RE .RE .PP The function `tcfdbtranbegin' is used in order to begin the transaction of a fixed\-length database object. .PP .RS .br \fBbool tcfdbtranbegin(TCFDB *\fIfdb\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tcfdbtrancommit' is used in order to commit the transaction of a fixed\-length database object. .PP .RS .br \fBbool tcfdbtrancommit(TCFDB *\fIfdb\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Update in the transaction is fixed when it is committed successfully. .RE .RE .PP The function `tcfdbtranabort' is used in order to abort the transaction of a fixed\-length database object. .PP .RS .br \fBbool tcfdbtranabort(TCFDB *\fIfdb\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Update in the transaction is discarded when it is aborted. The state of the database is rollbacked to before transaction. .RE .RE .PP The function `tcfdbpath' is used in order to get the file path of a fixed\-length database object. .PP .RS .br \fBconst char *tcfdbpath(TCFDB *\fIfdb\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS The return value is the path of the database file or `NULL' if the object does not connect to any database file. .RE .RE .PP The function `tcfdbrnum' is used in order to get the number of records of a fixed\-length database object. .PP .RS .br \fBuint64_t tcfdbrnum(TCFDB *\fIfdb\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS The return value is the number of records or 0 if the object does not connect to any database file. .RE .RE .PP The function `tcfdbfsiz' is used in order to get the size of the database file of a fixed\-length database object. .PP .RS .br \fBuint64_t tcfdbfsiz(TCFDB *\fIfdb\fB);\fR .RS `\fIfdb\fR' specifies the fixed\-length database object. .RE .RS The return value is the size of the database file or 0 if the object does not connect to any database file. .RE .RE .SH SEE ALSO .PP .BR tcftest (1), .BR tcfmttest (1), .BR tcfmgr (1), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tcbtest.10000644000175000017500000001213312013574114015610 0ustar mikiomikio.TH "TCBTEST" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcbtest \- test cases of the B+ tree database API .SH DESCRIPTION The command `\fBtcbtest\fR' is a utility for facility test and performance test. This command is used in the following format. `\fIpath\fR' specifies the path of a database file. `\fIrnum\fR' specifies the number of iterations. `\fIlmemb\fR' specifies the number of members in each leaf page. `\fInmemb\fR' specifies the number of members in each non\-leaf page. `\fIbnum\fR' specifies the number of buckets. `\fIapow\fR' specifies the power of the alignment. `\fIfpow\fR' specifies the power of the free block pool. .PP .RS .br \fBtcbtest write \fR[\fB\-mt\fR]\fB \fR[\fB\-cd\fR|\fB\-ci\fR|\fB\-cj\fR]\fB \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-lc \fInum\fB\fR]\fB \fR[\fB\-nc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-ls \fInum\fB\fR]\fB \fR[\fB\-ca \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fIrnum\fB \fR[\fB\fIlmemb\fB \fR[\fB\fInmemb\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Store records with keys of 8 bytes. They change as `00000001', `00000002'... .RE .br \fBtcbtest read \fR[\fB\-mt\fR]\fB \fR[\fB\-cd\fR|\fB\-ci\fR|\fB\-cj\fR]\fB \fR[\fB\-lc \fInum\fB\fR]\fB \fR[\fB\-nc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-wb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB\fR .RS Retrieve all records of the database above. .RE .br \fBtcbtest remove \fR[\fB\-mt\fR]\fB \fR[\fB\-cd\fR|\fB\-ci\fR|\fB\-cj\fR]\fB \fR[\fB\-lc \fInum\fB\fR]\fB \fR[\fB\-nc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB\fR .RS Remove all records of the database above. .RE .br \fBtcbtest rcat \fR[\fB\-mt\fR]\fB \fR[\fB\-cd\fR|\fB\-ci\fR|\fB\-cj\fR]\fB \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-lc \fInum\fB\fR]\fB \fR[\fB\-nc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-ls \fInum\fB\fR]\fB \fR[\fB\-ca \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-pn \fInum\fB\fR]\fB \fR[\fB\-dai\fR|\fB\-dad\fR|\fB\-rl\fR|\fB\-ru\fR]\fB \fIpath\fB \fIrnum\fB \fR[\fB\fIlmemb\fB \fR[\fB\fInmemb\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Store records with partway duplicated keys using concatenate mode. .RE .br \fBtcbtest queue \fR[\fB\-mt\fR]\fB \fR[\fB\-cd\fR|\fB\-ci\fR|\fB\-cj\fR]\fB \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-lc \fInum\fB\fR]\fB \fR[\fB\-nc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-ls \fInum\fB\fR]\fB \fR[\fB\-ca \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB \fIrnum\fB \fR[\fB\fIlmemb\fB \fR[\fB\fInmemb\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Perform queueing and dequeueing. .RE .br \fBtcbtest misc \fR[\fB\-mt\fR]\fB \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB \fIrnum\fB\fR .RS Perform miscellaneous test of various operations. .RE .br \fBtcbtest wicked \fR[\fB\-mt\fR]\fB \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB \fIrnum\fB\fR .RS Perform updating operations selected at random. .RE .RE .PP Options feature the following. .PP .RS \fB\-mt\fR : call the function `tchdbsetmutex'. .br \fB\-cd\fR : use the comparison function `tccmpdecimal'. .br \fB\-ci\fR : use the comparison function `tccmpint32'. .br \fB\-cj\fR : use the comparison function `tccmpint64'. .br \fB\-tl\fR : enable the option `BDBTLARGE'. .br \fB\-td\fR : enable the option `BDBTDEFLATE'. .br \fB\-tb\fR : enable the option `BDBTBZIP'. .br \fB\-tt\fR : enable the option `BDBTTCBS'. .br \fB\-tx\fR : enable the option `BDBTEXCODEC'. .br \fB\-lc \fInum\fR\fR : specify the number of cached leaf pages. .br \fB\-nc \fInum\fR\fR : specify the number of cached non\-leaf pages. .br \fB\-xm \fInum\fR\fR : specify the size of the extra mapped memory. .br \fB\-df \fInum\fR\fR : specify the unit step number of auto defragmentation. .br \fB\-ls \fInum\fR\fR : specify the maximum size of each leaf page. .br \fB\-ca \fInum\fR\fR : specify the capacity number of records. .br \fB\-nl\fR : enable the option `BDBNOLCK'. .br \fB\-nb\fR : enable the option `BDBLCKNB'. .br \fB\-rnd\fR : select keys at random. .br \fB\-wb\fR : use the function `tcbdbget3' instead of `tcbdbget'. .br \fB\-pn \fInum\fR\fR : specify the number of patterns. .br \fB\-dai\fR : use the function `tcbdbaddint' instead of `tcbdbputcat'. .br \fB\-dad\fR : use the function `tcbdbadddouble' instead of `tcbdbputcat'. .br \fB\-rl\fR : set the length of values at random. .br \fB\-ru\fR : select update operations at random. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tcbmttest (1), .BR tcbmgr (1), .BR tcbdb (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tctdb.30000644000175000017500000011655512013574114015257 0ustar mikiomikio.TH "TCTDB" 3 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tctdb \- the table database API .SH DESCRIPTION .PP Table database is a file containing records composed of the primary keys and arbitrary columns and is handled with the table database API. .PP To use the table database API, include `\fBtcutil.h\fR', `\fBtctdb.h\fR', and related standard header files. Usually, write the following description near the front of a source file. .PP .RS .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .RE .PP Objects whose type is pointer to `\fBTCTDB\fR' are used to handle table databases. A table database object is created with the function `\fBtctdbnew\fR' and is deleted with the function `\fBtctdbdel\fR'. To avoid memory leak, it is important to delete every object when it is no longer in use. .PP 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 `\fBtctdbopen\fR' is used to open a database file and the function `\fBtctdbclose\fR' 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. .SH API .PP The function `tctdberrmsg' is used in order to get the message string corresponding to an error code. .PP .RS .br \fBconst char *tctdberrmsg(int \fIecode\fB);\fR .RS `\fIecode\fR' specifies the error code. .RE .RS The return value is the message string of the error code. .RE .RE .PP The function `tctdbnew' is used in order to create a table database object. .PP .RS .br \fBTCTDB *tctdbnew(void);\fR .RS The return value is the new table database object. .RE .RE .PP The function `tctdbdel' is used in order to delete a table database object. .PP .RS .br \fBvoid tctdbdel(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS If the database is not closed, it is closed implicitly. Note that the deleted object and its derivatives can not be used anymore. .RE .RE .PP The function `tctdbecode' is used in order to get the last happened error code of a table database object. .PP .RS .br \fBint tctdbecode(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS The return value is the last happened error code. .RE .RS 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. .RE .RE .PP The function `tctdbsetmutex' is used in order to set mutual exclusion control of a table database object for threading. .PP .RS .br \fBbool tctdbsetmutex(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object which is not opened. .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tctdbtune' is used in order to set the tuning parameters of a table database object. .PP .RS .br \fBbool tctdbtune(TCTDB *\fItdb\fB, int64_t \fIbnum\fB, int8_t \fIapow\fB, int8_t \fIfpow\fB, uint8_t \fIopts\fB);\fR .RS `\fItdb\fR' specifies the table database object which is not opened. .RE .RS `\fIbnum\fR' 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. .RE .RS `\fIapow\fR' 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. .RE .RS `\fIfpow\fR' 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. .RE .RS `\fIopts\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the tuning parameters should be set before the database is opened. .RE .RE .PP The function `tctdbsetcache' is set the caching parameters of a table database object. .PP .RS .br \fBbool tctdbsetcache(TCTDB *\fItdb\fB, int32_t \fIrcnum\fB, int32_t \fIlcnum\fB, int32_t \fIncnum\fB);\fR .RS `\fItdb\fR' specifies the table database object which is not opened. .RE .RS `\fIrcnum\fR' 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. .RE .RS `\fIlcnum\fR' 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. .RE .RS `\fIncnum\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the caching parameters should be set before the database is opened. Leaf nodes and non\-leaf nodes are used in column indices. .RE .RE .PP The function `tctdbsetxmsiz' is used in order to set the size of the extra mapped memory of a table database object. .PP .RS .br \fBbool tctdbsetxmsiz(TCTDB *\fItdb\fB, int64_t \fIxmsiz\fB);\fR .RS `\fItdb\fR' specifies the table database object which is not opened. .RE .RS `\fIxmsiz\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the mapping parameters should be set before the database is opened. .RE .RE .PP The function `tctdbsetdfunit' is used in order to set the unit step number of auto defragmentation of a table database object. .PP .RS .br \fBbool tctdbsetdfunit(TCTDB *\fItdb\fB, int32_t \fIdfunit\fB);\fR .RS `\fItdb\fR' specifies the table database object which is not opened. .RE .RS `\fIdfunit\fR' specifie the unit step number. If it is not more than 0, the auto defragmentation is disabled. It is disabled by default. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the defragmentation parameters should be set before the database is opened. .RE .RE .PP The function `tctdbopen' is used in order to open a database file and connect a table database object. .PP .RS .br \fBbool tctdbopen(TCTDB *\fItdb\fB, const char *\fIpath\fB, int \fIomode\fB);\fR .RS `\fItdb\fR' specifies the table database object which is not opened. .RE .RS `\fIpath\fR' specifies the path of the database file. .RE .RS `\fIomode\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tctdbclose' is used in order to close a table database object. .PP .RS .br \fBbool tctdbclose(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tctdbput' is used in order to store a record into a table database object. .PP .RS .br \fBbool tctdbput(TCTDB *\fItdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB, TCMAP *\fIcols\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS `\fIpkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIpksiz\fR' specifies the size of the region of the primary key. .RE .RS `\fIcols\fR' specifies a map object containing columns. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tctdbput2' is used in order to store a string record into a table database object with a zero separated column string. .PP .RS .br \fBbool tctdbput2(TCTDB *\fItdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB, const void *\fIcbuf\fB, int \fIcsiz\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS `\fIpkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIpksiz\fR' specifies the size of the region of the primary key. .RE .RS `\fIcbuf\fR' 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. .RE .RS `\fIcsiz\fR' specifies the size of the region of the column string. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tctdbput3' is used in order to store a string record into a table database object with a tab separated column string. .PP .RS .br \fBbool tctdbput3(TCTDB *\fItdb\fB, const char *\fIpkstr\fB, const char *\fIcstr\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS `\fIpkstr\fR' specifies the string of the primary key. .RE .RS `\fIcstr\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tctdbputkeep' is used in order to store a new record into a table database object. .PP .RS .br \fBbool tctdbputkeep(TCTDB *\fItdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB, TCMAP *\fIcols\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS `\fIpkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIpksiz\fR' specifies the size of the region of the primary key. .RE .RS `\fIcols\fR' specifies a map object containing columns. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tctdbputkeep2' is used in order to store a new string record into a table database object with a zero separated column string. .PP .RS .br \fBbool tctdbputkeep2(TCTDB *\fItdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB, const void *\fIcbuf\fB, int \fIcsiz\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS `\fIpkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIpksiz\fR' specifies the size of the region of the primary key. .RE .RS `\fIcbuf\fR' 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. .RE .RS `\fIcsiz\fR' specifies the size of the region of the column string. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tctdbputkeep3' is used in order to store a new string record into a table database object with a tab separated column string. .PP .RS .br \fBbool tctdbputkeep3(TCTDB *\fItdb\fB, const char *\fIpkstr\fB, const char *\fIcstr\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS `\fIpkstr\fR' specifies the string of the primary key. .RE .RS `\fIcstr\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tctdbputcat' is used in order to concatenate columns of the existing record in a table database object. .PP .RS .br \fBbool tctdbputcat(TCTDB *\fItdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB, TCMAP *\fIcols\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS `\fIpkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIpksiz\fR' specifies the size of the region of the primary key. .RE .RS `\fIcols\fR' specifies a map object containing columns. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tctdbputcat2' is used in order to concatenate columns in a table database object with a zero separated column string. .PP .RS .br \fBbool tctdbputcat2(TCTDB *\fItdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB, const void *\fIcbuf\fB, int \fIcsiz\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS `\fIpkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIpksiz\fR' specifies the size of the region of the primary key. .RE .RS `\fIcbuf\fR' 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. .RE .RS `\fIcsiz\fR' specifies the size of the region of the column string. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tctdbputcat3' is used in order to concatenate columns in a table database object with with a tab separated column string. .PP .RS .br \fBbool tctdbputcat3(TCTDB *\fItdb\fB, const char *\fIpkstr\fB, const char *\fIcstr\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS `\fIpkstr\fR' specifies the string of the primary key. .RE .RS `\fIcstr\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tctdbout' is used in order to remove a record of a table database object. .PP .RS .br \fBbool tctdbout(TCTDB *\fItdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS `\fIpkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIpksiz\fR' specifies the size of the region of the primary key. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tctdbout2' is used in order to remove a string record of a table database object. .PP .RS .br \fBbool tctdbout2(TCTDB *\fItdb\fB, const char *\fIpkstr\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS `\fIpkstr\fR' specifies the string of the primary key. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tctdbget' is used in order to retrieve a record in a table database object. .PP .RS .br \fBTCMAP *tctdbget(TCTDB *\fItdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS `\fIpkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIpksiz\fR' specifies the size of the region of the primary key. .RE .RS If successful, the return value is a map object of the columns of the corresponding record. `NULL' is returned if no record corresponds. .RE .RS 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. .RE .RE .PP The function `tctdbget2' is used in order to retrieve a record in a table database object as a zero separated column string. .PP .RS .br \fBchar *tctdbget2(TCTDB *\fItdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB, int *\fIsp\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS `\fIpkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIpksiz\fR' specifies the size of the region of the primary key. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tctdbget3' is used in order to retrieve a string record in a table database object as a tab separated column string. .PP .RS .br \fBchar *tctdbget3(TCTDB *\fItdb\fB, const char *\fIpkstr\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS `\fIpkstr\fR' specifies the string of the primary key. .RE .RS If successful, the return value is the tab separated column string of the corresponding record. `NULL' is returned if no record corresponds. .RE .RS 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. .RE .RE .PP The function `tctdbvsiz' is used in order to get the size of the value of a record in a table database object. .PP .RS .br \fBint tctdbvsiz(TCTDB *\fItdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIksiz\fR' specifies the size of the region of the primary key. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RE .PP The function `tctdbvsiz2' is used in order to get the size of the value of a string record in a table database object. .PP .RS .br \fBint tctdbvsiz2(TCTDB *\fItdb\fB, const char *\fIpkstr\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS `\fIkstr\fR' specifies the string of the primary key. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RE .PP The function `tctdbiterinit' is used in order to initialize the iterator of a table database object. .PP .RS .br \fBbool tctdbiterinit(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RS The iterator is used in order to access the primary key of every record stored in a database. .RE .RE .PP The function `tctdbiternext' is used in order to get the next primary key of the iterator of a table database object. .PP .RS .br \fBvoid *tctdbiternext(TCTDB *\fItdb\fB, int *\fIsp\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tctdbiternext2' is used in order to get the next primary key string of the iterator of a table database object. .PP .RS .br \fBchar *tctdbiternext2(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tctdbiternext3' is used in order to get the columns of the next record of the iterator of a table database object. .PP .RS .br \fBTCMAP *tctdbiternext3(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tctdbfwmkeys' is used in order to get forward matching primary keys in a table database object. .PP .RS .br \fBTCLIST *tctdbfwmkeys(TCTDB *\fItdb\fB, const void *\fIpbuf\fB, int \fIpsiz\fB, int \fImax\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS `\fIpbuf\fR' specifies the pointer to the region of the prefix. .RE .RS `\fIpsiz\fR' specifies the size of the region of the prefix. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tctdbfwmkeys2' is used in order to get forward matching string primary keys in a table database object. .PP .RS .br \fBTCLIST *tctdbfwmkeys2(TCTDB *\fItdb\fB, const char *\fIpstr\fB, int \fImax\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS `\fIpstr\fR' specifies the string of the prefix. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tctdbaddint' is used in order to add an integer to a column of a record in a table database object. .PP .RS .br \fBint tctdbaddint(TCTDB *\fItdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB, int \fInum\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIksiz\fR' specifies the size of the region of the primary key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS If successful, the return value is the summation value, else, it is `INT_MIN'. .RE .RS 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. .RE .RE .PP The function `tctdbadddouble' is used in order to add a real number to a column of a record in a table database object. .PP .RS .br \fBdouble tctdbadddouble(TCTDB *\fItdb\fB, const void *\fIpkbuf\fB, int \fIpksiz\fB, double \fInum\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the primary key. .RE .RS `\fIksiz\fR' specifies the size of the region of the primary key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS If successful, the return value is the summation value, else, it is Not\-a\-Number. .RE .RS 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. .RE .RE .PP The function `tctdbsync' is used in order to synchronize updated contents of a table database object with the file and the device. .PP .RS .br \fBbool tctdbsync(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS This function is useful when another process connects to the same database file. .RE .RE .PP The function `tctdboptimize' is used in order to optimize the file of a table database object. .PP .RS .br \fBbool tctdboptimize(TCTDB *\fItdb\fB, int64_t \fIbnum\fB, int8_t \fIapow\fB, int8_t \fIfpow\fB, uint8_t \fIopts\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS `\fIbnum\fR' 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. .RE .RS `\fIapow\fR' specifies the size of record alignment by power of 2. If it is negative, the current setting is not changed. .RE .RS `\fIfpow\fR' 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. .RE .RS `\fIopts\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS This function is useful to reduce the size of the database file with data fragmentation by successive updating. .RE .RE .PP The function `tctdbvanish' is used in order to remove all records of a table database object. .PP .RS .br \fBbool tctdbvanish(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tctdbcopy' is used in order to copy the database file of a table database object. .PP .RS .br \fBbool tctdbcopy(TCTDB *\fItdb\fB, const char *\fIpath\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS `\fIpath\fR' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line. .RE .RS If successful, the return value is true, else, it is false. False is returned if the executed command returns non\-zero code. .RE .RS 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. .RE .RE .PP The function `tctdbtranbegin' is used in order to begin the transaction of a table database object. .PP .RS .br \fBbool tctdbtranbegin(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tctdbtrancommit' is used in order to commit the transaction of a table database object. .PP .RS .br \fBbool tctdbtrancommit(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Update in the transaction is fixed when it is committed successfully. .RE .RE .PP The function `tctdbtranabort' is used in order to abort the transaction of a table database object. .PP .RS .br \fBbool tctdbtranabort(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Update in the transaction is discarded when it is aborted. The state of the database is rollbacked to before transaction. .RE .RE .PP The function `tctdbpath' is used in order to get the file path of a table database object. .PP .RS .br \fBconst char *tctdbpath(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS The return value is the path of the database file or `NULL' if the object does not connect to any database file. .RE .RE .PP The function `tctdbrnum' is used in order to get the number of records ccccof a table database object. .PP .RS .br \fBuint64_t tctdbrnum(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS The return value is the number of records or 0 if the object does not connect to any database file. .RE .RE .PP The function `tctdbfsiz' is used in order to get the size of the database file of a table database object. .PP .RS .br \fBuint64_t tctdbfsiz(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS The return value is the size of the database file or 0 if the object does not connect to any database file. .RE .RE .PP The function `tctdbsetindex' is used in order to set a column index to a table database object. .PP .RS .br \fBbool tctdbsetindex(TCTDB *\fItdb\fB, const char *\fIname\fB, int \fItype\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS `\fIname\fR' 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. .RE .RS `\fItype\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Note that the setting indices should be set after the database is opened. .RE .RE .PP The function `tctdbgenuid' is used in order to generate a unique ID number of a table database object. .PP .RS .br \fBint64_t tctdbgenuid(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object connected as a writer. .RE .RS The return value is the new unique ID number or \-1 on failure. .RE .RE .PP The function `tctdbqrynew' is used in order to create a query object. .PP .RS .br \fBTDBQRY *tctdbqrynew(TCTDB *\fItdb\fB);\fR .RS `\fItdb\fR' specifies the table database object. .RE .RS The return value is the new query object. .RE .RE .PP The function `tctdbqrydel' is used in order to delete a query object. .PP .RS .br \fBvoid tctdbqrydel(TDBQRY *\fIqry\fB);\fR .RS `\fIqry\fR' specifies the query object. .RE .RE .PP The function `tctdbqryaddcond' is used in order to add a narrowing condition to a query object. .PP .RS .br \fBvoid tctdbqryaddcond(TDBQRY *\fIqry\fB, const char *\fIname\fB, int \fIop\fB, const char *\fIexpr\fB);\fR .RS `\fIqry\fR' specifies the query object. .RE .RS `\fIname\fR' specifies the name of a column. An empty string means the primary key. .RE .RS `\fIop\fR' 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. .RE .RS `\fIexpr\fR' specifies an operand exression. .RE .RE .PP The function `tctdbqrysetorder' is used in order to set the order of a query object. .PP .RS .br \fBvoid tctdbqrysetorder(TDBQRY *\fIqry\fB, const char *\fIname\fB, int \fItype\fB);\fR .RS `\fIqry\fR' specifies the query object. .RE .RS `\fIname\fR' specifies the name of a column. An empty string means the primary key. .RE .RS `\fItype\fR' specifies the order type: `TDBQOSTRASC' for string ascending, `TDBQOSTRDESC' for string descending, `TDBQONUMASC' for number ascending, `TDBQONUMDESC' for number descending. .RE .RE .PP The function `tctdbqrysetlimit' is used in order to set the limit number of records of the result of a query object. .PP .RS .br \fBvoid tctdbqrysetlimit(TDBQRY *\fIqry\fB, int \fImax\fB, int \fIskip\fB);\fR .RS `\fIqry\fR' specifies the query object. .RE .RS `\fImax\fR' specifies the maximum number of records of the result. If it is negative, no limit is specified. .RE .RS `\fIskip\fR' specifies the number of skipped records of the result. If it is not more than 0, no record is skipped. .RE .RE .PP The function `tctdbqrysearch' is used in order to execute the search of a query object. .PP .RS .br \fBTCLIST *tctdbqrysearch(TDBQRY *\fIqry\fB);\fR .RS `\fIqry\fR' specifies the query object. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tctdbqrysearchout' is used in order to remove each record corresponding to a query object. .PP .RS .br \fBbool tctdbqrysearchout(TDBQRY *\fIqry\fB);\fR .RS `\fIqry\fR' specifies the query object of the database connected as a writer. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tctdbqryproc' is used in order to process each record corresponding to a query object. .PP .RS .br \fBbool tctdbqryproc(TDBQRY *\fIqry\fB, TDBQRYPROC \fIproc\fB, void *\fIop\fB);\fR .RS `\fIqry\fR' specifies the query object of the database connected as a writer. .RE .RS `\fIproc\fR' 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. .RE .RS `\fIop\fR' specifies an arbitrary pointer to be given as a parameter of the iterator function. If it is not needed, `NULL' can be specified. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tctdbqryhint' is used in order to get the hint string of a query object. .PP .RS .br \fBconst char *tctdbqryhint(TDBQRY *\fIqry\fB);\fR .RS `\fIqry\fR' specifies the query object. .RE .RS The return value is the hint string. .RE .RE .PP The function `tctdbmetasearch' is used in order to retrieve records with multiple query objects and get the set of the result. .PP .RS .br \fBTCLIST *tctdbmetasearch(TDBQRY **\fIqrys\fB, int \fInum\fB, int \fItype\fB);\fR .RS `\fIqrys\fR' specifies an array of the query objects. .RE .RS `\fInum\fR' specifies the number of elements of the array. .RE .RS `\fItype\fR' specifies a set operation type: `TDBMSUNION' for the union set, `TDBMSISECT' for the intersection set, `TDBMSDIFF' for the difference set. .RE .RS 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. .RE .RS 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. .RE .RE .SH SEE ALSO .PP .BR tcttest (1), .BR tctmttest (1), .BR tctmgr (1), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tcmap.30000644000175000017500000000002212013574114015240 0ustar mikiomikio.so man3/tcutil.3 tokyocabinet-1.4.48/man/tcucodec.10000644000175000017500000001066112013574114015735 0ustar mikiomikio.TH "TCUCODEC" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcucodec \- popular encoders and decoders .SH DESCRIPTION .PP The command `\fBtcucodec\fR' is a tool to use encoding and decoding features. This command is used in the following format. `\fIfile\fR' specifies a input file. If it is omitted, the standard input is read. .PP .RS .br \fBtcucodec url \fR[\fB\-d\fR]\fB \fR[\fB\-br\fR]\fB \fR[\fB\-rs \fIbase\fB\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Perform URL encoding and its decoding. .RE .br \fBtcucodec base \fR[\fB\-d\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Perform Base64 encoding and its decoding. .RE .br \fBtcucodec quote \fR[\fB\-d\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Perform quoted\-printable encoding and its decoding. .RE .br \fBtcucodec mime \fR[\fB\-d\fR]\fB \fR[\fB\-en \fIname\fB\fR]\fB \fR[\fB\-q\fR]\fB \fR[\fB\-on\fR]\fB \fR[\fB\-hd\fR]\fB \fR[\fB\-bd\fR]\fB \fR[\fB\-part \fInum\fB\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Perform MIME encoding and its decoding. .RE .br \fBtcucodec hex \fR[\fB\-d\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Perform hexadecimal encoding and its decoding. .RE .br \fBtcucodec pack \fR[\fB\-d\fR]\fB \fR[\fB\-bwt\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Perform Packbits encoding and its decoding. .RE .br \fBtcucodec tcbs \fR[\fB\-d\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Perform TCBS encoding and its decoding. .RE .br \fBtcucodec zlib \fR[\fB\-d\fR]\fB \fR[\fB\-gz\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Perform ZLIB encoding and its decoding. .RE .br \fBtcucodec bzip \fR[\fB\-d\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Perform BZIP2 encoding and its decoding. .RE .br \fBtcucodec xml \fR[\fB\-d\fR]\fB \fR[\fB\-br\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Process XML. By default, escape meta characters. .RE .br \fBtcucodec cstr \fR[\fB\-d\fR]\fB \fR[\fB\-js\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Perform C\-string escaping and its unescaping. .RE .br \fBtcucodec ucs \fR[\fB\-d\fR]\fB \fR[\fB\-un\fR]\fB \fR[\fB\-kw \fIstr\fB\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Convert UTF\-8 string into UCS\-2 array. .RE .br \fBtcucodec hash \fR[\fB\-crc\fR]\fB \fR[\fB\-ch \fInum\fB\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Calculate the hash value. By default, use MD5 function. .RE .br \fBtcucodec cipher \fR[\fB\-key \fIstr\fB\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Perform stream cipher and its decipher. .RE .br \fBtcucodec date \fR[\fB\-ds \fIstr\fB\fR]\fB \fR[\fB\-jl \fInum\fB\fR]\fB \fR[\fB\-wf\fR]\fB \fR[\fB\-rf\fR]\fB\fR .RS Process date string. By default, print the current UNIX time. .RE .br \fBtcucodec tmpl \fR[\fB\-var \fIname\fB \fIvalue\fB\fR]\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Perform template serialization. .RE .br \fBtcucodec conf \fR[\fB\-v\fR|\fB\-i\fR|\fB\-l\fR|\fB\-p\fR]\fB\fR .RS Print some configurations. .RE .RE .PP Options feature the following. .PP .RS \fB\-d\fR : perform decoding (unescaping), not encoding (escaping). .br \fB\-br\fR : break up URL or XML into elements. .br \fB\-rs \fIbase\fR\fR : specify the base URL and resolve the relative URL. .br \fB\-en \fIname\fR\fR : specify the input encoding, which is UTF\-8 by default. .br \fB\-q\fR : use quoted\-printable encoding, which is Base64 by default. .br \fB\-on\fR : output the charset name when decoding. .br \fB\-bd\fR : perform MIME parsing and output the body. .br \fB\-hd\fR : perform MIME parsing and output the headers. .br \fB\-part \fInum\fR\fR : perform MIME parsing and output the specified part. .br \fB\-bwt\fR : convert by BWT as preprocessing. .br \fB\-gz\fR : use GZIP format. .br \fB\-crc\fR : use CRC32 function. .br \fB\-js\fR : use JSON compatible format. .br \fB\-un\fR : perform UCS normalization. .br \fB\-kw \fIstr\fR\fR : generate KWIC string. .br \fB\-ch \fInum\fR\fR : use consistent hashing function. .br \fB\-key \fIstr\fR\fR : specify the cipher key. .br \fB\-ds \fIstr\fR\fR : specify the time. .br \fB\-jl \fInum\fR\fR : specify the jet lag. .br \fB\-wf\fR : format the output in W3CDTF. .br \fB\-rf\fR : format the output in RFC 1123 format. .br \fB\-var \fIname\fR \fIvalue\fR\fR : specify a template variable. .br \fB\-v\fR : show the version number of Tokyo Cabinet. .br \fB\-i\fR : show options to include the headers of Tokyo Cabinet. .br \fB\-l\fR : show options to link the library of Tokyo Cabinet. .br \fB\-p\fR : show the directory path of the commands of Tokyo Cabinet. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tcutest (1), .BR tcumttest (1), .BR tcutil (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tcfmttest.10000644000175000017500000000404212013574114016155 0ustar mikiomikio.TH "TCFMTTEST" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcfmttest \- test cases of the fixed-length database API .SH DESCRIPTION .PP The command `\fBtcfmttest\fR' is a utility for facility test under multi\-thread situation. This command is used in the following format. `\fIpath\fR' specifies the path of a database file. `\fItnum\fR' specifies the number of running threads. `\fIrnum\fR' specifies the number of iterations. `\fIwidth\fR' specifies the width of the value of each record. `\fIlimsiz\fR' specifies the limit size of the database file. .PP .RS .br \fBtcfmttest write \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fItnum\fB \fIrnum\fB \fR[\fB\fIwidth\fB \fR[\fB\fIlimsiz\fB\fR]\fB\fR]\fB\fR .RS Store records with keys of 8 bytes. They change as `00000001', `00000002'... .RE .br \fBtcfmttest read \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-wb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fItnum\fB\fR .RS Retrieve all records of the database above. .RE .br \fBtcfmttest remove \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fItnum\fB\fR .RS Remove all records of the database above. .RE .br \fBtcfmttest wicked \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-nc\fR]\fB \fIpath\fB \fItnum\fB \fIrnum\fB\fR .RS Perform updating operations selected at random. .RE .br \fBtcfmttest typical \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-nc\fR]\fB \fR[\fB\-rr \fInum\fB\fR]\fB \fIpath\fB \fItnum\fB \fIrnum\fB \fR[\fB\fIwidth\fB \fR[\fB\fIlimsiz\fB\fR]\fB\fR]\fB\fR .RS Perform typical operations selected at random. .RE .RE .PP Options feature the following. .PP .RS \fB\-nl\fR : enable the option `FDBNOLCK'. .br \fB\-nb\fR : enable the option `FDBLCKNB'. .br \fB\-rnd\fR : select keys at random. .br \fB\-wb\fR : use the function `tcfdbget4' instead of `tcfdbget2'. .br \fB\-nc\fR : omit the comparison test. .br \fB\-rr\fR \fInum\fR : specifiy the ratio of reading operation by percentage. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tcftest (1), .BR tcfmgr (1), .BR tcfdb (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tcamttest.10000644000175000017500000000164212013574114016153 0ustar mikiomikio.TH "TCAMTTEST" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcamttest \- test cases of the abstract database API .SH DESCRIPTION .PP The command `\fBtcamttest\fR' is a utility for facility test under multi\-thread situation. This command is used in the following format. `\fIname\fR' specifies the database name. `\fItnum\fR' specifies the number of running threads. `\fIrnum\fR' specifies the number of iterations. .PP .RS .br \fBtcamttest write \fIname\fB \fItnum\fB \fIrnum\fB\fR .RS Store records with keys of 8 bytes. They change as `00000001', `00000002'... .RE .br \fBtcamttest read \fIname\fB \fItnum\fB\fR .RS Retrieve all records of the database above. .RE .br \fBtcamttest remove \fIname\fB \fItnum\fB\fR .RS Remove all records of the database above. .RE .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tcatest (1), .BR tcamgr (1), .BR tcadb (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tcmpool.30000644000175000017500000000002212013574114015611 0ustar mikiomikio.so man3/tcutil.3 tokyocabinet-1.4.48/man/tchmgr.10000644000175000017500000000667712013574114015444 0ustar mikiomikio.TH "TCHMGR" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tchmgr \- the command line utility of the hash database API .SH DESCRIPTION .PP The command `\fBtchmgr\fR' is a utility for test and debugging of the hash database API and its applications. `\fIpath\fR' specifies the path of a database file. `\fIbnum\fR' specifies the number of buckets. `\fIapow\fR' specifies the power of the alignment. `\fIfpow\fR' specifies the power of the free block pool. `\fIkey\fR' specifies the key of a record. `\fIvalue\fR' specifies the value of a record. `\fIfile\fR' specifies the input file. .PP .RS .br \fBtchmgr create \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fIpath\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Create a database file. .RE .br \fBtchmgr inform \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB\fR .RS Print miscellaneous information to the standard output. .RE .br \fBtchmgr put \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sx\fR]\fB \fR[\fB\-dk\fR|\fB\-dc\fR|\fB\-dai\fR|\fB\-dad\fR]\fB \fIpath\fB \fIkey\fB \fIvalue\fB\fR .RS Store a record. .RE .br \fBtchmgr out \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sx\fR]\fB \fIpath\fB \fIkey\fB\fR .RS Remove a record. .RE .br \fBtchmgr get \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sx\fR]\fB \fR[\fB\-px\fR]\fB \fR[\fB\-pz\fR]\fB \fIpath\fB \fIkey\fB\fR .RS Print the value of a record. .RE .br \fBtchmgr list \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-m \fInum\fB\fR]\fB \fR[\fB\-pv\fR]\fB \fR[\fB\-px\fR]\fB \fR[\fB\-fm \fIstr\fB\fR]\fB \fIpath\fB\fR .RS Print keys of all records, separated by line feeds. .RE .br \fBtchmgr optimize \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-tz\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-df\fR]\fB \fIpath\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Optimize a database file. .RE .br \fBtchmgr importtsv \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sc\fR]\fB \fIpath\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Store records of TSV in each line of a file. .RE .br \fBtchmgr version\fR .RS Print the version information of Tokyo Cabinet. .RE .RE .PP Options feature the following. .PP .RS \fB\-tl\fR : enable the option `HDBTLARGE'. .br \fB\-td\fR : enable the option `HDBTDEFLATE'. .br \fB\-tb\fR : enable the option `HDBTBZIP'. .br \fB\-tt\fR : enable the option `HDBTTCBS'. .br \fB\-tx\fR : enable the option `HDBTEXCODEC'. .br \fB\-nl\fR : enable the option `HDBNOLCK'. .br \fB\-nb\fR : enable the option `HDBLCKNB'. .br \fB\-sx\fR : the input data is evaluated as a hexadecimal data string. .br \fB\-dk\fR : use the function `tchdbputkeep' instead of `tchdbput'. .br \fB\-dc\fR : use the function `tchdbputcat' instead of `tchdbput'. .br \fB\-dai\fR : use the function `tchdbaddint' instead of `tchdbput'. .br \fB\-dad\fR : use the function `tchdbadddouble' instead of `tchdbput'. .br \fB\-px\fR : the output data is converted into a hexadecimal data string. .br \fB\-pz\fR : do not append line feed at the end of the output. .br \fB\-m \fInum\fR\fR : specify the maximum number of the output. .br \fB\-pv\fR : print values of records also. .br \fB\-fm \fIstr\fR\fR : specify the prefix of keys. .br \fB\-tz\fR : enable the option `UINT8_MAX'. .br \fB\-df\fR : perform defragmentation only. .br \fB\-sc\fR : normalize keys as lower cases. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tchtest (1), .BR tchmttest (1), .BR tchdb (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tcbmgr.10000644000175000017500000001101712013574114015416 0ustar mikiomikio.TH "TCBMGR" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcbmgr \- the command line utility of the B+ tree database API .SH DESCRIPTION The command `\fBtcbmgr\fR' is a utility for test and debugging of the B+ tree database API and its applications. `\fIpath\fR' specifies the path of a database file. `\fIlmemb\fR' specifies the number of members in each leaf page. `\fInmemb\fR' specifies the number of members in each non\-leaf page. `\fIbnum\fR' specifies the number of buckets. `\fIapow\fR' specifies the power of the alignment. `\fIfpow\fR' specifies the power of the free block pool. `\fIkey\fR' specifies the key of a record. `\fIvalue\fR' specifies the value of a record. `\fIfile\fR' specifies the input file. .PP .RS .br \fBtcbmgr create \fR[\fB\-cd\fR|\fB\-ci\fR|\fB\-cj\fR]\fB \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fIpath\fB \fR[\fB\fIlmemb\fB \fR[\fB\fInmemb\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Create a database file. .RE .br \fBtcbmgr inform \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB\fR .RS Print miscellaneous information to the standard output. .RE .br \fBtcbmgr put \fR[\fB\-cd\fR|\fB\-ci\fR|\fB\-cj\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sx\fR]\fB \fR[\fB\-dk\fR|\fB\-dc\fR|\fB\-dd\fR|\fB\-db\fR|\fB\-dai\fR|\fB\-dad\fR]\fB \fIpath\fB \fIkey\fB \fIvalue\fB\fR .RS Store a record. .RE .br \fBtcbmgr out \fR[\fB\-cd\fR|\fB\-ci\fR|\fB\-cj\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sx\fR]\fB \fIpath\fB \fIkey\fB\fR .RS Remove a record. .RE .br \fBtcbmgr get \fR[\fB\-cd\fR|\fB\-ci\fR|\fB\-cj\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sx\fR]\fB \fR[\fB\-px\fR]\fB \fR[\fB\-pz\fR]\fB \fIpath\fB \fIkey\fB\fR .RS Print the value of a record. .RE .br \fBtcbmgr list \fR[\fB\-cd\fR|\fB\-ci\fR|\fB\-cj\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-m \fInum\fB\fR]\fB \fR[\fB\-bk\fR]\fB \fR[\fB\-pv\fR]\fB \fR[\fB\-px\fR]\fB \fR[\fB\-j \fIstr\fB\fR]\fB \fR[\fB\-rb \fIbkey\fB \fIekey\fB\fR]\fB \fR[\fB\-fm \fIstr\fB\fR]\fB \fIpath\fB\fR .RS Print keys of all records, separated by line feeds. .RE .br \fBtcbmgr optimize \fR[\fB\-cd\fR|\fB\-ci\fR|\fB\-cj\fR]\fB \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-tz\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-df\fR]\fB \fIpath\fB \fR[\fB\fIlmemb\fB \fR[\fB\fInmemb\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Optimize a database file. .RE .br \fBtcbmgr importtsv \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sc\fR]\fB \fIpath\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Store records of TSV in each line of a file. .RE .br \fBtcbmgr version\fR .RS Print the version information of Tokyo Cabinet. .RE .RE .PP Options feature the following. .PP .RS \fB\-cd\fR : use the comparison function `tccmpdecimal'. .br \fB\-ci\fR : use the comparison function `tccmpint32'. .br \fB\-cj\fR : use the comparison function `tccmpint64'. .br \fB\-tl\fR : enable the option `BDBTLARGE'. .br \fB\-td\fR : enable the option `BDBTDEFLATE'. .br \fB\-tb\fR : enable the option `BDBTBZIP'. .br \fB\-tt\fR : enable the option `BDBTTCBS'. .br \fB\-tx\fR : enable the option `BDBTEXCODEC'. .br \fB\-nl\fR : enable the option `BDBNOLCK'. .br \fB\-nb\fR : enable the option `BDBLCKNB'. .br \fB\-sx\fR : the input data is evaluated as a hexadecimal data string. .br \fB\-dk\fR : use the function `tcbdbputkeep' instead of `tcbdbput'. .br \fB\-dc\fR : use the function `tcbdbputcat' instead of `tcbdbput'. .br \fB\-dd\fR : use the function `tcbdbputdup' instead of `tcbdbput'. .br \fB\-db\fR : use the function `tcbdbputdupback' instead of `tcbdbput'. .br \fB\-dai\fR : use the function `tcbdbaddint' instead of `tcbdbput'. .br \fB\-dad\fR : use the function `tcbdbadddouble' instead of `tcbdbput'. .br \fB\-px\fR : the output data is converted into a hexadecimal data string. .br \fB\-pz\fR : do not append line feed at the end of the output. .br \fB\-m \fInum\fR\fR : specify the maximum number of the output. .br \fB\-bk\fR : perform backword scanning. .br \fB\-pv\fR : print values of records also. .br \fB\-j \fIstr\fR\fR : specify the key where the cursor jump to. .br \fB\-rb \fIbkey\fR \fIekey\fR\fR : specify the range of keys. .br \fB\-fm \fIstr\fR\fR : specify the prefix of keys. .br \fB\-tz\fR : enable the option `UINT8_MAX'. .br \fB\-df\fR : perform defragmentation only. .br \fB\-sc\fR : normalize keys as lower cases. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tcbtest (1), .BR tcbmttest (1), .BR tcbdb (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tcadb.30000644000175000017500000005620712013574114015231 0ustar mikiomikio.TH "TCADB" 3 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcadb \- the abstract database API .SH DESCRIPTION .PP 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. .PP To use the abstract database API, include `\fBtcutil.h\fR', `\fBtcadb.h\fR', and related standard header files. Usually, write the following description near the front of a source file. .PP .RS .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .br \fB#include \fR .RE .PP Objects whose type is pointer to `\fBTCADB\fR' are used to handle abstract databases. An abstract database object is created with the function `\fBtcadbnew\fR' and is deleted with the function `\fBtcadbdel\fR'. To avoid memory leak, it is important to delete every object when it is no longer in use. .PP Before operations to store or retrieve records, it is necessary to connect the abstract database object to the concrete one. The function `\fBtcadbopen\fR' is used to open a concrete database and the function `\fBtcadbclose\fR' 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. .SH API .PP The function `tcadbnew' is used in order to create an abstract database object. .PP .RS .br \fBTCADB *tcadbnew(void);\fR .RS The return value is the new abstract database object. .RE .RE .PP The function `tcadbdel' is used in order to delete an abstract database object. .PP .RS .br \fBvoid tcadbdel(TCADB *\fIadb\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RE .PP The function `tcadbopen' is used in order to open an abstract database. .PP .RS .br \fBbool tcadbopen(TCADB *\fIadb\fB, const char *\fIname\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIname\fR' 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". .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tcadbclose' is used in order to close an abstract database object. .PP .RS .br \fBbool tcadbclose(TCADB *\fIadb\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tcadbput' is used in order to store a record into an abstract database object. .PP .RS .br \fBbool tcadbput(TCADB *\fIadb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcadbput2' is used in order to store a string record into an abstract object. .PP .RS .br \fBbool tcadbput2(TCADB *\fIadb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, it is overwritten. .RE .RE .PP The function `tcadbputkeep' is used in order to store a new record into an abstract database object. .PP .RS .br \fBbool tcadbputkeep(TCADB *\fIadb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tcadbputkeep2' is used in order to store a new string record into an abstract database object. .PP .RS .br \fBbool tcadbputkeep2(TCADB *\fIadb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If a record with the same key exists in the database, this function has no effect. .RE .RE .PP The function `tcadbputcat' is used in order to concatenate a value at the end of the existing record in an abstract database object. .PP .RS .br \fBbool tcadbputcat(TCADB *\fIadb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, const void *\fIvbuf\fB, int \fIvsiz\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIvbuf\fR' specifies the pointer to the region of the value. .RE .RS `\fIvsiz\fR' specifies the size of the region of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcadbputcat2' is used in order to concatenate a string value at the end of the existing record in an abstract database object. .PP .RS .br \fBbool tcadbputcat2(TCADB *\fIadb\fB, const char *\fIkstr\fB, const char *\fIvstr\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS `\fIvstr\fR' specifies the string of the value. .RE .RS If successful, the return value is true, else, it is false. .RE .RS If there is no corresponding record, a new record is created. .RE .RE .PP The function `tcadbout' is used in order to remove a record of an abstract database object. .PP .RS .br \fBbool tcadbout(TCADB *\fIadb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcadbout2' is used in order to remove a string record of an abstract database object. .PP .RS .br \fBbool tcadbout2(TCADB *\fIadb\fB, const char *\fIkstr\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcadbget' is used in order to retrieve a record in an abstract database object. .PP .RS .br \fBvoid *tcadbget(TCADB *\fIadb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int *\fIsp\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcadbget2' is used in order to retrieve a string record in an abstract database object. .PP .RS .br \fBchar *tcadbget2(TCADB *\fIadb\fB, const char *\fIkstr\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is the string of the value of the corresponding record. `NULL' is returned if no record corresponds. .RE .RS 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. .RE .RE .PP The function `tcadbvsiz' is used in order to get the size of the value of a record in an abstract database object. .PP .RS .br \fBint tcadbvsiz(TCADB *\fIadb\fB, const void *\fIkbuf\fB, int \fIksiz\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RE .PP The function `tcadbvsiz2' is used in order to get the size of the value of a string record in an abstract database object. .PP .RS .br \fBint tcadbvsiz2(TCADB *\fIadb\fB, const char *\fIkstr\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIkstr\fR' specifies the string of the key. .RE .RS If successful, the return value is the size of the value of the corresponding record, else, it is \-1. .RE .RE .PP The function `tcadbiterinit' is used in order to initialize the iterator of an abstract database object. .PP .RS .br \fBbool tcadbiterinit(TCADB *\fIadb\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RS The iterator is used in order to access the key of every record stored in a database. .RE .RE .PP The function `tcadbiternext' is used in order to get the next key of the iterator of an abstract database object. .PP .RS .br \fBvoid *tcadbiternext(TCADB *\fIadb\fB, int *\fIsp\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIsp\fR' specifies the pointer to the variable into which the size of the region of the return value is assigned. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcadbiternext2' is used in order to get the next key string of the iterator of an abstract database object. .PP .RS .br \fBchar *tcadbiternext2(TCADB *\fIadb\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcadbfwmkeys' is used in order to get forward matching keys in an abstract database object. .PP .RS .br \fBTCLIST *tcadbfwmkeys(TCADB *\fIadb\fB, const void *\fIpbuf\fB, int \fIpsiz\fB, int \fImax\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIpbuf\fR' specifies the pointer to the region of the prefix. .RE .RS `\fIpsiz\fR' specifies the size of the region of the prefix. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcadbfwmkeys2' is used in order to get forward matching string keys in an abstract database object. .PP .RS .br \fBTCLIST *tcadbfwmkeys2(TCADB *\fIadb\fB, const char *\fIpstr\fB, int \fImax\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIpstr\fR' specifies the string of the prefix. .RE .RS `\fImax\fR' specifies the maximum number of keys to be fetched. If it is negative, no limit is specified. .RE .RS 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. .RE .RS 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. .RE .RE .PP The function `tcadbaddint' is used in order to add an integer to a record in an abstract database object. .PP .RS .br \fBint tcadbaddint(TCADB *\fIadb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, int \fInum\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS If successful, the return value is the summation value, else, it is `INT_MIN'. .RE .RS 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. .RE .RE .PP The function `tcadbadddouble' is used in order to add a real number to a record in an abstract database object. .PP .RS .br \fBdouble tcadbadddouble(TCADB *\fIadb\fB, const void *\fIkbuf\fB, int \fIksiz\fB, double \fInum\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIkbuf\fR' specifies the pointer to the region of the key. .RE .RS `\fIksiz\fR' specifies the size of the region of the key. .RE .RS `\fInum\fR' specifies the additional value. .RE .RS If successful, the return value is the summation value, else, it is Not-a-Number. .RE .RS 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. .RE .RE .PP The function `tcadbsync' is used in order to synchronize updated contents of an abstract database object with the file and the device. .PP .RS .br \fBbool tcadbsync(TCADB *\fIadb\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcadboptimize' is used in order to optimize the storage of an abstract database object. .PP .RS .br \fBbool tcadboptimize(TCADB *\fIadb\fB, const char *\fIparams\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIparams\fR' 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. .RE .RS If successful, the return value is true, else, it is false. .RE .RS This function is useful to reduce the size of the database storage with data fragmentation by successive updating. .RE .RE .PP The function `tcadbvanish' is used in order to remove all records of an abstract database object. .PP .RS .br \fBbool tcadbvanish(TCADB *\fIadb\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RE .PP The function `tcadbcopy' is used in order to copy the database file of an abstract database object. .PP .RS .br \fBbool tcadbcopy(TCADB *\fIadb\fB, const char *\fIpath\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIpath\fR' specifies the path of the destination file. If it begins with `@', the trailing substring is executed as a command line. .RE .RS If successful, the return value is true, else, it is false. False is returned if the executed command returns non\-zero code. .RE .RS 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. .RE .RE .PP The function `tcadbtranbegin' is used in order to begin the transaction of an abstract database object. .PP .RS .br \fBbool tcadbtranbegin(TCADB *\fIadb\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RS 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. .RE .RE .PP The function `tcadbtrancommit' is used in order to commit the transaction of an abstract database object. .PP .RS .br \fBbool tcadbtrancommit(TCADB *\fIadb\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Update in the transaction is fixed when it is committed successfully. .RE .RE .PP The function `tcadbtranabort' is used in order to abort the transaction of an abstract database object. .PP .RS .br \fBbool tcadbtranabort(TCADB *\fIadb\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS If successful, the return value is true, else, it is false. .RE .RS Update in the transaction is discarded when it is aborted. The state of the database is rollbacked to before transaction. .RE .RE .PP The function `tcadbpath' is used in order to get the file path of an abstract database object. .PP .RS .br \fBconst char *tcadbpath(TCADB *\fIadb\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS 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. .RE .RE .PP The function `tcadbrnum' is used in order to get the number of records of an abstract database object. .PP .RS .br \fBuint64_t tcadbrnum(TCADB *\fIadb\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS The return value is the number of records or 0 if the object does not connect to any database instance. .RE .RE .PP The function `tcadbsize' is used in order to get the size of the database of an abstract database object. .PP .RS .br \fBuint64_t tcadbsize(TCADB *\fIadb\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS The return value is the size of the database or 0 if the object does not connect to any database instance. .RE .RE .PP The function `tcadbmisc' is used in order to call a versatile function for miscellaneous operations of an abstract database object. .PP .RS .br \fBTCLIST *tcadbmisc(TCADB *\fIadb\fB, const char *\fIname\fB, const TCLIST *\fIargs\fB);\fR .RS `\fIadb\fR' specifies the abstract database object. .RE .RS `\fIname\fR' 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. .RE .RS `\fIargs\fR' specifies a list object containing arguments. .RE .RS If successful, the return value is a list object of the result. `NULL' is returned on failure. .RE .RS 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. .RE .RE .SH SEE ALSO .PP .BR tcatest (1), .BR tcamttest (1), .BR tcamgr (1), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tclist.30000644000175000017500000000002212013574114015436 0ustar mikiomikio.so man3/tcutil.3 tokyocabinet-1.4.48/man/tcamgr.10000644000175000017500000000553512013574114015425 0ustar mikiomikio.TH "TCATEST" 3 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcamgr \- the command line utility of the abstract database API .SH DESCRIPTION .PP The command `\fBtcamgr\fR' is a utility for test and debugging of the abstract database API and its applications. `\fIname\fR' specifies the name of a database. `\fIkey\fR' specifies the key of a record. `\fIvalue\fR' specifies the value of a record. `\fIparams\fR' specifies the tuning parameters. `\fIfunc\fR' specifies the name of a function. `\fIarg\fR' specifies the arguments of the function. `\fIdest\fR' specifies the path of the destination file. .PP .RS .br \fBtcamgr create \fIname\fB\fR .RS Create a database file. .RE .br \fBtcamgr inform \fIname\fB\fR .RS Print miscellaneous information to the standard output. .RE .br \fBtcamgr put \fR[\fB\-sx\fR]\fB \fR[\fB\-sep \fIchr\fB\fR]\fB \fR[\fB\-dk\fR|\fB\-dc\fR|\fB\-dai\fR|\fB\-dad\fR]\fB \fIname\fB \fIkey\fB \fIvalue\fB\fR .RS Store a record. .RE .br \fBtcamgr out \fR[\fB\-sx\fR]\fB \fR[\fB\-sep \fIchr\fB\fR]\fB \fIname\fB \fIkey\fB\fR .RS Remove a record. .RE .br \fBtcamgr get \fR[\fB\-sx\fR]\fB \fR[\fB\-sep \fIchr\fB\fR]\fB \fR[\fB\-px\fR]\fB \fR[\fB\-pz\fR]\fB \fIname\fB \fIkey\fB\fR .RS Print the value of a record. .RE .br \fBtcamgr list \fR[\fB\-sep \fIchr\fB\fR]\fB \fR[\fB\-m \fInum\fB\fR]\fB \fR[\fB\-pv\fR]\fB \fR[\fB\-px\fR]\fB \fR[\fB\-fm \fIstr\fB\fR]\fB \fIname\fB\fR .RS Print keys of all records, separated by line feeds. .RE .br \fBtcamgr optimize \fIname\fB \fIparams\fB\fR .RS Optimize a database file. .RE .br \fBtcamgr misc \fR[\fB\-sx\fR]\fB \fR[\fB\-sep \fIchr\fB\fR]\fB \fR[\fB\-px\fR]\fB \fIname\fB \fIfunc\fB \fR[\fB\fIarg\fB...\fR]\fB\fR .RS Call a versatile function for miscellaneous operations. .RE .br \fBtcamgr map \fR[\fB\-fm \fIstr\fB\fR]\fB \fIname\fB \fIdest\fB\fR .RS Map records into another B+ tree database. .RE .br \fBtcamgr version\fR .RS Print the version information of Tokyo Cabinet. .RE .RE .PP Options feature the following. .PP .RS \fB\-sx\fR : the input data is evaluated as a hexadecimal data string. .br \fB\-sep \fIchr\fR\fR : specify the separator of the input data. .br \fB\-dk\fR : use the function `tcadbputkeep' instead of `tcadbput'. .br \fB\-dc\fR : use the function `tcadbputcat' instead of `tcadbput'. .br \fB\-dai\fR : use the function `tcadbaddint' instead of `tcadbput'. .br \fB\-dad\fR : use the function `tcadbadddouble' instead of `tcadbput'. .br \fB\-px\fR : the output data is converted into a hexadecimal data string. .br \fB\-pz\fR : do not append line feed at the end of the output. .br \fB\-m \fInum\fR\fR : specify the maximum number of the output. .br \fB\-pv\fR : print values of records also. .br \fB\-fm \fIstr\fR\fR : specify the prefix of keys. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tcatest (1), .BR tcamttest (1), .BR tcadb (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tcbmttest.10000644000175000017500000000702212013574114016152 0ustar mikiomikio.TH "TCBMTTEST" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcbmttest \- test cases of the B+ tree database API .SH DESCRIPTION .PP The command `\fBtcbmttest\fR' is a utility for facility test and performance test. This command is used in the following format. `\fIpath\fR' specifies the path of a database file. `\fItnum\fR' specifies the number of running threads. `\fIrnum\fR' specifies the number of iterations. `\fIlmemb\fR' specifies the number of members in each leaf page. `\fInmemb\fR' specifies the number of members in each non\-leaf page. `\fIbnum\fR' specifies the number of buckets. `\fIapow\fR' specifies the power of the alignment. `\fIfpow\fR' specifies the power of the free block pool. .PP .RS .br \fBtcbmttest write \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fItnum\fB \fIrnum\fB \fR[\fB\fIlmemb\fB \fR[\fB\fInmemb\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Store records with keys of 8 bytes. They change as `00000001', `00000002'... .RE .br \fBtcbmttest read \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-wb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fItnum\fB\fR .RS Retrieve all records of the database above. .RE .br \fBtcbmttest remove \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fItnum\fB\fR .RS Remove all records of the database above. .RE .br \fBtcbmttest wicked \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-nc\fR]\fB \fIpath\fB \fItnum\fB \fIrnum\fB\fR .RS Perform updating operations selected at random. .RE .br \fBtcbmttest typical \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-nc\fR]\fB \fR[\fB\-rr \fInum\fB\fR]\fB \fIpath\fB \fItnum\fB \fIrnum\fB \fR[\fB\fIlmemb\fB \fR[\fB\fInmemb\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Perform typical operations selected at random. .RE .br \fBtcbmttest race \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB \fItnum\fB \fIrnum\fB \fR[\fB\fIlmemb\fB \fR[\fB\fInmemb\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Perform race condition test. .RE .RE .PP Options feature the following. .PP .RS \fB\-tl\fR : enable the option `BDBTLARGE'. .br \fB\-td\fR : enable the option `BDBTDEFLATE'. .br \fB\-tb\fR : enable the option `BDBTBZIP'. .br \fB\-tt\fR : enable the option `BDBTTCBS'. .br \fB\-tx\fR : enable the option `BDBTEXCODEC'. .br \fB\-xm \fInum\fR\fR : specify the size of the extra mapped memory. .br \fB\-df \fInum\fR\fR : specify the unit step number of auto defragmentation. .br \fB\-nl\fR : enable the option `BDBNOLCK'. .br \fB\-nb\fR : enable the option `BDBLCKNB'. .br \fB\-rnd\fR : select keys at random. .br \fB\-wb\fR : use the function `tchdbget3' instead of `tchdbget'. .br \fB\-nc\fR : omit the comparison test. .br \fB\-rr \fInum\fR\fR : specify the ratio of reading operation by percentage. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tcbtest (1), .BR tcbmgr (1), .BR tcbdb (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tctmgr.10000644000175000017500000001250312013574114015441 0ustar mikiomikio.TH "TCTMGR" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tctmgr \- the command line utility of the table database API .SH DESCRIPTION .PP The command `\fBtctmgr\fR' is a utility for test and debugging of the table database API and its applications. `\fIpath\fR' specifies the path of a database file. `\fIbnum\fR' specifies the number of buckets. `\fIapow\fR' specifies the power of the alignment. `\fIfpow\fR' specifies the power of the free block pool. `\fIpkey\fR' specifies the primary key of a record. `\fIcols\fR' specifies the names and the values of a record alternately. `\fIname\fR' specifies the name of a column. `\fIop\fR' specifies an operator. `\fIexpr\fR' specifies the condition expression. `\fIfile\fR' specifies the input file. .PP .RS .br \fBtctmgr create \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fIpath\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Create a database file. .RE .br \fBtctmgr inform \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB\fR .RS Print miscellaneous information to the standard output. .RE .br \fBtctmgr put \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sx\fR]\fB \fR[\fB\-dk\fR|\fB\-dc\fR|\fB\-dai\fR|\fB\-dad\fR]\fB \fIpath\fB \fIpkey\fB \fR[\fB\fIcols\fB ...\fR]\fB\fR .RS Store a record. .RE .br \fBtctmgr out \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sx\fR]\fB \fIpath\fB \fIpkey\fB\fR .RS Remove a record. .RE .br \fBtctmgr get \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sx\fR]\fB \fR[\fB\-px\fR]\fB \fR[\fB\-pz\fR]\fB \fIpath\fB \fIpkey\fB\fR .RS Print the value of a record. .RE .br \fBtctmgr list \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-m \fInum\fB\fR]\fB \fR[\fB\-pv\fR]\fB \fR[\fB\-px\fR]\fB \fR[\fB\-fm \fIstr\fB\fR]\fB \fIpath\fB\fR .RS Print the primary keys of all records, separated by line feeds. .RE .br \fBtctmgr search \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-ord \fIname\fB \fItype\fB\fR]\fB \fR[\fB\-m \fInum\fB\fR]\fB \fR[\fB\-sk \fInum\fB\fR]\fB \fR[\fB\-kw\fR]\fB \fR[\fB\-pv\fR]\fB \fR[\fB\-px\fR]\fB \fR[\fB\-ph\fR]\fB \fR[\fB\-bt \fInum\fB\fR]\fB \fR[\fB\-rm\fR]\fB \fR[\fB\-ms \fItype\fB\fR]\fB \fIpath\fB \fR[\fB\fIname\fB \fIop\fB \fIexpr\fB ...\fR]\fB\fR .RS Print records matching conditions, separated by line feeds. .RE .br \fBtctmgr optimize \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-tz\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-df\fR]\fB \fIpath\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Optimize a database file. .RE .br \fBtctmgr setindex \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-it \fItype\fB\fR]\fB \fIpath\fB \fIname\fB\fR .RS Set the index of a column. .RE .br \fBtctmgr importtsv \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-sc\fR]\fB \fIpath\fB \fR[\fB\fIfile\fB\fR]\fB\fR .RS Store records of TSV in each line of a file. .RE .br \fBtctmgr version\fR .RS Print the version information of Tokyo Cabinet. .RE .RE .PP Options feature the following. .PP .RS \fB\-tl\fR : enable the option `TDBTLARGE'. .br \fB\-td\fR : enable the option `TDBTDEFLATE'. .br \fB\-tb\fR : enable the option `TDBTBZIP'. .br \fB\-tt\fR : enable the option `TDBTTCBS'. .br \fB\-tx\fR : enable the option `TDBTEXCODEC'. .br \fB\-nl\fR : enable the option `TDBNOLCK'. .br \fB\-nb\fR : enable the option `TDBLCKNB'. .br \fB\-sx\fR : the input data is evaluated as a hexadecimal data string. .br \fB\-dk\fR : use the function `tctdbputkeep' instead of `tctdbput'. .br \fB\-dc\fR : use the function `tctdbputcat' instead of `tctdbput'. .br \fB\-dai\fR : use the function `tctdbaddint' instead of `tctdbput'. .br \fB\-dad\fR : use the function `tctdbadddouble' instead of `tctdbput'. .br \fB\-px\fR : the output data is converted into a hexadecimal data string. .br \fB\-pz\fR : do not append line feed at the end of the output. .br \fB\-m \fInum\fR\fR : specify the maximum number of the output. .br \fB\-pv\fR : print values of records also. .br \fB\-fm \fIstr\fR\fR : specify the prefix of keys. .br \fB\-ord \fIname\fR \fItype\fR\fR : specify the order of the result. .br \fB\-sk \fInum\fR\fR : specify the number of skipped records. .br \fB\-kw\fR : print KWIC string. .br \fB\-ph\fR : print hint information also. .br \fB\-bt\fR : specify the number of benchmark tests. .br \fB\-rm\fR : remove every record in the result. .br \fB\-ms \fItype\fR\fR : specify the set operation of meta search. .br \fB\-tz\fR : enable the option `UINT8_MAX'. .br \fB\-df\fR : perform defragmentation only. .br \fB\-it \fItype\fR\fR : specify the index type among "lexical", "decimal", "token", "qgram", and "void". .br \fB\-cd\fR : create the number index instead of the string index. .br \fB\-cv\fR : remove the existing index. .br \fB\-sc\fR : normalize keys as lower cases. .br .RE .PP 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. .SH SEE ALSO .PP .BR tcttest (1), .BR tctmttest (1), .BR tctdb (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tcxstr.30000644000175000017500000000002212013574114015463 0ustar mikiomikio.so man3/tcutil.3 tokyocabinet-1.4.48/man/tchtest.10000644000175000017500000000740612013574114015625 0ustar mikiomikio.TH "TCHTEST" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tchtest \- test cases of the hash database API .SH DESCRIPTION .PP To use the hash database API easily, the commands `\fBtchtest\fR', `\fBtchmttest\fR', and `\fBtchmgr\fR' are provided. .PP The command `\fBtchtest\fR' is a utility for facility test and performance test. This command is used in the following format. `\fIpath\fR' specifies the path of a database file. `\fIrnum\fR' specifies the number of iterations. `\fIbnum\fR' specifies the number of buckets. `\fIapow\fR' specifies the power of the alignment. `\fIfpow\fR' specifies the power of the free block pool. .PP .RS .br \fBtchtest write \fR[\fB\-mt\fR]\fB \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-as\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fIrnum\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Store records with keys of 8 bytes. They change as `00000001', `00000002'... .RE .br \fBtchtest read \fR[\fB\-mt\fR]\fB \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-wb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB\fR .RS Retrieve all records of the database above. .RE .br \fBtchtest remove \fR[\fB\-mt\fR]\fB \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB\fR .RS Remove all records of the database above. .RE .br \fBtchtest rcat \fR[\fB\-mt\fR]\fB \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-rc \fInum\fB\fR]\fB \fR[\fB\-xm \fInum\fB\fR]\fB \fR[\fB\-df \fInum\fB\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-pn \fInum\fB\fR]\fB \fR[\fB\-dai\fR|\fB\-dad\fR|\fB\-rl\fR|\fB\-ru\fR]\fB \fIpath\fB \fIrnum\fB \fR[\fB\fIbnum\fB \fR[\fB\fIapow\fB \fR[\fB\fIfpow\fB\fR]\fB\fR]\fB\fR]\fB\fR .RS Store records with partway duplicated keys using concatenate mode. .RE .br \fBtchtest misc \fR[\fB\-mt\fR]\fB \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB \fIrnum\fB\fR .RS Perform miscellaneous test of various operations. .RE .br \fBtchtest wicked \fR[\fB\-mt\fR]\fB \fR[\fB\-tl\fR]\fB \fR[\fB\-td\fR|\fB\-tb\fR|\fB\-tt\fR|\fB\-tx\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB \fIrnum\fB\fR .RS Perform updating operations selected at random. .RE .RE .PP Options feature the following. .PP .RS \fB\-mt\fR : call the function `tchdbsetmutex'. .br \fB\-tl\fR : enable the option `HDBTLARGE'. .br \fB\-td\fR : enable the option `HDBTDEFLATE'. .br \fB\-tb\fR : enable the option `HDBTBZIP'. .br \fB\-tt\fR : enable the option `HDBTTCBS'. .br \fB\-tx\fR : enable the option `HDBTEXCODEC'. .br \fB\-rc \fInum\fR\fR : specify the number of cached records. .br \fB\-xm \fInum\fR\fR : specify the size of the extra mapped memory. .br \fB\-df \fInum\fR\fR : specify the unit step number of auto defragmentation. .br \fB\-nl\fR : enable the option `HDBNOLCK'. .br \fB\-nb\fR : enable the option `HDBLCKNB'. .br \fB\-as\fR : use the function `tchdbputasync' instead of `tchdbput'. .br \fB\-rnd\fR : select keys at random. .br \fB\-wb\fR : use the function `tchdbget3' instead of `tchdbget'. .br \fB\-pn \fInum\fR\fR : specify the number of patterns. .br \fB\-dai\fR : use the function `tchdbaddint' instead of `tchdbputcat'. .br \fB\-dad\fR : use the function `tchdbadddouble' instead of `tchdbputcat'. .br \fB\-rl\fR : set the length of values at random. .br \fB\-ru\fR : select update operations at random. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tchmttest (1), .BR tchmgr (1), .BR tchdb (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tcftest.10000644000175000017500000000452712013574114015624 0ustar mikiomikio.TH "TCFTEST" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcftest \- test cases of the fixed-length database API .SH DESCRIPTION .PP The command `\fBtcftest\fR' is a utility for facility test and performance test. This command is used in the following format. `\fIpath\fR' specifies the path of a database file. `\fIrnum\fR' specifies the number of iterations. `\fIwidth\fR' specifies the width of the value of each record. `\fIlimsiz\fR' specifies the limit size of the database file. .PP .RS .br \fBtcftest write \fR[\fB\-mt\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB \fIrnum\fB \fR[\fB\fIwidth\fB \fR[\fB\fIlimsiz\fB\fR]\fB\fR]\fB\fR .RS Store records with keys of 8 bytes. They change as `00000001', `00000002'... .RE .br \fBtcftest read \fR[\fB\-mt\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-wb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB\fR .RS Retrieve all records of the database above. .RE .br \fBtcftest remove \fR[\fB\-mt\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-rnd\fR]\fB \fIpath\fB\fR .RS Remove all records of the database above. .RE .br \fBtcftest rcat \fR[\fB\-mt\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fR[\fB\-pn \fInum\fB\fR]\fB \fR[\fB\-dai\fR|\fB\-dad\fR|\fB\-rl\fR]\fB \fIpath\fB \fIrnum\fB \fR[\fB\fIlimsiz\fB\fR]\fB\fR]\fB\fR .RS Store records with partway duplicated keys using concatenate mode. .RE .br \fBtcftest misc \fR[\fB\-mt\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB \fIrnum\fB\fR .RS Perform miscellaneous test of various operations. .RE .br \fBtcftest wicked \fR[\fB\-mt\fR]\fB \fR[\fB\-nl\fR|\fB\-nb\fR]\fB \fIpath\fB \fIrnum\fB\fR .RS Perform updating operations selected at random. .RE .RE .PP Options feature the following. .PP .RS \fB\-mt\fR : call the function `tcfdbsetmutex'. .br \fB\-nl\fR : enable the option `FDBNOLCK'. .br \fB\-nb\fR : enable the option `FDBLCKNB'. .br \fB\-rnd\fR : select keys at random. .br \fB\-wb\fR : use the function `tcfdbget4' instead of `tcfdbget2'. .br \fB\-pn \fInum\fR\fR : specify the number of patterns. .br \fB\-dai\fR : use the function `tcfdbaddint' instead of `tcfdbputcat'. .br \fB\-dad\fR : use the function `tcfdbadddouble' instead of `tcfdbputcat'. .br \fB\-rl\fR : set the length of values at random. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tcfmttest (1), .BR tcfmgr (1), .BR tcfdb (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/man/tokyocabinet.30000644000175000017500000004262212013574114016643 0ustar mikiomikio.TH "TOKYOCABINET" 3 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tokyocabinet \- a modern implementation of DBM .SH INTRODUCTION .PP 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. .PP 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. .PP 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. .PP 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. .PP 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. .PP 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. .SH THE DINOSAUR WING OF THE DBM FORKS .PP 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. .PP .RS improves space efficiency : smaller size of database file. .br improves time efficiency : faster processing speed. .br improves parallelism : higher performance in multi\-thread environment. .br improves usability : simplified API. .br improves robustness : database file is not corrupted even under catastrophic situation. .br supports 64\-bit architecture : enormous memory space and database file are available. .br .RE .PP 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. .PP 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). .SH EFFECTIVE IMPLEMENTATION OF HASH DATABASE .PP 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)". .PP 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. .PP 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. .PP 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. .PP 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. .PP 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. .SH USEFUL IMPLEMENTATION OF B+ TREE DATABASE .PP 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)". .PP B+ tree database is implemented, based on 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, QDBM 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. .PP 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. .SH NAIVE IMPLEMENTATION OF FIXED\-LENGTH DATABASE .PP 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. .PP 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. .PP 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. .SH FLEXIBLE IMPLEMENTATION OF TABLE DATABASE .PP 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. .PP 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. .PP 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. .SH PRACTICAL FUNCTIONALITY .PP 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. .PP 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. .PP 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. .SH SIMPLE BUT VARIOUS INTERFACES .PP 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. .PP 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. .PP 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. .PP 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. .SH HOW TO USE THE LIBRARY .PP 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 `\fBtcutil.h\fR', `\fBtchdb.h\fR', and `\fBtcbdb.h\fR', applications should include one or more of them accordingly to use the API. As the library is provided as `\fBlibtokyocabinet.a\fR' and `\fBlibtokyocabinet.so\fR' and they depends `\fBlibz.so\fR', `\fBlibrt.so\fR', `\fBlibpthread.so\fR', `\fBlibm.so\fR', and `\fBlibc.so\fR', linker options `\fB\-ltokyocabinet\fR', `\fB\-lz\fR', `\fB\-lbz2\fR', `\fB\-lrt\fR', `\fB\-lpthread\fR', `\fB\-lm\fR', and `\fB\-lc\fR' are required for build command. A typical build command is the following. .PP .RS gcc \-I/usr/local/include tc_example.c \-o tc_example \\ .br \-L/usr/local/lib \-ltokyocabinet \-lz \-lbz2 \-lrt \-lpthread \-lm \-lc .RE .PP You can also use Tokyo Cabinet in programs written in C++. Because each header is wrapped in C linkage (`\fBextern "C"\fR' block), you can simply include them into your C++ programs. .SH LICENSE .PP 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. .PP 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. .PP You should have received a copy of the GNU Lesser General Public License along with Tokyo Cabinet (See the file `\fBCOPYING\fR'); if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111\-1307 USA. .PP Tokyo Cabinet was written by FAL Labs. You can contact the author by e\-mail to `\fBinfo@fallabs.com\fR'. .SH SEE ALSO .PP .BR tcutil (3), .BR tchdb (3), .BR tcbdb (3), .BR tcfdb (3), .BR tctdb (3), .BR tcadb (3) .PP Please see .I http://1978th.net/tokyocabinet/ for detail. tokyocabinet-1.4.48/man/tcumttest.10000644000175000017500000000206212013574114016174 0ustar mikiomikio.TH "TCUMTTEST" 1 "2012-08-18" "Man Page" "Tokyo Cabinet" .SH NAME tcutest \- test cases of the on-memory database API .SH DESCRIPTION .PP The command `\fBtcumttest\fR' is a utility for facility test under multi\-thread situation. This command is used in the following format. `\fIrnum\fR' specifies the number of iterations. `\fIbnum\fR' specifies the number of buckets. .PP .RS .br \fBtcumttest combo \fR[\fB\-rnd\fR]\fB \fItnum\fB \fIrnum\fB \fR[\fB\fIbnum\fB\fR]\fB\fR .RS Peform storing, retrieving, and removing in turn. .RE .br \fBtcumttest typical \fR[\fB\-nc\fR]\fB \fR[\fB\-rr \fInum\fB\fR]\fB \fItnum\fB \fIrnum\fB \fR[\fB\fIbnum\fB\fR]\fB\fR .RS Perform typical operations selected at random. .RE .RE .PP Options feature the following. .PP .RS \fB\-rnd\fR : select keys at random. .br \fB\-nc\fR : omit the comparison test. .br \-rr \fInum\fR : specifiy the ratio of reading operation by percentage. .br .RE .PP This command returns 0 on success, another on failure. .SH SEE ALSO .PP .BR tcutest (1), .BR tcucodec (1), .BR tcutil (3), .BR tokyocabinet (3) tokyocabinet-1.4.48/tchdb.h0000644000175000017500000013127212013574446014556 0ustar mikiomikio/************************************************************************************************* * The hash database API of Tokyo Cabinet * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #ifndef _TCHDB_H /* duplication check */ #define _TCHDB_H #if defined(__cplusplus) #define __TCHDB_CLINKAGEBEGIN extern "C" { #define __TCHDB_CLINKAGEEND } #else #define __TCHDB_CLINKAGEBEGIN #define __TCHDB_CLINKAGEEND #endif __TCHDB_CLINKAGEBEGIN #include /************************************************************************************************* * API *************************************************************************************************/ typedef struct { /* type of structure for a hash database */ void *mmtx; /* mutex for method */ void *rmtxs; /* mutexes for records */ void *dmtx; /* mutex for the while database */ void *wmtx; /* mutex for write ahead logging */ void *eckey; /* key for thread specific error code */ char *rpath; /* real path for locking */ uint8_t type; /* database type */ uint8_t flags; /* additional flags */ uint64_t bnum; /* number of the bucket array */ uint8_t apow; /* power of record alignment */ uint8_t fpow; /* power of free block pool number */ uint8_t opts; /* options */ char *path; /* path of the database file */ int fd; /* file descriptor of the database file */ uint32_t omode; /* open mode */ uint64_t rnum; /* number of the records */ uint64_t fsiz; /* size of the database file */ uint64_t frec; /* offset of the first record */ uint64_t dfcur; /* offset of the cursor for defragmentation */ uint64_t iter; /* offset of the iterator */ char *map; /* pointer to the mapped memory */ uint64_t msiz; /* size of the mapped memory */ uint64_t xmsiz; /* size of the extra mapped memory */ uint64_t xfsiz; /* extra size of the file for mapped memory */ uint32_t *ba32; /* 32-bit bucket array */ uint64_t *ba64; /* 64-bit bucket array */ uint32_t align; /* record alignment */ uint32_t runit; /* record reading unit */ bool zmode; /* whether compression is used */ int32_t fbpmax; /* maximum number of the free block pool */ void *fbpool; /* free block pool */ int32_t fbpnum; /* number of the free block pool */ int32_t fbpmis; /* number of missing retrieval of the free block pool */ bool async; /* whether asynchronous storing is called */ TCXSTR *drpool; /* delayed record pool */ TCXSTR *drpdef; /* deferred records of the delayed record pool */ uint64_t drpoff; /* offset of the delayed record pool */ TCMDB *recc; /* cache for records */ uint32_t rcnum; /* maximum number of cached records */ TCCODEC enc; /* pointer to the encoding function */ void *encop; /* opaque object for the encoding functions */ TCCODEC dec; /* pointer to the decoding function */ void *decop; /* opaque object for the decoding functions */ int ecode; /* last happened error code */ bool fatal; /* whether a fatal error occured */ uint64_t inode; /* inode number */ time_t mtime; /* modification time */ uint32_t dfunit; /* unit step number of auto defragmentation */ uint32_t dfcnt; /* counter of auto defragmentation */ bool tran; /* whether in the transaction */ int walfd; /* file descriptor of write ahead logging */ uint64_t walend; /* end offset of write ahead logging */ int dbgfd; /* file descriptor for debugging */ volatile int64_t cnt_writerec; /* tesing counter for record write times */ volatile int64_t cnt_reuserec; /* tesing counter for record reuse times */ volatile int64_t cnt_moverec; /* tesing counter for record move times */ volatile int64_t cnt_readrec; /* tesing counter for record read times */ volatile int64_t cnt_searchfbp; /* tesing counter for FBP search times */ volatile int64_t cnt_insertfbp; /* tesing counter for FBP insert times */ volatile int64_t cnt_splicefbp; /* tesing counter for FBP splice times */ volatile int64_t cnt_dividefbp; /* tesing counter for FBP divide times */ volatile int64_t cnt_mergefbp; /* tesing counter for FBP merge times */ volatile int64_t cnt_reducefbp; /* tesing counter for FBP reduce times */ volatile int64_t cnt_appenddrp; /* tesing counter for DRP append times */ volatile int64_t cnt_deferdrp; /* tesing counter for DRP defer times */ volatile int64_t cnt_flushdrp; /* tesing counter for DRP flush times */ volatile int64_t cnt_adjrecc; /* tesing counter for record cache adjust times */ volatile int64_t cnt_defrag; /* tesing counter for defragmentation times */ volatile int64_t cnt_shiftrec; /* tesing counter for record shift times */ volatile int64_t cnt_trunc; /* tesing counter for truncation times */ } TCHDB; enum { /* enumeration for additional flags */ HDBFOPEN = 1 << 0, /* whether opened */ HDBFFATAL = 1 << 1 /* whether with fatal error */ }; enum { /* enumeration for tuning options */ HDBTLARGE = 1 << 0, /* use 64-bit bucket array */ HDBTDEFLATE = 1 << 1, /* compress each record with Deflate */ HDBTBZIP = 1 << 2, /* compress each record with BZIP2 */ HDBTTCBS = 1 << 3, /* compress each record with TCBS */ HDBTEXCODEC = 1 << 4 /* compress each record with custom functions */ }; enum { /* enumeration for open modes */ HDBOREADER = 1 << 0, /* open as a reader */ HDBOWRITER = 1 << 1, /* open as a writer */ HDBOCREAT = 1 << 2, /* writer creating */ HDBOTRUNC = 1 << 3, /* writer truncating */ HDBONOLCK = 1 << 4, /* open without locking */ HDBOLCKNB = 1 << 5, /* lock without blocking */ HDBOTSYNC = 1 << 6 /* synchronize every transaction */ }; /* Get the message string corresponding to an error code. `ecode' specifies the error code. The return value is the message string of the error code. */ const char *tchdberrmsg(int ecode); /* Create a hash database object. The return value is the new hash database object. */ TCHDB *tchdbnew(void); /* Delete a hash database object. `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. */ void tchdbdel(TCHDB *hdb); /* Get the last happened error code of a hash database object. `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. */ int tchdbecode(TCHDB *hdb); /* Set mutual exclusion control of a hash database object for threading. `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. */ bool tchdbsetmutex(TCHDB *hdb); /* Set the tuning parameters of a hash database object. `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. */ bool tchdbtune(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts); /* Set the caching parameters of a hash database object. `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. */ bool tchdbsetcache(TCHDB *hdb, int32_t rcnum); /* Set the size of the extra mapped memory of a hash database object. `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. */ bool tchdbsetxmsiz(TCHDB *hdb, int64_t xmsiz); /* Set the unit step number of auto defragmentation of a hash database object. `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. */ bool tchdbsetdfunit(TCHDB *hdb, int32_t dfunit); /* Open a database file and connect a hash database object. `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. */ bool tchdbopen(TCHDB *hdb, const char *path, int omode); /* Close a hash database object. `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. */ bool tchdbclose(TCHDB *hdb); /* Store a record into a hash database object. `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. */ bool tchdbput(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a string record into a hash database object. `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. */ bool tchdbput2(TCHDB *hdb, const char *kstr, const char *vstr); /* Store a new record into a hash database object. `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. */ bool tchdbputkeep(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a new string record into a hash database object. `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. */ bool tchdbputkeep2(TCHDB *hdb, const char *kstr, const char *vstr); /* Concatenate a value at the end of the existing record in a hash database object. `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. */ bool tchdbputcat(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Concatenate a string value at the end of the existing record in a hash database object. `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. */ bool tchdbputcat2(TCHDB *hdb, const char *kstr, const char *vstr); /* Store a record into a hash database object in asynchronous fashion. `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. */ bool tchdbputasync(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a string record into a hash database object in asynchronous fashion. `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. */ bool tchdbputasync2(TCHDB *hdb, const char *kstr, const char *vstr); /* Remove a record of a hash database object. `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. */ bool tchdbout(TCHDB *hdb, const void *kbuf, int ksiz); /* Remove a string record of a hash database object. `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. */ bool tchdbout2(TCHDB *hdb, const char *kstr); /* Retrieve a record in a hash database object. `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. */ void *tchdbget(TCHDB *hdb, const void *kbuf, int ksiz, int *sp); /* Retrieve a string record in a hash database object. `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. */ char *tchdbget2(TCHDB *hdb, const char *kstr); /* Retrieve a record in a hash database object and write the value into a buffer. `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. */ int tchdbget3(TCHDB *hdb, const void *kbuf, int ksiz, void *vbuf, int max); /* Get the size of the value of a record in a hash database object. `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. */ int tchdbvsiz(TCHDB *hdb, const void *kbuf, int ksiz); /* Get the size of the value of a string record in a hash database object. `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. */ int tchdbvsiz2(TCHDB *hdb, const char *kstr); /* Initialize the iterator of a hash database object. `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. */ bool tchdbiterinit(TCHDB *hdb); /* Get the next key of the iterator of a hash database object. `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. */ void *tchdbiternext(TCHDB *hdb, int *sp); /* Get the next key string of the iterator of a hash database object. `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. */ char *tchdbiternext2(TCHDB *hdb); /* Get the next extensible objects of the iterator of a hash database object. `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. */ bool tchdbiternext3(TCHDB *hdb, TCXSTR *kxstr, TCXSTR *vxstr); /* Get forward matching keys in a hash database object. `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. */ TCLIST *tchdbfwmkeys(TCHDB *hdb, const void *pbuf, int psiz, int max); /* Get forward matching string keys in a hash database object. `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. */ TCLIST *tchdbfwmkeys2(TCHDB *hdb, const char *pstr, int max); /* Add an integer to a record in a hash database object. `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. */ int tchdbaddint(TCHDB *hdb, const void *kbuf, int ksiz, int num); /* Add a real number to a record in a hash database object. `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. */ double tchdbadddouble(TCHDB *hdb, const void *kbuf, int ksiz, double num); /* Synchronize updated contents of a hash database object with the file and the device. `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. */ bool tchdbsync(TCHDB *hdb); /* Optimize the file of a hash database object. `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. */ bool tchdboptimize(TCHDB *hdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts); /* Remove all records of a hash database object. `hdb' specifies the hash database object connected as a writer. If successful, the return value is true, else, it is false. */ bool tchdbvanish(TCHDB *hdb); /* Copy the database file of a hash database object. `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. */ bool tchdbcopy(TCHDB *hdb, const char *path); /* Begin the transaction of a hash database object. `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. */ bool tchdbtranbegin(TCHDB *hdb); /* Commit the transaction of a hash database object. `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. */ bool tchdbtrancommit(TCHDB *hdb); /* Abort the transaction of a hash database object. `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. */ bool tchdbtranabort(TCHDB *hdb); /* Get the file path of a hash database object. `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. */ const char *tchdbpath(TCHDB *hdb); /* Get the number of records of a hash database object. `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. */ uint64_t tchdbrnum(TCHDB *hdb); /* Get the size of the database file of a hash database object. `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. */ uint64_t tchdbfsiz(TCHDB *hdb); /************************************************************************************************* * features for experts *************************************************************************************************/ /* Set the error code of a hash database object. `hdb' specifies the hash database object. `ecode' specifies the error code. `file' specifies the file name of the code. `line' specifies the line number of the code. `func' specifies the function name of the code. */ void tchdbsetecode(TCHDB *hdb, int ecode, const char *filename, int line, const char *func); /* Set the type of a hash database object. `hdb' specifies the hash database object. `type' specifies the database type. */ void tchdbsettype(TCHDB *hdb, uint8_t type); /* Set the file descriptor for debugging output. `hdb' specifies the hash database object. `fd' specifies the file descriptor for debugging output. */ void tchdbsetdbgfd(TCHDB *hdb, int fd); /* Get the file descriptor for debugging output. `hdb' specifies the hash database object. The return value is the file descriptor for debugging output. */ int tchdbdbgfd(TCHDB *hdb); /* Check whether mutual exclusion control is set to a hash database object. `hdb' specifies the hash database object. If mutual exclusion control is set, it is true, else it is false. */ bool tchdbhasmutex(TCHDB *hdb); /* Synchronize updating contents on memory of a hash database object. `hdb' specifies the hash database object connected as a writer. `phys' specifies whether to synchronize physically. If successful, the return value is true, else, it is false. */ bool tchdbmemsync(TCHDB *hdb, bool phys); /* Get the number of elements of the bucket array of a hash database object. `hdb' specifies the hash database object. The return value is the number of elements of the bucket array or 0 if the object does not connect to any database file. */ uint64_t tchdbbnum(TCHDB *hdb); /* Get the record alignment of a hash database object. `hdb' specifies the hash database object. The return value is the record alignment or 0 if the object does not connect to any database file. */ uint32_t tchdbalign(TCHDB *hdb); /* Get the maximum number of the free block pool of a a hash database object. `hdb' specifies the hash database object. The return value is the maximum number of the free block pool or 0 if the object does not connect to any database file. */ uint32_t tchdbfbpmax(TCHDB *hdb); /* Get the size of the extra mapped memory of a hash database object. `hdb' specifies the hash database object. The return value is the size of the extra mapped memory or 0 if the object does not connect to any database file. */ uint64_t tchdbxmsiz(TCHDB *hdb); /* Get the inode number of the database file of a hash database object. `hdb' specifies the hash database object. The return value is the inode number of the database file or 0 if the object does not connect to any database file. */ uint64_t tchdbinode(TCHDB *hdb); /* Get the modification time of the database file of a hash database object. `hdb' specifies the hash database object. The return value is the inode number of the database file or 0 if the object does not connect to any database file. */ time_t tchdbmtime(TCHDB *hdb); /* Get the connection mode of a hash database object. `hdb' specifies the hash database object. The return value is the connection mode. */ int tchdbomode(TCHDB *hdb); /* Get the database type of a hash database object. `hdb' specifies the hash database object. The return value is the database type. */ uint8_t tchdbtype(TCHDB *hdb); /* Get the additional flags of a hash database object. `hdb' specifies the hash database object. The return value is the additional flags. */ uint8_t tchdbflags(TCHDB *hdb); /* Get the options of a hash database object. `hdb' specifies the hash database object. The return value is the options. */ uint8_t tchdbopts(TCHDB *hdb); /* Get the pointer to the opaque field of a hash database object. `hdb' specifies the hash database object. The return value is the pointer to the opaque field whose size is 128 bytes. */ char *tchdbopaque(TCHDB *hdb); /* Get the number of used elements of the bucket array of a hash database object. `hdb' specifies the hash database object. The return value is the number of used elements of the bucket array or 0 if the object does not connect to any database file. */ uint64_t tchdbbnumused(TCHDB *hdb); /* Set the custom codec functions of a hash database object. `hdb' specifies the hash database object. `enc' specifies the pointer to the custom encoding function. It receives four parameters. The first parameter is the pointer to the region. The second parameter is the size of the region. The third parameter is the pointer to the variable into which the size of the region of the return value is assigned. The fourth parameter is the pointer to the optional opaque object. It returns the pointer to the result object allocated with `malloc' call if successful, else, it returns `NULL'. `encop' specifies an arbitrary pointer to be given as a parameter of the encoding function. If it is not needed, `NULL' can be specified. `dec' specifies the pointer to the custom decoding function. `decop' specifies an arbitrary pointer to be given as a parameter of the decoding function. If it is not needed, `NULL' can be specified. If successful, the return value is true, else, it is false. Note that the custom codec functions should be set before the database is opened and should be set every time the database is being opened. */ bool tchdbsetcodecfunc(TCHDB *hdb, TCCODEC enc, void *encop, TCCODEC dec, void *decop); /* Get the custom codec functions of a hash database object. `hdb' specifies the hash database object. `ep' specifies the pointer to a variable into which the pointer to the custom encoding function is assigned `eop' specifies the pointer to a variable into which the arbitrary pointer to be given to the encoding function is assigned. `dp' specifies the pointer to a variable into which the pointer to the custom decoding function is assigned `dop' specifies the pointer to a variable into which the arbitrary pointer to be given to the decoding function is assigned. */ void tchdbcodecfunc(TCHDB *hdb, TCCODEC *ep, void **eop, TCCODEC *dp, void **dop); /* Get the unit step number of auto defragmentation of a hash database object. `hdb' specifies the hash database object. The return value is the unit step number of auto defragmentation. */ uint32_t tchdbdfunit(TCHDB *hdb); /* Perform dynamic defragmentation of a hash database object. `hdb' specifies the hash database object connected as a writer. `step' specifie the number of steps. If it is not more than 0, the whole file is defragmented gradually without keeping a continuous lock. If successful, the return value is true, else, it is false. */ bool tchdbdefrag(TCHDB *hdb, int64_t step); /* Clear the cache of a hash tree database object. `hdb' specifies the hash tree database object. If successful, the return value is true, else, it is false. */ bool tchdbcacheclear(TCHDB *hdb); /* Store a record into a hash database object with a duplication handler. `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. `NULL' means that record addition is ommited if there is no corresponding record. `vsiz' specifies the size of the region of the value. `proc' specifies the pointer to the callback function to process duplication. It receives four parameters. The first parameter is the pointer to the region of the value. The second parameter is the size of the region of the value. The third parameter is the pointer to the variable into which the size of the region of the return value is assigned. The fourth parameter is the pointer to the optional opaque object. It returns the pointer to the result object allocated with `malloc'. It is released by the caller. If it is `NULL', the record is not modified. If it is `(void *)-1', the record is removed. `op' specifies an arbitrary pointer to be given as a parameter of the callback function. If it is not needed, `NULL' can be specified. If successful, the return value is true, else, it is false. Note that the callback function can not perform any database operation because the function is called in the critical section guarded by the same locks of database operations. */ bool tchdbputproc(TCHDB *hdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op); /* Retrieve the next record of a record in a hash database object. `hdb' specifies the hash database object. `kbuf' specifies the pointer to the region of the key. If it is `NULL', the first record is retrieved. `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 key of the next 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. */ void *tchdbgetnext(TCHDB *hdb, const void *kbuf, int ksiz, int *sp); /* Retrieve the next string record in a hash database object. `hdb' specifies the hash database object. `kstr' specifies the string of the key. If it is `NULL', the first record is retrieved. If successful, the return value is the string of the key of the next 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. */ char *tchdbgetnext2(TCHDB *hdb, const char *kstr); /* Retrieve the key and the value of the next record of a record in a hash database object. `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. `vbp' specifies the pointer to the variable into which the pointer to the value is assigned. `vsp' specifies the pointer to the variable into which the size of the value is assigned. If successful, the return value is the pointer to the region of the key of the next record. 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 retion pointed to by `vbp' should not be released. */ char *tchdbgetnext3(TCHDB *hdb, const char *kbuf, int ksiz, int *sp, const char **vbp, int *vsp); /* Move the iterator to the record corresponding a key of a hash database object. `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 true, else, it is false. False is returned if there is no record corresponding the condition. */ bool tchdbiterinit2(TCHDB *hdb, const void *kbuf, int ksiz); /* Move the iterator to the record corresponding a key string of a hash database object. `hdb' specifies the hash database 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. */ bool tchdbiterinit3(TCHDB *hdb, const char *kstr); /* Process each record atomically of a hash database object. `hdb' specifies the hash database object. `iter' specifies the pointer to the iterator function called for each record. It receives five parameters. The first parameter is the pointer to the region of the key. The second parameter is the size of the region of the key. The third parameter is the pointer to the region of the value. The fourth parameter is the size of the region of the value. The fifth parameter is the pointer to the optional opaque object. It returns true to continue iteration or false to stop 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. Note that the callback function can not perform any database operation because the function is called in the critical section guarded by the same locks of database operations. */ bool tchdbforeach(TCHDB *hdb, TCITER iter, void *op); /* Void the transaction of a hash database object. `hdb' specifies the hash database object connected as a writer. If successful, the return value is true, else, it is false. This function should be called only when no update in the transaction. */ bool tchdbtranvoid(TCHDB *hdb); __TCHDB_CLINKAGEEND #endif /* duplication check */ /* END OF FILE */ tokyocabinet-1.4.48/tcumttest.c0000644000175000017500000004040112013574446015512 0ustar mikiomikio/************************************************************************************************* * The test cases of the on-memory database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include "myconf.h" #define RECBUFSIZ 48 // buffer for records typedef struct { // type of structure for combo thread TCMDB *mdb; TCNDB *ndb; int rnum; bool rnd; int id; } TARGCOMBO; typedef struct { // type of structure for typical thread TCMDB *mdb; TCNDB *ndb; int rnum; bool nc; int rratio; int id; } TARGTYPICAL; /* global variables */ const char *g_progname; // program name unsigned int g_randseed; // random seed /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void iprintf(const char *format, ...); static void iputchar(int c); static void eprint(int line, const char *func); static int myrand(int range); static int myrandnd(int range); static int runcombo(int argc, char **argv); static int runtypical(int argc, char **argv); static int proccombo(int tnum, int rnum, int bnum, bool tr, bool rnd); static int proctypical(int tnum, int rnum, int bnum, bool tr, bool nc, int rratio); static void *threadwrite(void *targ); static void *threadread(void *targ); static void *threadremove(void *targ); static void *threadtypical(void *targ); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; const char *ebuf = getenv("TCRNDSEED"); g_randseed = ebuf ? tcatoix(ebuf) : tctime() * 1000; srand(g_randseed); if(argc < 2) usage(); int rv = 0; if(!strcmp(argv[1], "combo")){ rv = runcombo(argc, argv); } else if(!strcmp(argv[1], "typical")){ rv = runtypical(argc, argv); } else { usage(); } if(rv != 0){ printf("FAILED: TCRNDSEED=%u PID=%d", g_randseed, (int)getpid()); for(int i = 0; i < argc; i++){ printf(" %s", argv[i]); } printf("\n\n"); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: test cases of the on-memory database API of Tokyo Cabinet\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s combo [-tr] [-rnd] tnum rnum [bnum]\n", g_progname); fprintf(stderr, " %s typical [-tr] [-nc] [-rr num] tnum rnum [bnum]\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); } /* print a character and flush the buffer */ static void iputchar(int c){ putchar(c); fflush(stdout); } /* print error message of on-memory database */ static void eprint(int line, const char *func){ fprintf(stderr, "%s: %d: %s: error\n", g_progname, line, func); } /* get a random number */ static int myrand(int range){ if(range < 2) return 0; int high = (unsigned int)rand() >> 4; int low = range * (rand() / (RAND_MAX + 1.0)); low &= (unsigned int)INT_MAX >> 4; return (high + low) % range; } /* get a random number based on normal distribution */ static int myrandnd(int range){ int num = (int)tcdrandnd(range >> 1, range / 10); return (num < 0 || num >= range) ? 0 : num; } /* parse arguments of combo command */ static int runcombo(int argc, char **argv){ char *tstr = NULL; char *rstr = NULL; char *bstr = NULL; bool tr = false; bool rnd = false; for(int i = 2; i < argc; i++){ if(!tstr && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tr")){ tr = true; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else { usage(); } } if(!tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int bnum = bstr ? tcatoix(bstr) : -1; int rv = proccombo(tnum, rnum, bnum, tr, rnd); return rv; } /* parse arguments of typical command */ static int runtypical(int argc, char **argv){ char *tstr = NULL; char *rstr = NULL; char *bstr = NULL; bool tr = false; int rratio = -1; bool nc = false; for(int i = 2; i < argc; i++){ if(!tstr && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tr")){ tr = true; } else if(!strcmp(argv[i], "-nc")){ nc = true; } else if(!strcmp(argv[i], "-rr")){ if(++i >= argc) usage(); rratio = tcatoix(argv[i]); } else { usage(); } } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else { usage(); } } if(!tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int bnum = bstr ? tcatoix(bstr) : -1; int rv = proctypical(tnum, rnum, bnum, tr, nc, rratio); return rv; } /* perform combo command */ static int proccombo(int tnum, int rnum, int bnum, bool tr, bool rnd){ iprintf("\n seed=%u tnum=%d rnum=%d bnum=%d tr=%d rnd=%d\n\n", g_randseed, tnum, rnum, bnum, tr, rnd); bool err = false; double stime = tctime(); TCMDB *mdb = (bnum > 0) ? tcmdbnew2(bnum) : tcmdbnew(); TCNDB *ndb = tcndbnew(); TARGCOMBO targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].mdb = mdb; targs[0].ndb = tr ? ndb : NULL; targs[0].rnum = rnum; targs[0].rnd = rnd; targs[0].id = 0; if(threadwrite(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].mdb = mdb; targs[i].ndb = tr ? ndb : NULL; targs[i].rnum = rnum; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadwrite, targs + i) != 0){ eprint(__LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(__LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } if(tnum == 1){ targs[0].mdb = mdb; targs[0].ndb = tr ? ndb : NULL; targs[0].rnum = rnum; targs[0].rnd = rnd; targs[0].id = 0; if(threadread(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].mdb = mdb; targs[i].ndb = tr ? ndb : NULL; targs[i].rnum = rnum; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){ eprint(__LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(__LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } if(tnum == 1){ targs[0].mdb = mdb; targs[0].ndb = tr ? ndb : NULL; targs[0].rnum = rnum; targs[0].rnd = rnd; targs[0].id = 0; if(threadremove(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].mdb = mdb; targs[i].ndb = tr ? ndb : NULL; targs[i].rnum = rnum; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadremove, targs + i) != 0){ eprint(__LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(__LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } if(tr){ iprintf("record number: %llu\n", (unsigned long long)tcndbrnum(ndb)); iprintf("size: %llu\n", (unsigned long long)tcndbmsiz(ndb)); } else { iprintf("record number: %llu\n", (unsigned long long)tcmdbrnum(mdb)); iprintf("size: %llu\n", (unsigned long long)tcmdbmsiz(mdb)); } tcndbdel(ndb); tcmdbdel(mdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform typical command */ static int proctypical(int tnum, int rnum, int bnum, bool tr, bool nc, int rratio){ iprintf("\n seed=%u tnum=%d rnum=%d bnum=%d tr=%d nc=%d" " rratio=%d\n\n", g_randseed, tnum, rnum, bnum, tr, nc, rratio); bool err = false; double stime = tctime(); TCMDB *mdb = (bnum > 0) ? tcmdbnew2(bnum) : tcmdbnew(); TCNDB *ndb = tcndbnew(); TARGTYPICAL targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].mdb = mdb; targs[0].ndb = tr ? ndb : NULL; targs[0].rnum = rnum; targs[0].nc = nc; targs[0].rratio = rratio; targs[0].id = 0; if(threadtypical(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].mdb = mdb; targs[i].ndb = tr ? ndb : NULL; targs[i].rnum = rnum; targs[i].nc = nc; targs[i].rratio= rratio; targs[i].id = i; if(pthread_create(threads + i, NULL, threadtypical, targs + i) != 0){ eprint(__LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(__LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } if(tr){ iprintf("record number: %llu\n", (unsigned long long)tcndbrnum(ndb)); iprintf("size: %llu\n", (unsigned long long)tcndbmsiz(ndb)); } else { iprintf("record number: %llu\n", (unsigned long long)tcmdbrnum(mdb)); iprintf("size: %llu\n", (unsigned long long)tcmdbmsiz(mdb)); } tcndbdel(ndb); tcmdbdel(mdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* thread the write function */ static void *threadwrite(void *targ){ TCMDB *mdb = ((TARGCOMBO *)targ)->mdb; TCNDB *ndb = ((TARGCOMBO *)targ)->ndb; int rnum = ((TARGCOMBO *)targ)->rnum; bool rnd = ((TARGCOMBO *)targ)->rnd; int id = ((TARGCOMBO *)targ)->id; double stime = tctime(); if(id == 0) iprintf("writing:\n"); int base = id * rnum; for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) : i)); if(ndb){ tcndbput(ndb, buf, len, buf, len); } else { tcmdbput(mdb, buf, len, buf, len); } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(id == 0) iprintf("time: %.3f\n", tctime() - stime); return NULL; } /* thread the read function */ static void *threadread(void *targ){ TCMDB *mdb = ((TARGCOMBO *)targ)->mdb; TCNDB *ndb = ((TARGCOMBO *)targ)->ndb; int rnum = ((TARGCOMBO *)targ)->rnum; bool rnd = ((TARGCOMBO *)targ)->rnd; int id = ((TARGCOMBO *)targ)->id; double stime = tctime(); if(id == 0) iprintf("reading:\n"); int base = id * rnum; for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrand(i) : i)); int vsiz; char *vbuf = ndb ? tcndbget(ndb, kbuf, ksiz, &vsiz) : tcmdbget(mdb, kbuf, ksiz, &vsiz); if(vbuf) tcfree(vbuf); if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(id == 0) iprintf("time: %.3f\n", tctime() - stime); return NULL; } /* thread the remove function */ static void *threadremove(void *targ){ TCMDB *mdb = ((TARGCOMBO *)targ)->mdb; TCNDB *ndb = ((TARGCOMBO *)targ)->ndb; int rnum = ((TARGCOMBO *)targ)->rnum; bool rnd = ((TARGCOMBO *)targ)->rnd; int id = ((TARGCOMBO *)targ)->id; double stime = tctime(); if(id == 0) iprintf("removing:\n"); int base = id * rnum; for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) : i)); if(ndb){ tcndbout(ndb, buf, len); } else { tcmdbout(mdb, buf, len); } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(id == 0) iprintf("time: %.3f\n", tctime() - stime); return NULL; } /* thread the typical function */ static void *threadtypical(void *targ){ TCMDB *mdb = ((TARGTYPICAL *)targ)->mdb; TCNDB *ndb = ((TARGCOMBO *)targ)->ndb; int rnum = ((TARGTYPICAL *)targ)->rnum; bool nc = ((TARGTYPICAL *)targ)->nc; int rratio = ((TARGTYPICAL *)targ)->rratio; int id = ((TARGTYPICAL *)targ)->id; bool err = false; TCMAP *map = (!nc && id == 0) ? tcmapnew2(rnum + 1) : NULL; int base = id * rnum; int mrange = tclmax(50 + rratio, 100); for(int i = 1; !err && i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", base + myrandnd(i)); int rnd = myrand(mrange); if(rnd < 10){ if(ndb){ tcndbput(ndb, buf, len, buf, len); } else { tcmdbput(mdb, buf, len, buf, len); } if(map) tcmapput(map, buf, len, buf, len); } else if(rnd < 15){ if(ndb){ tcndbputkeep(ndb, buf, len, buf, len); } else { tcmdbputkeep(mdb, buf, len, buf, len); } if(map) tcmapputkeep(map, buf, len, buf, len); } else if(rnd < 20){ if(ndb){ tcndbputcat(ndb, buf, len, buf, len); } else { tcmdbputcat(mdb, buf, len, buf, len); } if(map) tcmapputcat(map, buf, len, buf, len); } else if(rnd < 30){ if(ndb){ tcndbout(ndb, buf, len); } else { tcmdbout(mdb, buf, len); } if(map) tcmapout(map, buf, len); } else if(rnd < 31){ if(myrand(10) == 0) tcmdbiterinit(mdb); for(int j = 0; !err && j < 10; j++){ int ksiz; char *kbuf = ndb ? tcndbiternext(ndb, &ksiz) : tcmdbiternext(mdb, &ksiz); if(kbuf) tcfree(kbuf); } } else { int vsiz; char *vbuf = ndb ? tcndbget(ndb, buf, len, &vsiz) : tcmdbget(mdb, buf, len, &vsiz); if(vbuf){ if(map){ int msiz; const char *mbuf = tcmapget(map, buf, len, &msiz); if(msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){ eprint(__LINE__, "(validation)"); err = true; } } tcfree(vbuf); } else { if(map && tcmapget(map, buf, len, &vsiz)){ eprint(__LINE__, "(validation)"); err = true; } } } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(map){ tcmapiterinit(map); int ksiz; const char *kbuf; while(!err && (kbuf = tcmapiternext(map, &ksiz)) != NULL){ int vsiz; char *vbuf = ndb ? tcndbget(ndb, kbuf, ksiz, &vsiz) : tcmdbget(mdb, kbuf, ksiz, &vsiz); if(vbuf){ int msiz; const char *mbuf = tcmapget(map, kbuf, ksiz, &msiz); if(!mbuf || msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){ eprint(__LINE__, "(validation)"); err = true; } tcfree(vbuf); } else { eprint(__LINE__, "(validation)"); err = true; } } tcmapdel(map); } return err ? "error" : NULL; } // END OF FILE tokyocabinet-1.4.48/tcfdb.h0000644000175000017500000013612312013574446014554 0ustar mikiomikio/************************************************************************************************* * The fixed-length database API of Tokyo Cabinet * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #ifndef _TCFDB_H /* duplication check */ #define _TCFDB_H #if defined(__cplusplus) #define __TCFDB_CLINKAGEBEGIN extern "C" { #define __TCFDB_CLINKAGEEND } #else #define __TCFDB_CLINKAGEBEGIN #define __TCFDB_CLINKAGEEND #endif __TCFDB_CLINKAGEBEGIN #include /************************************************************************************************* * API *************************************************************************************************/ typedef struct { /* type of structure for a fixed-length database */ void *mmtx; /* mutex for method */ void *amtx; /* mutex for attribute */ void *rmtxs; /* mutexes for records */ void *tmtx; /* mutex for transaction */ void *wmtx; /* mutex for write ahead logging */ void *eckey; /* key for thread specific error code */ char *rpath; /* real path for locking */ uint8_t type; /* database type */ uint8_t flags; /* additional flags */ uint32_t width; /* width of the value of each record */ uint64_t limsiz; /* limit size of the file */ int wsiz; /* size of the width region */ int rsiz; /* size of each record */ uint64_t limid; /* limit ID number */ char *path; /* path of the database file */ int fd; /* file descriptor of the database file */ uint32_t omode; /* open mode */ uint64_t rnum; /* number of the records */ uint64_t fsiz; /* size of the database file */ uint64_t min; /* minimum ID number */ uint64_t max; /* maximum ID number */ uint64_t iter; /* ID number of the iterator */ char *map; /* pointer to the mapped memory */ unsigned char *array; /* pointer to the array region */ int ecode; /* last happened error code */ bool fatal; /* whether a fatal error occured */ uint64_t inode; /* inode number */ time_t mtime; /* modification time */ bool tran; /* whether in the transaction */ int walfd; /* file descriptor of write ahead logging */ uint64_t walend; /* end offset of write ahead logging */ int dbgfd; /* file descriptor for debugging */ int64_t cnt_writerec; /* tesing counter for record write times */ int64_t cnt_readrec; /* tesing counter for record read times */ int64_t cnt_truncfile; /* tesing counter for file truncate times */ } TCFDB; enum { /* enumeration for additional flags */ FDBFOPEN = 1 << 0, /* whether opened */ FDBFFATAL = 1 << 1 /* whether with fatal error */ }; enum { /* enumeration for open modes */ FDBOREADER = 1 << 0, /* open as a reader */ FDBOWRITER = 1 << 1, /* open as a writer */ FDBOCREAT = 1 << 2, /* writer creating */ FDBOTRUNC = 1 << 3, /* writer truncating */ FDBONOLCK = 1 << 4, /* open without locking */ FDBOLCKNB = 1 << 5, /* lock without blocking */ FDBOTSYNC = 1 << 6 /* synchronize every transaction */ }; enum { /* enumeration for ID constants */ FDBIDMIN = -1, /* minimum number */ FDBIDPREV = -2, /* less by one than the minimum */ FDBIDMAX = -3, /* maximum number */ FDBIDNEXT = -4 /* greater by one than the miximum */ }; /* Get the message string corresponding to an error code. `ecode' specifies the error code. The return value is the message string of the error code. */ const char *tcfdberrmsg(int ecode); /* Create a fixed-length database object. The return value is the new fixed-length database object. */ TCFDB *tcfdbnew(void); /* Delete a fixed-length database object. `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. */ void tcfdbdel(TCFDB *fdb); /* Get the last happened error code of a fixed-length database object. `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. */ int tcfdbecode(TCFDB *fdb); /* Set mutual exclusion control of a fixed-length database object for threading. `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. */ bool tcfdbsetmutex(TCFDB *fdb); /* Set the tuning parameters of a fixed-length database object. `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. */ bool tcfdbtune(TCFDB *fdb, int32_t width, int64_t limsiz); /* Open a database file and connect a fixed-length database object. `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. */ bool tcfdbopen(TCFDB *fdb, const char *path, int omode); /* Close a fixed-length database object. `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. */ bool tcfdbclose(TCFDB *fdb); /* Store a record into a fixed-length database object. `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. */ bool tcfdbput(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz); /* Store a record with a decimal key into a fixed-length database object. `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. */ bool tcfdbput2(TCFDB *fdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a string record with a decimal key into a fixed-length database object. `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. */ bool tcfdbput3(TCFDB *fdb, const char *kstr, const void *vstr); /* Store a new record into a fixed-length database object. `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. */ bool tcfdbputkeep(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz); /* Store a new record with a decimal key into a fixed-length database object. `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. */ bool tcfdbputkeep2(TCFDB *fdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Store a new string record with a decimal key into a fixed-length database object. `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. */ bool tcfdbputkeep3(TCFDB *fdb, const char *kstr, const void *vstr); /* Concatenate a value at the end of the existing record in a fixed-length database object. `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. */ bool tcfdbputcat(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz); /* Concatenate a value with a decimal key in a fixed-length database object. `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. */ bool tcfdbputcat2(TCFDB *fdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz); /* Concatenate a string value with a decimal key in a fixed-length database object. `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. */ bool tcfdbputcat3(TCFDB *fdb, const char *kstr, const void *vstr); /* Remove a record of a fixed-length database object. `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. */ bool tcfdbout(TCFDB *fdb, int64_t id); /* Remove a record with a decimal key of a fixed-length database object. `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. */ bool tcfdbout2(TCFDB *fdb, const void *kbuf, int ksiz); /* Remove a string record with a decimal key of a fixed-length database object. `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. */ bool tcfdbout3(TCFDB *fdb, const char *kstr); /* Retrieve a record in a fixed-length database object. `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. */ void *tcfdbget(TCFDB *fdb, int64_t id, int *sp); /* Retrieve a record with a decimal key in a fixed-length database object. `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. */ void *tcfdbget2(TCFDB *fdb, const void *kbuf, int ksiz, int *sp); /* Retrieve a string record with a decimal key in a fixed-length database object. `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. */ char *tcfdbget3(TCFDB *fdb, const char *kstr); /* Retrieve a record in a fixed-length database object and write the value into a buffer. `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. */ int tcfdbget4(TCFDB *fdb, int64_t id, void *vbuf, int max); /* Get the size of the value of a record in a fixed-length database object. `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. */ int tcfdbvsiz(TCFDB *fdb, int64_t id); /* Get the size of the value with a decimal key in a fixed-length database object. `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. */ int tcfdbvsiz2(TCFDB *fdb, const void *kbuf, int ksiz); /* Get the size of the string value with a decimal key in a fixed-length database object. `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. */ int tcfdbvsiz3(TCFDB *fdb, const char *kstr); /* Initialize the iterator of a fixed-length database object. `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. */ bool tcfdbiterinit(TCFDB *fdb); /* Get the next ID number of the iterator of a fixed-length database object. `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. */ uint64_t tcfdbiternext(TCFDB *fdb); /* Get the next decimay key of the iterator of a fixed-length database object. `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. */ void *tcfdbiternext2(TCFDB *fdb, int *sp); /* Get the next decimay key string of the iterator of a fixed-length database object. `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. */ char *tcfdbiternext3(TCFDB *fdb); /* Get range matching ID numbers in a fixed-length database object. `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. */ uint64_t *tcfdbrange(TCFDB *fdb, int64_t lower, int64_t upper, int max, int *np); /* Get range matching decimal keys in a fixed-length database object. `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. */ TCLIST *tcfdbrange2(TCFDB *fdb, const void *lbuf, int lsiz, const void *ubuf, int usiz, int max); /* Get range matching decimal keys with strings in a fixed-length database object. `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. */ TCLIST *tcfdbrange3(TCFDB *fdb, const char *lstr, const char *ustr, int max); /* Get keys with an interval notation in a fixed-length database object. `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. */ TCLIST *tcfdbrange4(TCFDB *fdb, const void *ibuf, int isiz, int max); /* Get keys with an interval notation string in a fixed-length database object. `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. */ TCLIST *tcfdbrange5(TCFDB *fdb, const void *istr, int max); /* Add an integer to a record in a fixed-length database object. `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. */ int tcfdbaddint(TCFDB *fdb, int64_t id, int num); /* Add a real number to a record in a fixed-length database object. `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. */ double tcfdbadddouble(TCFDB *fdb, int64_t id, double num); /* Synchronize updated contents of a fixed-length database object with the file and the device. `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. */ bool tcfdbsync(TCFDB *fdb); /* Optimize the file of a fixed-length database object. `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. */ bool tcfdboptimize(TCFDB *fdb, int32_t width, int64_t limsiz); /* Remove all records of a fixed-length database object. `fdb' specifies the fixed-length database object connected as a writer. If successful, the return value is true, else, it is false. */ bool tcfdbvanish(TCFDB *fdb); /* Copy the database file of a fixed-length database object. `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. */ bool tcfdbcopy(TCFDB *fdb, const char *path); /* Begin the transaction of a fixed-length database object. `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. */ bool tcfdbtranbegin(TCFDB *fdb); /* Commit the transaction of a fixed-length database object. `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. */ bool tcfdbtrancommit(TCFDB *fdb); /* Abort the transaction of a fixed-length database object. `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. */ bool tcfdbtranabort(TCFDB *fdb); /* Get the file path of a fixed-length database object. `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. */ const char *tcfdbpath(TCFDB *fdb); /* Get the number of records of a fixed-length database object. `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. */ uint64_t tcfdbrnum(TCFDB *fdb); /* Get the size of the database file of a fixed-length database object. `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. */ uint64_t tcfdbfsiz(TCFDB *fdb); /************************************************************************************************* * features for experts *************************************************************************************************/ /* Set the error code of a fixed-length database object. `fdb' specifies the fixed-length database object. `ecode' specifies the error code. `file' specifies the file name of the code. `line' specifies the line number of the code. `func' specifies the function name of the code. */ void tcfdbsetecode(TCFDB *fdb, int ecode, const char *filename, int line, const char *func); /* Set the file descriptor for debugging output. `fdb' specifies the fixed-length database object. `fd' specifies the file descriptor for debugging output. */ void tcfdbsetdbgfd(TCFDB *fdb, int fd); /* Get the file descriptor for debugging output. `fdb' specifies the fixed-length database object. The return value is the file descriptor for debugging output. */ int tcfdbdbgfd(TCFDB *fdb); /* Check whether mutual exclusion control is set to a fixed-length database object. `fdb' specifies the fixed-length database object. If mutual exclusion control is set, it is true, else it is false. */ bool tcfdbhasmutex(TCFDB *fdb); /* Synchronize updating contents on memory of a fixed-length database object. `fdb' specifies the fixed-length database object connected as a writer. `phys' specifies whether to synchronize physically. If successful, the return value is true, else, it is false. */ bool tcfdbmemsync(TCFDB *fdb, bool phys); /* Get the minimum ID number of records of a fixed-length database object. `fdb' specifies the fixed-length database object. The return value is the minimum ID number of records or 0 if the object does not connect to any database file. */ uint64_t tcfdbmin(TCFDB *fdb); /* Get the maximum ID number of records of a fixed-length database object. `fdb' specifies the fixed-length database object. The return value is the maximum ID number of records or 0 if the object does not connect to any database file. */ uint64_t tcfdbmax(TCFDB *fdb); /* Get the width of the value of each record of a fixed-length database object. `fdb' specifies the fixed-length database object. The return value is the width of the value of each record or 0 if the object does not connect to any database file. */ uint32_t tcfdbwidth(TCFDB *fdb); /* Get the limit file size of a fixed-length database object. `fdb' specifies the fixed-length database object. The return value is the limit file size or 0 if the object does not connect to any database file. */ uint64_t tcfdblimsiz(TCFDB *fdb); /* Get the limit ID number of a fixed-length database object. `fdb' specifies the fixed-length database object. The return value is the limit ID number or 0 if the object does not connect to any database file. */ uint64_t tcfdblimid(TCFDB *fdb); /* Get the inode number of the database file of a fixed-length database object. `fdb' specifies the fixed-length database object. The return value is the inode number of the database file or 0 if the object does not connect to any database file. */ uint64_t tcfdbinode(TCFDB *fdb); /* Get the modification time of the database file of a fixed-length database object. `fdb' specifies the fixed-length database object. The return value is the inode number of the database file or 0 if the object does not connect to any database file. */ time_t tcfdbmtime(TCFDB *fdb); /* Get the connection mode of a fixed-length database object. `fdb' specifies the fixed-length database object. The return value is the connection mode. */ int tcfdbomode(TCFDB *fdb); /* Get the database type of a fixed-length database object. `fdb' specifies the fixed-length database object. The return value is the database type. */ uint8_t tcfdbtype(TCFDB *fdb); /* Get the additional flags of a fixed-length database object. `fdb' specifies the fixed-length database object. The return value is the additional flags. */ uint8_t tcfdbflags(TCFDB *fdb); /* Get the pointer to the opaque field of a fixed-length database object. `fdb' specifies the fixed-length database object. The return value is the pointer to the opaque field whose size is 128 bytes. */ char *tcfdbopaque(TCFDB *fdb); /* Store a record into a fixed-length database object with a duplication handler. `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. `NULL' means that record addition is ommited if there is no corresponding record. `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. `proc' specifies the pointer to the callback function to process duplication. It receives four parameters. The first parameter is the pointer to the region of the value. The second parameter is the size of the region of the value. The third parameter is the pointer to the variable into which the size of the region of the return value is assigned. The fourth parameter is the pointer to the optional opaque object. It returns the pointer to the result object allocated with `malloc'. It is released by the caller. If it is `NULL', the record is not modified. If it is `(void *)-1', the record is removed. `op' specifies an arbitrary pointer to be given as a parameter of the callback function. If it is not needed, `NULL' can be specified. If successful, the return value is true, else, it is false. Note that the callback function can not perform any database operation because the function is called in the critical section guarded by the same locks of database operations. */ bool tcfdbputproc(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz, TCPDPROC proc, void *op); /* Move the iterator to the record corresponding a key of a fixed-length database object. `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 true, else, it is false. False is returned if there is no record corresponding the condition. */ bool tcfdbiterinit2(TCFDB *fdb, int64_t id); /* Move the iterator to the decimal record of a fixed-length database object. `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 true, else, it is false. False is returned if there is no record corresponding the condition. */ bool tcfdbiterinit3(TCFDB *fdb, const void *kbuf, int ksiz); /* Move the iterator to the decimal string record of a fixed-length database object. `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 true, else, it is false. False is returned if there is no record corresponding the condition. */ bool tcfdbiterinit4(TCFDB *fdb, const char *kstr); /* Process each record atomically of a fixed-length database object. `fdb' specifies the fixed-length database object. `iter' specifies the pointer to the iterator function called for each record. It receives five parameters. The first parameter is the pointer to the region of the key. The second parameter is the size of the region of the key. The third parameter is the pointer to the region of the value. The fourth parameter is the size of the region of the value. The fifth parameter is the pointer to the optional opaque object. It returns true to continue iteration or false to stop 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. Note that the callback function can not perform any database operation because the function is called in the critical section guarded by the same locks of database operations. */ bool tcfdbforeach(TCFDB *fdb, TCITER iter, void *op); /* Generate the ID number from arbitrary binary data. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. The return value is the ID number. */ int64_t tcfdbkeytoid(const char *kbuf, int ksiz); __TCFDB_CLINKAGEEND #endif /* duplication check */ /* END OF FILE */ tokyocabinet-1.4.48/doc/0000755000175000017500000000000012013574114014050 5ustar mikiomikiotokyocabinet-1.4.48/doc/common.css0000644000175000017500000000566111105512730016057 0ustar mikiomikio/* * Style Sheets commonly used by documents of Tokyo Cabinet */ html { margin: 0em 0em; padding: 0em 0em; background: #eeeeee none; } body { margin: 1em 2em; padding: 0em 0em; background: #eeeeee none; color: #111111; } hr { margin: 2.5em 0em 1.5em 0em; height: 1px; border: none; background: #999999 none; color: #999999; } h1,h2,h3,h4,h5,h6 { font-weight: bold; } h1 { margin: 1.0em 0em 1.3em 0em; padding: 0em 0em; font-size: 180%; color: #000000; } h2 { margin: 1.0em 0em 1.0em 0.2em; padding: 0.5em 0.5em; width: 60%; border-left: solid 0.6em #445555; border-bottom: solid 1px #bbbbbb; font-size: 150%; color: #000011; } h3 { margin: 0.8em 0em 0.5em 0.2em; padding: 0em 0em; font-size: 120%; color: #001111; } p { margin: 0.8em 0em; line-height: 140%; text-indent: 0.8em; } div,pre,table { margin: 0.8em 1.5em; } div.note,div.navi { text-align: right; margin: 0em 0.5em; color: #333333; } span.void { color: #888888; } div.logo { text-align: center; margin: 3em 0em; } div.logo img { border: inset 2px #ccccdd; } div.illust { margin: 1em 0em; text-align: center; } div.illust img { border: solid 1px #ccccdd; } pre { padding: 0.2em; background-color: #ddddee; border: 1px solid #bbbbcc; font-size: 95%; } li,dt,dd { line-height: 130%; } dt { margin-left: 1.2em; } dd { margin-left: 2.5em; text-indent: -0.3em; } dl.api { margin-top: -0.2em; } dl.api dd { margin-left: 3.0em; font-size: 95%; color: #333333; } ul { margin: 0.5em 2.0em; padding: 0em; } ul.options { list-style-type: none; margin: 0.5em 1.5em; font-size: 95%; color: #333333; } ul ul { margin-top: 0em; margin-bottom: 0em; } table { border-collapse: collapse; } td { text-align: left; vertical-align: top; padding: 0.1em 0.5em; border: solid 1px #aaaabb; font-size: 95%; } td.label { border: none; font-size: 80%; color: #333333; } td.number { text-align: right; } a { color: #0022aa; text-decoration: none; } a:hover,a:focus { color: #0033ee; text-decoration: underline; } code,kbd { font-style: normal; font-weight: bold; font-size: 100%; color: #001111; } var { padding: 0em 0.15em 0em 0em; font-style: italic; color: #001122; } @media print { html,body { margin: 0em 0em; background-color: #ffffff; color: #000000; } h1 { padding: 8em 0em 0.5em 0em; text-align: center; } h2 { page-break-before: always; } div.note { text-align: center; } div.navi,div.logo { display: none; } hr { display: none; } pre { margin: 0.8em 0.8em; background-color: #ffffff; border: 1px solid #aaaaaa; font-size: 90%; } a,code,kbd { color: #000000; text-decoration: none; } h1,h2,h3 { font-family: sans-serif; } p,div,li,dt,dd { font-family: serif; } pre,code,kbd { font-family: monospace; } dd { font-size: 90%; } } /* END OF FILE */ tokyocabinet-1.4.48/doc/spex-en.html0000644000175000017500000154732512013574114016336 0ustar mikiomikio 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. Features
    3. Installation
    4. The Utility API
    5. The Hash Database API
    6. The B+ Tree Database API
    7. The Fixed-length Database API
    8. The Table Database API
    9. The Abstract Database API
    10. File Format
    11. License

    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.

    name offset length feature
    magic number 0 32 identification of the database. Begins with "ToKyO CaBiNeT"
    database type 32 1 hash (0x01) / B+ tree (0x02) / fixed-length (0x03) / table (0x04)
    additional flags 33 1 logical union of open (1<<0) and fatal (1<<1)
    alignment power 34 1 the alignment size, by power of 2
    free block pool power 35 1 the number of elements in the free block pool, by power of 2
    options 36 1 logical union of large (1<<0), Deflate (1<<1), BZIP2 (1<<2), TCBS (1<<3), extra codec (1<<4)
    bucket number 40 8 the number of elements of the bucket array
    record number 48 8 the number of records in the database
    file size 56 8 the file size of the database
    first record 64 8 the offset of the first record
    opaque region 128 128 users 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.

    name offset length feature
    magic number 0 1 identification of record block. always 0xC8
    hash value 1 1 the hash value to decide the path of the hash chain
    left chain 2 4 the alignment quotient of the destination of the left chain
    right chain 6 4 the alignment quotient of the destination of the right chain
    padding size 10 2 the size of the padding
    key size 12 vary the size of the key
    value size vary vary the size of the value
    key vary vary the data of the key
    value vary vary the data of the value
    padding vary vary useless data

    However, regions of free blocks contain the following information.

    name offset length feature
    magic number 0 1 identification of record block. always 0xB0
    block size 1 4 size 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.

    name offset length feature
    offset 0 8 the offset of the updated region
    size 8 4 the size of the updated region
    data 12 vary the 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.

    name offset length feature
    comparison function 0 1 tccmplexical (0x00), tccmpdecimal (0x01), tccmpint32 (0x02), tccmpint64 (0x03), other (0xff)
    reserved region 1 7 not used
    record number of leaf node 8 4 the maximum number of records in a leaf node
    index number of non-leaf node 12 4 the maximum number of indices in a leaf node
    root node ID 16 8 the page ID of the root node of B+ tree
    first leaf ID 24 8 the page ID of the first leaf node
    last leaf ID 32 8 the page ID of the last leaf node
    leaf number 40 8 the number of the leaf nodes
    non-leaf number 48 8 the number of the non-leaf nodes
    record number 56 8 the 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.

    name offset length feature
    key size 0 vary the size of the key
    value size vary vary the size of the value
    duplication number vary vary the number of values with the same key
    key vary vary the data of the key
    value vary vary the data of the value
    duplicated records vary vary a 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.

    name offset length feature
    previous leaf 0 vary the ID number of the previous leaf node
    next leaf vary vary the ID number of the next leaf node
    record list vary vary the 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.

    name offset length feature
    page ID 0 vary the ID number of the referred page
    key size vary vary the size of the key
    key vary vary the 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.

    name offset length feature
    accession ID 0 vary the ID number of the first child node
    index list vary vary the 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.

    name offset length feature
    magic number 0 32 identification of the database. Begins with "ToKyO CaBiNeT"
    database type 32 1 always 0x03
    additional flags 33 1 logical union of open (1<<0) and fatal (1<<1)
    record number 48 8 the number of records in the database
    file size 56 8 the file size of the database
    record width 64 8 the width of each record
    limit size 72 8 the limit size of the database
    least ID 80 8 the least ID number of records
    greatest ID 88 8 the greatest ID number of records
    opaque region 128 128 users 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.

    name offset length feature
    value size 0 vary the size of the value
    value vary vary the data of the value
    padding vary vary padding. 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'.


    tokyocabinet-1.4.48/doc/tokyoproducts.pdf0000644000175000017500000111156711375376035017524 0ustar mikiomikio%PDF-1.3 %Çì¢ 5 0 obj <> stream xœÝ”O‹AÅï}ôÔqf!mUuWwµ„¼íšUtYݰn"h„¬ß¬êüq3‚ êA3!¼žÔ¼ª÷£{6€‘Я½¸Y‡‡çV_Ã&07̰ürè­ü^ŸÀ—ÐZkBæu·:XdA„‚ösw–'á,ô®pþ|'¬v4&ÿô÷õÍNç6WƒÊ±˜/{#ë“™`f_®HZÔ óu¸àñˆ%cjy€ëq†1åÔ8 °gÙø|pML©°ð'Ô|Õ¯ü)V´ò§KÉfuዊ",Ý £5/h¾—½I±OÌŽÚ?è‹Vká‰Ábɱh½š¿OçÆ‰À/ã´¿Ï©hçTþ3Nz?%ÑØƲãô¬sJšT&S_>êŸzæL凚]¥bhN;ÌZ~ lÅ\~övjQýµý“D:—\ÿY.ë$ ½ÿ¶ÅZ0źOøÄÛ§Zs’¾y)"eÕmÀšÛäþbœ%n±‰ezäƒdÄI‰ÅKÖ9‹íí—®‹›æŽÌr Ý6~)%êÕè[¿VÏ5s0ŠE­þ­OCÜR?b\<ñ>0éý´û¬¬1[֪߳ƑbRÂì~6-s퓳‘MY|p{'¨Þ> i¦šzž~"±U_;ºBª^϶hæ˜Ìü‡K%+zo–5³h·Ô(s§e„JeËüÎu6*æ®IÅ]˜£Ú«+wè‡q÷†¶Cp<œ…³ð ~faxendstream endobj 6 0 obj 564 endobj 15 0 obj <> stream xœíZßG‚Y¼À<îXº¡«·°ü–ìaòÃqâœÛÉæŸ¯zzzª÷ön÷ÂÜ]"áÈNÝlÏ×UÕU_UõÞëN ¤;ÅÿM—ÛÕŸ> ݳ¯^¯´NS¶ÛfɳøjeÃŽ<ŠÏWîu߬2RJɰÞ<«Ö)Õy…Þ|µ:»·ú`•wí>ü{°öõ*†ÿäRþrÛÝ?…^:£Àžå°‘%ßPw¢:rq¾ apº;Ý®Ö?èO_¬Þ?Å^ÔAÁ¤lâ-›Ÿ—Ø9 ÖvÁ»#ÓaIÙbäÇëîoý äÈšu÷¤Wƒr^ûàÖݧüA²Žôº{‰|"­‰™!*⺻ÛëA;ÖëO°"8^°á}Lѯ»S–‰ëîc¼h]4iÝ}Þc…µ„ÕÄŠ³Þzhè>=}°”ç/wGv<)3|o¼A“èèÈÓj×fr@4‘.q÷CŽ;öX}AëÁÙ}ú”½ó°?Áílb»ô`Mš¥Á’b׈§0Ö Þ$EÍã¯ØB€zÏÖâ0gFçÙä5ÄϲǬ‰½Oª‚÷!ëú§çУuü¸®xÙ»€HWÿ-Έ€n…34¥!Þg¯3$JYìCÊG[s€V¹àg ‚¹joœ'г—ç‰=È‹¿¥åmÝ`¿ƒìpÁ ÉÏGE¼Ö;Kav×HÑäR~ „5÷`7A£ –àp#{œ3QŠ¡]ÏÜm"‡“:)Ž–²UÐØé ³h•¢¡c"²\Ž0”nŠ,ƃXŽ,nÁ;‹‰QýÏ•˜ä#hKÖ¤ø3‘î²1˜ä‹þˆåÆ©²™ÿ‚DE¦Yp˜Tö7ÿÈG⣇ü/A5/µŸCt‘OsÝý“í"O»}\óèúYÊ€Úã­°”±™4n€¥Š×ÂR÷¹€‡èE¥¦|{ ™"‘³|¢XN.™¦‚‹ºþP¼xþø¯—®Ê‰\]ݼ›–å­Àz%Þ’ÌðDtCc~£üE uÁ AQ™1‚7SOX&I‚®f¦›Hˆ êüÞLg… «/+²|ºßªÇ}°8È®Ë,>Ò·ÂeÖàÁpY±±á²&1Eu$2mJ#¾z˜‡Ù.ˆÌ}&?S1‡4 Õ-pSñ°ä¦ïŽÙ‹ru–\‰kDC"2N¶Hç™Ë&,9¼ Ö‘í—䨇X»#ÓÇ_rÝHÞ;±¬áƒºŸÁq˜Ž×àô†ëìzl²ðÌ­0E2ƒ¹¦ml˜BdI“=_s5°Îê±Ìù#‹õG=øDÛLÉa¦"âbÝý¡çqÂ7²²ü7ûr¾YmÚ«3€+]¼ø–ô3D‘÷VÛöj´ìéY¯vO‡àÒV7É,ÁebËaI\Ç–%‘jûáƒÍª8uSÌ7FŒd¾ÿãuã¢Lî(ñjLþ5<ìÐ3Ɔ?Û QôW‡HÁßûæÌs‹"æÙ88Âú}÷o»½à¹«½Ü{Ê~RÖ‰ýÓôÃý´ëOü@ÉÃa# ‚ÏO¬y"^ý¢?AÊPR¶Q«µ_6Àí'¯ø§d)»¯,îj'v(JØP}ê\-mDðÒ_ù© pïÁÎEŽz«â:WqѵA‚Ç1)Ïþ§]þZÔêŒ³Š¿[Å ÆÓnè^­4Ši#CͲ rr,ó»E>»Ç1®á8VÓ dù oTÁËF ™•)€Ž5f6 Wd/öCÌ ÛDj(4 ª¦ëþƒ}€¿/æ’¹”ž¯6GÙ¹i•È›+ö(dwØCéiæI ~;žšüHü?êO,b&‚ßâ Å4•Ȭ,ä·{p9½þɼö‹˜›AŠ?í‰#ÅB<üä°6!½4^ {ƒ£Zÿ¬Æ Š8™³l’ox.L_ª<“h“;ß HµÊý×( Â÷6ßäx†ÿx~äš“’í¢»‘:Ž›òœ=Px*4P*évùÃ,ƒÉÆ& ’K!Ó¾æëMçKe`ä+>G …GÿOܹKàGx$ Æo]u"‡–]åfF†“)Õ4§]I/‹”’ò”6.šRßù&ï`RÌDãXÑŽ¿è1¡–&$ñö¬E!ô">2¿xî~X°1“æ8媑ª •°2ËBæff9°M«­@^ŽYtôƒósT™åçè•B ˜ø>y3SÀ/fQòÍ;óãJ7àq Mã‘u‹"È­8–’ Õ™(ôlU´YF_…®ØCÎïù`ÝšÁËFP‚ÌÊQ·fm"5”šs¤/d,GÑ1öl%%òU£(µ gbýnkŒˬQæ’_rŸ–»Á_q›xƸSŸ!BàWÇ›¬ïödïW™ôÀè¶"*j“W1]üR}7×.§täpžþºJo÷Ρctæ™>ÓŠª/Ñ¥Õ³r2õ\œ¢z.E¾˜ÌfÔi‡I¾=kq™Í ÛDª&Už¢o+kô°gÓ()‘ä0‡XéÐkO-ÇoÀ`Þ KEó.“:vBµ“¼&lo]+N9=[Ôì¹Yv˜K«CÐÕ;£|(V©ìPäÛ³ÇÆJÙ6 R5©ò+ X9ÇÊåöl%%òÒ±â)ߣä`ù-HðÜ*]Ô×îùîTâÞÛ_ X#Û]ù\¬Ã èº+”H7²óIewño¾"HXæw‹|¸òUð²Ñ(@feŽ©|dÛ‚H …æO ›ãé{6­’yÁx¢˜'3D~Ž&I-ïíå„öâÉ Y‰ëe ¸7¢‘Ú;äÕ0äu§·)±n¹+{(ª4zÎÁ[ÚÕL²çtÎYgÝ8y·È—pWE-;$ùö¬Å1ÜUA¶ ˆTMª<ÅÚV–X;hϦQR"_9Öt3EÖ_· g>Yþò9‡ÚïøjË*4D(n†¡ˆ!ði@;2#Ÿ/D„€Âxé€k1üwïôã(?à«¿¯OÏ7Q¬þ ‹×m¦endstream endobj 16 0 obj 2292 endobj 22 0 obj <> stream xœÝUMkA½÷qÿ€uœ L[]ýqTð–8˜CHPb6Á¸ÂÆÿVõn$»…Ä: 3®×Uݯ^3kÀ˜ÐÇ-8_…çG.¿†u j (°ê(;ü¤ìá ¼ Çð%ôL­5M–ëæò{ QDÈh¯›‹°<‡¡W…£×[`Üu¨‘ýéwñù ^̶¯RcË0/{!«#©À”`B ’ý ¤% Ì«p2À«qÂX )ëFŒ"$Y8õ@a"M\SŠØ•]ÒÂpSÕRPçpf¦ÞŽGªÅ9³qrjØ8'йj3Ê{›¦\­î±/­Ü—.GÉ5i§ó›ðr6ø06ਠ«© ¶ÝG†‡™Z4kýLNÙÌP)rx6Ο£ù¯ŠQw^MQ(öD'ã”bºmð;—_2rím±¥¤Ð¶%V×g~öf*å½À…„Y¶ Ûr–‡ÔHœy)Q®Þk³OiTy7åÇûÒß \{ÀÒ¨»f1š1"rzäÅøm“[¿:ÿ­nOä7•䟿s“Túïh¯ØaøPPÇendstream endobj 23 0 obj 427 endobj 27 0 obj <> stream xœíXYoGVò¸<‚„š+Ù!Ù¡»ºúR‘‘(¼ëlqج „8äÿK©ª¹ª×kcsEŠâ•¬žÙêîªú¾ºö±­cù3,žÎnÜKæÅß³73€b‚Es(«ÈË?g˜6ÖÝòåluÝü5““J)ÁÑYo_ŒG`°ÖDKÿÞîÏ®ÏîÎäVsï—~A²of¹õü'/ôúù¡¹µ$½ŠÁÜ–h–r݃.™…3 kœ…Ðx°mÎfy8{47¹YØ6"‚Os³jhí277÷›…o!§ˆssÐ, uŠŸ›¼ÁgëJœ›µÚ°j®Í¤ÌÍ~\iƒ‡ÇË;³ÛK²Åþ-Ýâ#Øb[0Þå6ãg6å}`!½}Ù°â`Kò HJmŠlËü‹fùÇä6Ò±¤‚eðÞøü1n†ÖƒI‘|Y_ü~Ü{§Îbo$ö”\NÎÏ6†¹ÙmHqáxýûßåÈ ­ºu¶ž1šÖϺuÈ´>Çk,>ŽÔL7CÙ¸aèüBìxÂ, /zuß”×_ô·EWz •™Z|f¬ÑÁp·ÇÒ41(‹±VVËüΛ‰¥!B-e#Ð{ì¼dY›MòØúª û0¶œ ¡ÅYß±å? ‚gM“á6ó$ô Ã6DC¯ñ/9Äèq@’œ¹\3¨É#ÉwžZqê¤óææµxÆç„ôpɼä/B¶]~¥Ô™sæ OØ!gɯd]ÞÁùØ[[Ø1®õT£°Da|ô!ü>z›†ämÈ²ÓÆcªßï³n–X³dïàBA¼Ú¹+ß„Á7X¸z8ª2*ý‰odël#6€‰T7 ipÈð>ëÁù„¹þæí€hSòÝä?ðÌjèZç…äÿsb>ÐV2É“I [zŽ-u#yævõð!‡Ú 棚$âŠË§9ãg¶àñŠ\dñd3@e|`ˆºÀÀ–AwÃÖ(ÑwòÆd7V¼1fË"k0 s—dbA \Ýû' ‘ÃR½÷u]ÉÌ#uYmiÛf¡Ž·\ZÄ3Å[ÏÞãà‘¸Þ¡œÌS(î5B«’ë ë‹’ÈZ|-BG’ÉÌBBë*œ¦ðÓï«°W¡{C?ì±×S*¾ôhF»*ºw¤s)z 긭ŸÔñ+¥Ïà[¦|€ºS¡ŽŽó ÉpçÇã†$4É„À4HGA] -Ä0ÆÛB7Š™Üw–#¤Úø2˦˜°F®æ@ý¤ý¸Þ–Fµ€Ô ` •ÈO£ó!CuÔÜŽQGqdVåè¤ÄI‡R8}âìC¸&Ãë‚r½Vâ>-‘F¤¾ê†’3 ”ÕÑW ßâ×%KÁþ‘Ó†“ä6ï$s¤¿ãȉ”‰ææ7îÛáćYh±«nyJ")øÒ1<[± êHˆ~ EHZ¶‘sßR•Š:÷Lï·'V¡ ÇâûÝúTÆ R¶g"•?ËÉíÁ¶$&ž!Ü2XEŽˆ‡rÁœ%ÔÇ\¨óew`ƒß±l¦tQ:¨$†c.©òš:e¯ã%QÿÈ1cÎÕ h…{| ç/Ó09Ca>Å‘`¦ñ [m—=—· /¢¥Ìœh~$ŽS†æ ~me)ݸ+™;×…¯n§so:Xn1w8?'0I‡ÀƒŠ)¯è ç³%÷®”¢ÝŒ)É€P“S룉Zé°¯9©é³ROõ¼´@¬g_ˆ¸¯ÃÑ®Ýæ‘ te¯Ø¯Ù¡7£ƒ×+‘ïå·€‘MÑ-ʅƇ@åÓÍÿÔ*òoKÿÂüONãéà3Ìÿ½ÕüÿPÍNÕÐ' -äÀÕBz¦’žƒ{š²}‹á0éIM =•a ÔàwdªôEòp5_|†¹»IÏÝÿ)Ï}ÔyKló™ÚoÝp?ã” ‰;¦º›æàµêÚôœú-Wó\BªÚðË<RóMÐÓWº%ÛZ§Wõˆ—ÌiÖläþæ&;Ós? už½OM®«;ö©½Ö}箴ľä i=‚Û€Ÿ—N‚,bú\åòMy)–çæk2ñrõæ²|ÔîçRF•û0L1ž©4^> stream xœíYÝoE <î# ÊT¼ÜVºí|´ABâ­í‘´©Ú&4É…^ƒÔòÿKس_öæ&\ÈÝ)B$R4»ñØcç÷³g? Ù(-$þöƒw—ÕÃgA,ÿª>VZ'ᤗyäqø¡²a2n‡Õáñg•5¥”œ]Ÿ–ƒ ë¤^ŸOgÕùƒêi•­Šg¿týXÅÆàO~AÇï.Å“¬Ka¼Xœg3`Å*#æJÌ¥¡Qÿz+—Õì³zñ¾úyv”€¥4&…dšcÏ·µêš Eðžý7Î¥ë}SÒvνš‰£Z5R'ífâU=—×ÁJ3/ñAj•¢óLê‹z®›B 3ñ[=×>ª™8Á±‘^Ú™8®a®v…d-üÎÄ•†` ŒOQ±N%>>¬ç°¼(Q¾¤gU»‘Wæõâ×míM9hyk”4íÞü·b¦ú`©3X[&×…IIßÈ(”Ò]Ž9Œqm'hݸ`§Ä·žàLòƒ‹F9ˆÏ|PÖãø}tÚ&;DÚ»3¾Á$ktäBGõÜAnK§0@à®6>ø>@!oÆs0lu9È:Mp÷H5ÑD ûxF×CÄ_€F£L5:¦´Ãp’¸Ì{?ç:6}ý§@ðuŽãl/ó~=÷à¹×3ñ(‚ã:Æ@µôœœT9ñ–ü„XêÕ9XÁkûd­›è?Fý^*Ôù’j]ˆ¯è?JÛBÍÑeœˆ3Ø€Ô8£†|Þ.(Ëh÷NÉÆîœ:çnÍvÕ(?™ ‡¸çù¤¢È›Úàé š¾\ë+%ϳ’ý£;îÇ{åvgnËw2bÛc ,oÀGäD€Ç˜b:Á‘Qú8û—òÛƒ5Â>[ÏCã¼+__¥ç\†£ŒN ‚[ViîW‚—¥òŒðJ†‰¯GЇ‚¤ðÖƒÊAB+åAã—0Q{e"HÛzœ5_‘÷­Â Õ<=F/xµ.z¡hå Ê*‹UÆ ™¹o H)xÿC™êqƹÖ|YúØ’nöÉzú3£?VšW Ë+üž¥‰Gz|•Õ %õK‘qK5›ð’ŒÁ8Έd6õôEeÇ-½ÖÝÖ9r0=`­ÍFòz1­?Ú×g$§ÛÕÖY–Q´6êÁžxü†:5°¹4­ú}2k÷7‹?hOƒM‘d)]Ö{ÔŒµ<ÓDdUc*:'+Ç䢯©Â³¾Ϲ¼£‚â…d¼ï‚CƒÁÝwžs¬à ¼D»™‘ëX/sVk„p£Ù¦’‡êcx22ðqí’s»‚¡,-îŒÇÛ#|#‘X7'ü ›–« €8¢6­ xû 袶ºØçq $^âk jö4Óv'³O´\Œ".ÁM*^î5h½R ]ê@¯”jÛPFã:þŽJœ”¡«%1Æ[V•º FÓ s˦L$ÆèFš[÷¸ DИ˗!Ñ™x ëvÞ#x$`5Åi­sG™¤yÆÐë„´”†'@)m*°ë®Ò„¶öòû½¸s”ï¼c(1BÑs§W›˜¶u"ÀµÄ“¯dÒÓf‰Þ¦™¶º8~’ ©¨ó‰xœk…þºMÝ!êw¡¦°gC°=ó¹ ÐÓ;”ô–_ì‘ ›²À[Ä4mc å‹¢Õ&|±»OQÀälÓ©›u—ÛºFl ð`cœÀ/À×ö—Å.¼äcîFUÈ5¬ÃE4tóκ{6eº¥§ë[“U1ä…¶†‡ƒDhocþ¶o´·!wÓ»FûÎ;†ö“[­¡R-¢]OÝQMêY qýžÁS±Xæ—nË\:ËÄ;@.sHÖÔ[³×ݰ1{äÛMYª³¨CJäÎZº, DôÿîÜ|w¶Ç‘N¦üùuSŽìÉ\êrQKÑì^Ô%àx†í?a$ ˆŽßMRL\‘VæÒkùÚ”¨ùŽ5TëYqÝ\~¦㓟¨§Ç^І`ÅÐÒºb[®‚+Op )æ¾ï¼NÐOYn« åäšêª“&ÆÄÒv¸ “eô‰îi;¯Ø™‰@ú Ó‚~}‹णž Wbš¯»<ðvŒ.°»¶ XjÒ¯"ÇSY<’¢6ð:¯RÔ*B6$;œ•§Õß¹²]endstream endobj 33 0 obj 1642 endobj 37 0 obj <> stream xœå}[¯nËq•@ˆý€À¼%/ ÁÃÞgÑÝÕWÞ@¤H<$>ع“‹}bŸH1ùûTQÝÕß>ßv8ö:Û‰°%«|ùW±i:5–†ïÝùÿoñ³éµÏ—Ñ_?þá_Îô~ñsªaw?zŒ"Ó·'çÆÝÉ:¤5ûÃã?ü ¯µMYï_þ'ö¬Î¶.}ɦò:“˜ÜÜ¿cz“Z¥=ôø§¦69Ù¾ÿþ‡6ìGåÁl~µøÅË‚ ÐýǸ0ßOTKúx-d¯Åœ¯êr.®ÿÔ´ÑÖï¼SÊkbïËÛZýçåUa¤êt~ãC~µùª½þ;¥¥©ûÍxú}k»z[?]œb³UCW ½k›ÿõá‹þ:Ôÿd³zo+¶È׫XäÚgWùÏlyZé¥àÍô:º4íåÏ a$+½ù¬xÕèñ¯> [ûúñ®‘ýµ¯xùîP§H2ƒü5Ò ÝäÏ€;>ÅOBÏLnjLÅöMÖ•ÍP´Ç6Ìýøjñh„U›`“ïNLWJKãØã2GsØÝÉscû‡MÜ£OAÓ?ú…{Sì*m¼Öo]_vqÃØ\0æ®þE׋?ºÅ‹=®Ï{¶_5ñiSþvKrM|#è\ïeºgs ð^’k¥¾|¾’ºNu©ò<Ìü^›o:„ÕψéÜž/ðí'8lsÇï_~ø|áÍ_×ÞÈIm¾³¶Çßùƒ­Uµ„ñݹ)íU~-®Aô„öƒOðÁ1üžnÎP;¬¡×dµñÜå#’õe<†Â¦©zg[¸×MÏþX5l¦òq'ŽþÙ…{·ü'#?“°¸áÿåò¼)Ì«q›Küå`þÈt¡ÍR¿gÈT›ô_À¯æŸÀË[vju=¢Ó…1ŸäÅÖ–(IÎì×ÿGÔÿá5®ÀãOxÁ›f?ñžÒµ0wï×\Ëûã½–â÷ÏHz¸ü«_(ŠÌ\˯¸œß –W]{ÕÏå5Ï×õYx¾ÏñÎoHq(}4ÛÒôšgêÜùò¯úh†#Æc/H» ð÷?ˆ GògBc_Ë?÷¿£MSÈ>SâÈsnÕdÑÏÞ©ÿŸhsé´ëç]øRžø†ù-ŸTÝÔê»Gp~G[‹üÚôVj±TÅ/?Ê7_=¥Ðó0.‚ñ¿–nµìÿøÁ„ôe®œëC î&AÿÖæÑÛZ]î^èHB¾ã‡ÎìççÚœ&¾¿Aåê"¼)‘«S¹+&ws¶‹‘üô9ÁøÄÙó›Äër¶#]‡4'èÉP›L€<=‹•ºŸ^”äìMq®î>ÁïæŸ|¨¯R‡¹‹Ÿ|hyÙv~wD¦SÓ_i%öÃ)~{óÄÄ>:ŽÝ®ý–lùÏ71ýLHäKü­éÍg𸛢OkfÕß }þþsÌ'âdÏ-þÊ–ÄIçg¶ KÖ”ö1ôÜ)ß´ÓN*5Æ[¦/_ÝÝONßÈoÜýüûxùê翼ëÅÆkM¹÷s0ÃHÆÓ=McRS]´Êå/²Š®·Œ—Õ¼lA­»½ü]˜ô³f?àÓšŒ·Þ/?{¼ç<ù´>4~|¼eIOz~|¼KÏ?>ÞutÏ?>>W6æÇÇ»q[éYÏwã>Û³Æwã1Ÿöüøx7žãé¦<>ÞÕÇ=›àãcoÜR—'=ôx7Îm>küøx7.íÙ¦|ôx7VWû¬ñyü0fÝØgïÆMžmÊGwãþ±å<{¼§–òÑãÝxæg›òÑãÝxå§›òøXœÔŒ­²()öžR¦§Ïð-ÉJ®¼ÓNŸ•WéªP4æÑ~q¢Ñ—¿§pYºÚé|ÿ/>ÔµìÝ¿¶lÀµú€³z‘{&ßøÃ›O¥g¸-Lå7?dÃ]õÆôó˜Ê?3°WÖ-xÿ¯ãñ÷”Ð÷×|Ǹ>1ÃÕŸoÕ³ço8¿öæ[e~ºUOÿðæSùî·ª”çVõôùÎo¾õVaÀ϶êùÞ|*Ÿa«joO·êÙ󷛟dìÑ[n• øéV=ýÛOå3lÕÈÏ·êÙó7œ_}ó­²?ݪ§xó©|†­ZŸ°ªgÏßp~ãÍ·j}ʪžþáͧòÝo•(y¶UOŸ¿Ýüj’×ù–;…ñ>Û©çxë™|†ªãùF={þ†Ó“·Þ(ïÓzú‡·žÉgبñ ‹zöü §×ßz£Æ§,êéÞz&Ÿa£Ö˜O7êÙó7œÞBû-wÊüt§žþáͧòÝoU-åéV=}þvókå­· ~¶UÏÿðæSy›­âoÍû‡öE=c÷jAH»»Ÿù—pŸ}VíΤVÊû…üÂRµlEü½ÓâþÆ¢×"MÖû¢Žt·ø7öžNKðƒ¬Mt‹òlµé¾¨ND²¢õ¥kÜäåk•ámtI~f²\r79S!Oœx'å|ÉÕäFÙÚW¶_ÖF±­$ÊÖ¦ˆÉ=YŸ¹S¶1¤IÙÆe³,ôÙ³°§ËJ@e¸¬}Jgûb®Nõ²µ—AYÇ ¥Q¶ö¹PÖþuù!#š>ø\´]QÊÓ¢ì|·¢nÁe£êjÈ…í«Åû—Ë:¯Ô]îÆ—\ÖßM‰ïê–(Vqœm¨Ü8„¶|ü ¶Ç1"s »!@â:èrÈ\|wT•§ËÚfvÎqZ_+†LîoŸÖ¦²O+ʘÂßZÖ¦p_t™TÆú Ý/ÝŠAy˜ÌçÙúç>Žlcà]î”í·’P¶wqß4‚wÙOi—\/y†lI˜ÝªÚéGVünµq&Ž³â·°£YŸ™íí’é,™²õ_ØfظþëV]¶ßjœ#Öj$Êö|blŠy¶²í×Ćkù/ÊÝäLû¸.yœ6sÉéÇ÷t,›K1ê!ÇF½µTˆŽŸò°u(>_›ín´y­IŠ5¬XO®‰ê¶ ÚÈÐåP™sWÈÞ[€½_[Îë’[¼û cß9¯œâwÓµ§˜»ãò1»þ/ì#u{Ù»n#X¡-O{·º-˜n8>`¹ÝŽ ÓǦæ˜ÇÖæœaƒŽKm½ëÍöÑmßôj÷ºš¼3ìÝìzôÀ„TžlLе­–8‚ÜC ‹6¶@vÌÑ×1ØÓ»JL3<,\sâ¤_í±Š÷¯j¡˜ÉqfÃLê|צUç’¬½ã‰aµnäÁpUÖƒíÂß"æ§ËÐîè# ýÂ4ÿ_e .pïÚ4?ElÃü÷‘~ŠxE?µe 4.ß4à³ú7äÞÁþ{¿™8Üúÿ¸}¨ÝW«2-PÕT:wtÛ,µ˜–Aë¿òwÌA|íþ²oiÏeµî¾îϹgÕž—±u®öâz¬óìn¶vÝñ.!»zì­¶é6©ï¶á˜hm¨g£Y›æØ7¬ì‹6ikÝ/lŸÚÆ&¤zñÜLP—ޏVLîÄ>¤ƒ¹î³bÙÆ|^uìkæƒÛe’Á>‡é õ{ŽÐEƒ=ÕWb¢­¡ëñ4ÎQ| Ë*›Ëú»…kK9»léõ岎9—Í–èmãTE‰Ë¶G™¾Á6Z‡_(뺥‰1œ«z7ÊÚO¢>­l>ž¾Äà¡&úÈ%¦)ô 8§˜l¾jq ¨6uʆÂ1Ð÷³„>èÿ°ž¾Íuðk…ŸPf ¿Õ!þÎJþUØFdû¿’à;áïKjeûŒ‚2L`GIà‰ïÎã?Jcl%ÃbÝT^!çm`1|7ËÜþ»ä:Oÿ¹ÿ]TYm l€=*¾„ã/ÅÚCŸKÁ\*eôÓØ¦ÛsìÊX7ößN¦GÿÊð|s„­©\‰es2•ûö÷*Û>BŠØš9Ÿ6\¡ð­µ¾)ÛV‰1Ìcl?7ؾ¿sœ}lþ§²½Û*×§oþWŠ€7Po+Üðê@3ö¢]ûX×öߺkóî/ý=÷¼ú@~ úsÉ#Ú§}¦º‘ð[®‡à|™z]â˜S‰¹$p‹J}¶5ŸÄôþÔÝŽl¾Äɘ´5[7÷ý‹¾Ÿ¶ï6^Mèw¶Ë¢¿4Óݾßp@m_ˆ‡Fç+ø[æ_ñÓÌ{Ÿ€EÉù%0*åÀ±’fúH£-Óf¬ƒìh¾vc#dq\•ÀR;ïžO Vj¡±êÅä¸MCÃïºß1Ùýøû1¾«¾‰Ï‡ù2úǡ۩¾Ì}œù/bé°³\cŸvÆPŸè¼Ó|¢Ÿ-—µñ3§­IwÎgsìÎAFkßgB+Râúãli¤ >Ý|ô>÷šæxÚÄsò’>/¹Ð,ÁÒŹÈΰ¹„ñR†£Å׊Á?Š@ëá¥43„¯É­Zr¾¬êsdå€*snº*«2¹¡Ž£¹ë:‡–’ónëŸíºÉç|V—süjÑÙåçÝûEž8Ëêr½4®ºx¦17UW«G_}ùÊÈpÕmoÕÏIæ +qÙ>Vd¥]‰ö¯ýÏF¹Yy·û6Ó•í·loƬ›ê >šÕ!}c\Äšl|³Ïã{jw_bü Ó§–´ö~+†BWÜ'Éæj¥€“9Ž tÑ}••ßwÇeð3âuCY>ûoö&úfógj|·ßZüÝ~æ~Îøìös'®Ÿçq—½OÓõÊõ,Û¯XL¥©4¿5Gq?dØÑÝw®ÍÃà Q­?wÎ.ÅB157×\“¹n,…äg&ú`£‡5›ŠØ\’°ãˆÉû‡?† —Šó4õÁBO_QJ07¯Òe®;¦Rê€õàwUÌ }õjs©†X}gƒï¡_iàBgÇÂöˆÇp­ÚIéSÞüLéÓ‰IŠS˜$øãÄ6àÜXUóï*ƒ«5ÊsûxIíp5ç WS9oN¶åÉþm.‹ã·žìœ{° t‰ül­àg+âse÷øš´~øëª%Öð⸠{DÛ™àý8w•‰>©{“±î)tÆ÷4ËÙkßw¹ô!SONˆúãü zå|úæq‘-×m"†¤úƒû‘ã’7z||’¼ÉW,VÔÉE,´ªºH,°8'c~¥’/:îÌÃͤjÎEŒ:~Ùo5çà…ü] ÷éûä"ˆ»’3uT¯’3uÄíÈkOÎù,éäq8¾kã‘\Cv¼6,+´IÓ!É;?l ²Æ}­óexWÆá"ëpA‡&OÅgYCHE¼{'ô©ðÒ0NØ£4pìµÐgï¤!†‡½žOKÀè§…¼¶ñù8q,aŽ8+ˆ‘¹BrDæ¿êŽÇáˆ#¸¾ Çç‘:@ž7ŒsÐ÷ƒÎ¥ ƒ#b¾“ñ¼A9é²÷i¨ÆµŒRÆy€Ü‘y:rÇøœñ9rÇø¸£0¯Ê¹÷uâšÂœ¯¯8lD:Ï*Âu>ÜQ׿mî(¹B>Çþ>/‡;êþæ÷úÀ¢ëÉÉùný‘K¯òÑ7ò?ê!y!õ“˜L½%ÔÓ&ÚS·#·¨z~x'õßûÉíÄžÕ^N.Xí¨Ûa|±¯ƒ[S?õ\¼“ç^Äà/ô3 ãÄ íyæ4Ú¨ûÛ‰¦~^¹‚˜c´TùsÁô5æ~†XÝ‘‘Àa?[ŸÉ§ÍEÕ¸ q¾:æ¿lî#¬=ÏiÈY1^XÛ~ÁdÉÞ>íÚŒ‚\§töiu@ÂX ò’Œ ™¨Ï¢ï›ÈÁñì\Þpß—øUj“-çñ‘†œ±ºpJòuðÎé<qçëkçÜÔG×Ó±»“µ÷àÐÎÁM»Ç¹Í ÏKTðHò„d|€c0Ê_GsŽa1&Æ —ñì1Ú»ŒAZÈ¡zþÝàµîz†|fÛxžÚdÖi#çaëð"Äy.b¬Ñ¹Ÿ{Ìrs-ð3 “i±ò³¹k9 á€@›ŸÙКùί)wçƒ ÝãÛv|ëÉókúgÝ[Ï'6•=þ—­=k ~ô<Ș°v¡ü…}ŠUtyÿ¸/ú" ÅôÌ|™Å*zö˜¶ªjÏÕùµµgLØ ³gçÚÝÚ0ïkGòž›ïµa¾`¹ämƈw‡Õpz6X·ôüî´±1WmÐÞ3c¿–f8ãW¬é9¥ÎÝcû:ž4N^²'ŽÙBº¶Ì)¨o;·ˆ½ðó‡v×sÊ'hô7ÈW¶Ù]ç´½ëq—6˜kX m0§°ôgT/<—¡¼¾sîFZgMƒ¥CZkl#¸ÔNnK¦Íeí§&Ïj?â¹…„–=?"v^ñ3“Åí×pÙô²ûYÊì‡ùbœ«ºç^aÛÌ#¬Š3(ÇY£Fm¿®“o­â¹àŸŒOϙݞVCn…s´‹{äyá|¿sFˆSzÞÖÎ÷Œ©/øžÛ¬ª¹lüÀûA®¯É<±®¼æá‹*ÿä9_wX]^ðs¾þrÔQ-äõ<¼Öõü´)éÊá>ÊS´r¶“ÿMWž._9â+ÎD¾›ýÜ Žè1ûubWŒ¥íÜ4ø1eøižg·s>Íœà¼rÖË1WN^5µvò­ ¹õ-GŒ'¡öÎÇŒœ#}$Þe –÷ï¹uðZ€»;Öƒo‰Ë‡ûw¾RNL+E=\I¬™Ëg =N†µÝ¹ÑTO\ 1Q¯µÂžYN­tÃ} uÆmvFÍõ*Kè[ñçisô¼À¿ÝÆÛwM‚û³ujÓÝzÞkè¿÷¶]4ÄÃÒ±µmGfƒÙû1ÛÜv.âöH.ö¾í·Ö°kð·÷š O6>Xnã†Å6žˆÎàÓŽ?¸ì¿1*vnL3lÜXg˜YÛÉl,%0Óðvc©áp÷51|ÞØ«æÝÆÎ‰‰aµcx ×¥i^ëiT´M_5×ã ,–³v~yšïpÙ|qñ\³ù ®|SžS6ÿîù\ø2¯Y„¿ów‡ùMæ=-¬Úw] e÷•ðËžG®á+GÍ6.§«ýÕ¦Ïx®Ø{úOwŸÛSŒ¡Y{¯Bu¸ÇÞÀœ«€KxüHì¹×kŠõéùwûçSö:[·]•Ïplà3žÏæÇ}îà<›ÕàK¶§ks>ûvçÜm·œ°_^':vûŸÒdV |½‘Œ‘¢O‚7¾"Ÿu®Ù[åÃÕ¯r®V$¸âÒÎÕT>\­Hp5•WC.ϹbÝÎu”_®¦òÜ\­èÁsïŸÊcïqîŸçþêæ[È:+ýð¸ù™Ëíj3ãÝ™6WCîüî»ÞŽs¿Dma…ž£îÍõ¿×° ð6¯£os;²rãsóÄAÄî.σ§¸7ê8‹;8­zýÍ9CÞÙ©.·ƒãˆÛ;¾[ÝÛÆý¶‚·YmÁöˆù»ÿ@ÎÚyîʸ¿A|Ûy[/áŸP'·< àmž³N‡·YÎb×éã®ÖÜ9ëà+¸ó5=ßÁ»žC©‡·¹ÌçóªÕCíšÏ…uxž‹©Ç—óÎÚ®Õ‹»Ìià|׸2.Þæ÷(™*W-—Ä8Gm‡· ı˜Óá}@ž»æB;¸7×<'Ž<óæqϱðN"mÁeæÄS;ü̆µy› kçÇ™[ßõ…ùp8Üt‡w½‹ýO;|ngsŽ#øÜ@^O®:¶âõŽóìî3îµ*±G#_uoY¢î-«î-GMÛºêÞæ¼ä¨“ÃÝÛ-KFÄM;Æì5 Ð·-—3~ꧬÐÛúìµkÌóz]ëÕ¼Þcn×Kœmkó<#êÖÛá®ß¶µ$‡ÛáîšÛ&jKÜfÛ ng^ëØ¸Õ¥mÛ+0÷øˆ¸»ç2‚Û±Þ1§ÇÒøýÁ(ã »Ú LÓ¥9X§ÓÞÜÎxÿÁFÃÒº±T K{W`¬º“æuf Ÿ´lžg4|v_`\й•¶mngçƒùv¶wn×"&‡ó[ÛµÈð/ÎíVp»:OLÎý”ó]ø/¯oîÁíèïÒáåwDÜW:¼åzbrÉ'Þþ}ü¬´ðïrùz žg1ˆ#çïføqç²óp;ÄGv ±ç Œ)z~Öäìõ”ÉÉø…¯­²5aÞÙ®ngcs;p!çv%]ÜÎÚçmoÅ퀦‹ÿöòÌÁíPUDýÊjÊævbWý·Å®9·¾s;IØoÖymñ˜œÊuÇäì›ßHÇ÷:h佬uqy ôý[fß?±×|¿Uî›ÛIÆÃ6PoáÜNÌu;Ÿ» vd芷‘ïR·Xçþî¿K™5OÐEp;1ÛãÏä¯|×8"cr*Ëæv*÷“»zàÜN2ן}öº¹Ê‡ÛIis;±Rçv*×mçbwDœÛ‰]»rn§TùS•OþTJ;ùS)íÄä ^µåsÇBå“?•ÒÖõüjÓÓ'd‰w{Ôá•~ÕðÅ[•ëæ+Â;ž¼£Pæ¹+ežü©”uîö ïr±Cp\p¬î5Xis;äjëXÜ;Ôiymœ þÝåurRŸaäUk8Sôß}ÔñùPÇqäqß‘uÜGnÔý倶Ÿ°¸éöøb©ó6ÔÕº¿©v¦wÞf×·bž”~ yR÷gÈ“:oC>Ëýó\ô‹Ì“ú"7Ê}aÞ“ï2×IÞÖ'ß”ƒŸµw ZÎ×ó« clåmX›åïçhåÜ¥•Æ;¶ü]Æ–X;ú}ò6ÞI$&6ðƒ]wî*z >÷—9Sò6Ïi²OÜí Þz”ý÷àm­ok=x›ÕPoÞæ2«okønÈLÑ÷ñok-x[kÁÛ8—}ÿ#ÞÖ›dŸñý—½Vù¬¡ó­–Ï7AìŸ:¼­1>×Ïž¹E{ÖÉ]wü·RìWåÝêäŠ;+ÔCú6æ[ÝÆ‘•qýœz;Óám¬p=G¾ÕõŸ÷ÜvÒám¬C˜aƒÎÛX·àv×sأ޶š½oû5Øvmø°í½•ÀÓunÔx‚vÇËÉnü©5pÉðmã•áÞÁ±ø&3pON.•¸ºqRjà§”ÀUÃdçm•ñ¶~0Üyò°ÎÛjäX¹WçmȽ:o«¹…_È'—ª¾fÞVÓÉ¥ª|r©*Ÿ\*ý—óúµ}_êäRÝ'’·É Îä2ß'—ê¾ÕýæŒ8‹·q¹\íËåÓW²qÍê‘7ÞÌo4yc%ê—CQ×ìØŠ;޹üÇ|Ýp¯¾ºØûŽé¸OàX»åîÖß`w ¶Ï°»›«á³Øîc¾™âk2Ã'­¾Ê®•o¶p/ÒûYáóx‡€¾w°n5¥óíÍÊûx·ò~¸Z½î|$ŸÚ5|ÖÅùVåw4öó»Í¸ä~É+ÞïžTÞ9HCñ»rî0âÓ2ÎÕ*¿ñ[åp5,ŸsµÊûÕåµ¹>ëé\­&r)Ž‹ý“{±ý8µn5vÉcsµ-ó·ÆùN¬Ê²¹š÷ßÙyÞÆõ_¬ü]Ä/¥Æ\ ×¼-óÝz¯I5”s—“kHŽÅµ%÷ª¸ÙO®VqçƒüŒ{ºå´¢}šÑOÜ)Ùr ½ÊúFžD=¤?ó»Õ®«^3bÃÔswF,ßÙú?"ö¼pÎ!fÂÖ¶á\äýôˆmÃf·=öˆ…ÃÞ·ý¶ˆ¯ø.¦Êk†l|h›Ç½¥'5bù¸±ñ§F옶1ªF®ÀîIL«‘[°{›«á>ÖÆF‰ðvc©ÈájÀç½VKç\ ÷!œ«áÄÆpð6rµUÒáj¸÷°}A®‡«-æà(³fmR?\m¡Þš\ >Ë9 }çBÇwgäñ™ÇÍ“\æ»äg¼ß=ƒcÍ‹Ÿy—åj·Éñ|DŽËïFp #â|¼÷À5äýrµyq5r rµyqµ‰üXr9¸î48W›W›W›W›W›W›W›W›W›W›7W«üª¸.2;W« '}çjÀAçjý·}>¦Ê\g%!~؈S6WƒÉ:W« ÷Y±zà~q)öÏh¡Œ(¸ZµoÃ8WÃç}ÿð©fßãê1U¶‘÷ªç:ÙOÔºáÓ!—hS[¼ËÜü¢¼âwë‰ÍÖÌû,r‹ñ£v’8n±kçj5#¿<\›«áš¸sµŠ\'¹ZEüœ\­"®Nû¬ˆ½“«UÄäÉÕ*bõ´óŠx>¹ZEœŸ\­"§I¼À•tçjøì‡ãKEN“¸S-wéx„ÏY:W«–§püRùÔ|àj»ã>+â8Ù±²"§I ­ü†}9¾-Ḓȧ‹«}Õ1ºâ;Än|ÂÄ1Ÿ×t¬¯ÈoÒT|Lj¾¡"¿IŸÁ ƒ\ ŸEqSíÛÎÕ*¾HŸ„Ï=¹¯ªÈ1чUäž–÷ƒo›ù:ÔS^ ë‡\®‡«1wéï"_I®æùJ¶ïå’ƒ“!§æ|‹ß›ÝÏï6ó¹Ì\'ß§æ]å~|3~µíÔçÂ{‹l¿òŽ«ás¥›«á[»ÎÕqAî/óžäjž—\”ƒ«]_Õ¬žßdû\Íά!Ws™¿U‚«I ®Æ¤¸‘$ð$å gÒ…?'ÞOLsŒÖ9vY¾ucj?뀙ucc¹°1fÎX:ûæj•õ'޽†ÛäjÄsrµjßU9>N>´"J/Ï2_ÐO>Kì\­æ~ò¡ø<±s5ú&r5ú,r÷e>ÆMÜ?ž|hÍõð¤šY#î¾òp,÷§î+e…®9d™Ñ^æõ¼_Ïåê'…Ï-3Æ€ü©¯aI›«9g W!— FÙçþœ«ÕŒz?Sá›˹Šl®V­´Í¹šÊ}sµÊü©Ÿ·g ^d\só%«AZ~¾š§v Ü)ä¾¹Zð.p5T‘8W“\MêÅÕ~çÝÿ‰zKendstream endobj 38 0 obj 11450 endobj 42 0 obj <> stream xœí}Û¯æÉu•@D¿ ˜'r‘o},æP÷Ë ‰%IvÆcˆŒgF!Éæ!ÿ>µ×ZuùúüާÇùúEÄR¼]]¿úê²÷ÚתóË÷äóÿLâoÞ¿ùQ¾ù?o~ù&„þ]zxªùwoRý€&ùí›ÿðáïß`¤Þ{öc¬_}³†HÙ¹‡âÆÿûÕ/Þ|ýÃ7þÆ?ØF¥Ç‡˜Z~¹wujOqüßÃ?¼i6úÿÉ÷ÔJõ­=<'þâ/j6л7X õ 1~ó—;¢á¤ÿæýÃý|lAHí©—‡Ï¿ÆšÆ’’¯Ÿù‡Ï܃wá)¤ÝS Ÿ¿óåÛ‡?zülÌ%¸PòÛ‡üø™Š%Æðö!=º'×]r­¾}(Ÿ…§±i.~ðÿÁ>O!{çß>ü`üKÏ9×öA¯?|ü,>kºítóã_(¾¥>Fú±M¤ÅØÃ¦mRçÇÅ>µ?>~‡‘b*qüÄ×6[ïcŽø‡øZu£Óçø…îúýaý[ ¿œ}~*!ÿÏÏÿäÍÿ|¸Ó<ùì)Œi?øÐÆ´þÿ!|ä!ü&¢0v<†§!Äç)”y ¹=ÕòPëS†(¼ýgŸÿí>ð!!½öÔ繯ÿ}_®O)=Ôòôáÿfòþkô.i‘_KŒ=Ͼ 4–¨7N»áTÇ §^š·³ëõÞŽî«Ç¾Ä.È£=¼$&ÿ¸]ûõKÁ¦yŸ^e%ßWàlj÷áäãœ|‹ã·tÞÿÜÎÛV»>á)×hzìó¯leÿeÌtÈl*o~ïÑ?ÙJ†üþ/›tI©üÜ„%‡œýÅ®&Éú?îC˧ßß_þÀÆë%DÛ±5'ÆÂ¿´ýÈ0„wôù+û_µÄ\±=cGb'œàÛ>æò?°¯¥•¸&PB°OóSµ/ÿÚ=éN{¾¾ØˆãÆ|Ç—;f›kåÃSûß¹Ú¹…O'ÿ!ø'ÿ[‘ÿàûS{ù×oäÿ¯m—‰ïïp@­ÛQ|a¨ÜýøâV:ìøCvU§?,¸Çoæj ãlþ‰©›\jÈ:Ûк÷ƒ~]à æ¿ÐÑÆWíö ÿonÁ]'¤ü”¾æü'[Dï5ÿ·±6ŸRÒúÇ› ãËìã ??{’~Z"?yŒOÍ¥oQçœÊÑåü² û0²“=e,µ½|êœNøü§¶ywÞmrß y¯ÁôÂØÆ½t4c'ƨ¥Üõ‰}š}ñXÇ&cOŽŸÔÑ,ä¿^†ì£ÓO{ð€¶sȳËñe± „ê‡t°÷ä@øsgOúÇ—çð›üklêe_«†c§Î}ýÑõN›ùü`1Ê ëùÁc4Эà ²èÏŸ¾ùü‡_Þôú˱Ÿr,7äí’θý—c ®¥ì¸ýàÌú!‰¿%³>yÿJf½Öx£ÑNô›²/`õ äþ•¹ÎÍ•^_²u~µì=†IÒ ý¿’ílfà—”Sn±ßNåðgôýfSÿJÊMGv*·¢ûxwLCÙ¶[üË1™:ÐÀpè›c’œXüÐS:¶ñÆQú9ôx¬ø2÷øT_+Æ”Ò@Ýrï´¦»š la²}äÔ&×æÈ‹ Aá’odv¦cA³ãÔ _Ø€¾ø—TãéL îе·¾ÒÖ˜éb~ý“‡½ÚoCÿä!‹½¼†þÑ?FÿL ³óº®%_½r$Yûô ÿOþ®âœ£³mþ>âüÓŸš”Än<ÿÍa³ž2uŠü!¸§ˆ>Úú[k—ßY|÷»äÛçÑ ›V Ár¡µuWŒÿ2°%ø‡±ÇõÁG?–:°ƒõòÃ?lž¾ì÷îM¨cÏÔŽÏ/ÛG÷6ì5dzûÑ~tÎDñù製<fŒ«HÁ@ùf-—í£{va6ßÌýª}t/1Íæ›¹íg÷Zêåè/Ÿ 3º÷æ~Õ>vÆ_Ïý²}t×s?ÛÏîñzî£ýjîƒÑ/ç~Ù>º×æ~Õ>º·æÞ®çÞ_˜{¿œ»¯×s¿lÝûõÜ/ÛÇ©ºxÆ]óŒgüåÜ—ž‰®¹‡Ø‡¥øÞè!¨½Ç‡¿[t"î“NVmºŸÑ}¬ÈyöéÍèb´ÿœÜ@£½Ø¬d{´öÈö4–êÛ³ŸØ^‡ å2Û›õÉø]?¦8hÌ3xk/˜CºN6Ÿ@ÛÞ¸ŠoÆ©ø6:›CCŸˆ¹5ô‰ 4ƉÅútÌ!bO¸o)ؘ½‚.c>ž{•m>&bFçf4úû-B—XR5¿Uºy?ó1Í‘lÃŒÎÖߣO­ 1ŸÚæÞ6oýÆl¶v0ŸáÀ˜kŠù4ãÙÇÖî#ÆéÎ~7až}Û ±çãpÏ{ÊFck)³Û¹xîyoÖ¿`þ½]ÙŽuìbsn¢ËAÛ˜-´'ms®3ãƒæüí,|‰œíCf{±ßÍœCºø-ö6q-ÞÆO\‹I¥'¶fëûfó ³eìs8öÿ<››O³ÿšêÝÆM£ÉÓž8Hž †â{X¸|^¸9¬´…›!÷%W¡¹…›Ñæ/™Œ¹ä6Ɖ1×…ƒÑöM8³ÄÁˆ1¹'ÑöJ8!çÜ“ä6&ÓyÂ)ÑÌŰŒzxAÙKXWUàeXL ë;Æï×ø-¼‹ÀÊt•ð.Ö²ð.RpØCâÝø±½W!ì=>˸›ò¦ýë\„_8¯y¾CÑĬ²åŸøåüònã—#–‘—ÊÆ)ƒ“‰M.´…GÎ÷…GÎô¢ãž8·x;˜è tÕ’…CFê);ö-tªd*„-k^íÀ#¶ã\ÝrêÔž'î :M¬Ýê”qa d_X#LPûÄ ` ¹r°é°èÒÆšACbMó™¿ñÞhà¥}/:ŠÆ¾t®¤±¿yÓЃîS‡ˆæüZpó,mûÛóAs|ØYMùtœü4èô!}{ž|)ÚkÌ2m¨Aש·'­92Ǽé¤1¿sØtå·v û`§!«ƒÎG{9ú Zcšy? ´æ<'ÚíßòiÓŽû{Íå=7çל)““Þë]tòyÕ^Žöz´§µ·®ï}^<7oÞç5y 6H.›Žo„>é)Æ?SŒß¦lïö<û;þ,È¡­Þk)ÃÈA{ lêtÈiÊeiaÉkÎ$ϕږ|ÃTÉ}C<(Nêdµ»…+ÅlBáM}E*Хħ»ˆ¸Up~À³Pèï`ü Ÿ X2æ Œ yëáéË`™²‡ßBCg™š_ç0O®=Å}öÐQâ§hv—亅²s™º"Dèjèá–™ü¬ >uNmÙá:œkÔÃø-Ø¢ÔoƒöSï…óÔ‡“æ·ð}(“¢ ûø©o:Oý<ÆwSoOZs8~«s¨Ë.óÓ^z˜{[€üM½ $оûÖö^aLØ2ºrž`ARξ¥mˆó&êç°ÎWØšÃò)?¤i‹ >‰ÓFü³y¦87mº±åðÈðÿ¹ö’Ü´}ð*lU®½äƒ·ËÁó‡}Z¨cË–/â]O» l¯ö²ìâÒÚ²— mû¸é–gÿ‰6În^ Èe:Ó.ªxlF´Ø¾þŽ0i:öqÝ™;õ¼ÙBP¦Ÿw¾h~÷ÛÀÖ|t¾jM÷]t¾h¶(^½œóEó;s;/ç|Ñl¿r5ç«æÑàø¼óE³…A/÷ùªÙÂÃ×û|ÑlÓË}¾j¶xéå>_59§Ë}¾j¶ óå>_5¿“Žy6«fœ¯æ|ÕüN[‡¤ÝNã¢Aþ¦ÖÛ‘Ÿ7[ˆß0âyç‹æw4z/:_4Έ>ŸóE³åÜåÈÍv‚îrÎÍÖ¹_Ž|ÑláåË}¾jÛå>_5[$úrŸ¯š-}½ÏÍcäx¹ÏWÍ£sºÜç«æÑ9_îóU³u¾Üç«æ#>ŽÌ* ¼‚4(¡0•”\Ho’‘f¶gÇ0¥™D90|i¦•éSÐÖ§°ÏЄc~›­ÌdXp)wŽo†hÙšŠç·fº•ÀþÆ%°™A%2˜d®t‰ÇL¥’Ô^Œ®¤mÌÌ9˜j/™ó4W·dŽ9Ôç 9NCÿÚÜöR8‡žÚúÐh·-ží·´Í{‚°Ý¢1f~NcLÌÇ[ؤTŽca“RÙÇÜV³ÉAÛœÛ ÐKgs5 Ì„ðBåUSoftõìcg]=ËκrŸެä³LSÍœ³™M®LBø¢vÒvÖggž]júÖκõÉFsvˉÚƒà¦}3anQ}НqlžZ£ýnÕþ˜ùU³è`óç˜f&ÖÀù˜9^=÷Äø¹Â¬„%>hí‰]×^¿µ¶÷³…½Ï•ã˜IZ´'ç¨vœ5Çñ'?ôƒgÊâ½[:.þ¼¥mœL9í}ósÚæÉ³»¡Éç”—y!Ï›‰9eÇLÏ’Øß Àû–/aä.º-ÂKò•y»ƒ¦¼˜©]œú>tɾáI&£ÝÆ ò 8k>ƇfÏ/üqìcgô­aÔ±zà[!îMLKÞm¬#?g»›ý¿§´lw¹É¡ˆh9›ÑN—:6Ãbë ×oºÌ9 :ιÚÍ50ýµ ÚÍ5Çf.÷‚i™¬o ¹wÑDG{±•ߚ̦(›ÉLñJ‘ÙYê·0!ųJõÄyö!”¢ùXX£0LŒÐy("tFÌejŽ<Ç4Ѥq MÖÍǃ^¸ñ7Ú…ËšÓ¿œ3÷¿Z˜»ê[ÓÙ•)Ó 3Ç3Mg²G=1è¥?ݧ^ÑÞöcÏ›Rµaê'‘Îg§ùk˜¨ÐÁ…ßö°Ï·ã|Éó–Ò †ÚôæFœ¦. ¸,:l¾B8N¼GÞß‚çûŽLž‡Œ0eÒdDòesSÚÙôm—A/›&6båÅöŠ6Ðk7m#Éx [ö‹RÖeÚX+ü´½ˆ'Y2Þû´Õ˜ÎÎ@Müé– –©Ý+ýë'öiaÅÄ1„Út0Œ{/n%ö Î*ÄD`_-yaiÉŒá”æQ;KIòiø*97ÌJâ ìÄ4QµVâU`¿dv¨xÔ9ñ½íK K6r K–²d¯i‡ŠáN¦Ü Çe¦¤ŠÉLÅ`ng¹pd¦­A3m ^!^ Îá£Â4zž'ßdàù)·eÃÆ ùáþdÛakN›G30šòœÅäã 9!¨ç1gxÙâûT¶Ì¤¼ìÓ˜ÀßLs'ØAÄÓD‡ã8È9SíO¥ÚÑLoÆ ÛJ©6Ðìc²Q½ÒñÀö§½©Ô¼Ùh,ƒ‰ÀP¥=ìÐ]²R•š·9Ôª¨áù¡ÈÆ9 DÙÈ'Áö¶•ÄÀÆTºßlU¦ b.‹Và¯ú”…ËÏ —ƒÉs#v“çÆ4#‚h-ä=7–Iþ¦Ônvïnm¢mX– Ë*çƒ2†ª´'ly¥:a³+½™ ·˜ÆÌ u.Àz¥|á*ò[ð ±)B7S— k`4ô=y‰6á{Jqá{*\Êæ+Ø’â7èxî[¦oDÞ†­@þÌ¡lòEÈyã8Býâó\Ó´m‡LÁF¡|™Vˆ¡Å-{6èêãý–SÚCÄÓ……çUð[äg¤<²JVhá‰a4ÏÎÄ å*}o"=gú;sÎÐÙ™gWmY8I?;oÜóÛ^ÉSÆv•KmÝœd‹ØÄR;÷”d3Áï'›ý4±ÚÖ%=Áv¯qúÒs¶çCOã>33ß[‚|ë $NìkÛë¸ôé°Ï@z*=a®ÅÔÅ0]zÂÔëÔÀVé „?²ÊÑŒ§¥'"Ê<×lëÉ,»A™Q&îäT–žÈ6·,þƒ.!Og——žH®JÉšN­4bJ¿ÛþPO0aJ=ÁÔ.õÓû-¥ƒVê5M1镞•Î`ùÀ¤ñm‰{ü¤4oÞ´ÉõG€Jýà·5¥ôWa*¨A×*5 ¬±Ù4U)zì9çÖ-¾T™¾êö»Ue!ðuâ*Õþ`ùGU©Uƒ.g)t•ÊaÁ“*Ÿyé;Ce-õà è–1öEýá‰ûýàòùS|H"l~–Î+vg¹ªMS$#~ñyÜq•ˆ4­ôG¤í/ݹõìéˆØKT©Ò–ß¶\'ÌŸvO¢þ`èBbTꈋJö¥?¶”þ€Í'ýý—³lDÄ`Indbznˆ“ÆøœÊ+¥?,43õq•þ‡%ôR¢ýWɲqÓÒ…qâ3b/÷~rÇ·³=nP8j»Vª°}‹Áé&…™©GÚ¤Ò¢ûÚëd›`NÜw¬!ªýul Xìš“±¡‡^QyY]ú3ÁŸ¢ƒt´öxT"m{ºøcë” f®9šÏ˜eEè!~ ¿Dv"Ήþ&ìÇLþCŠ>sÎqva:âòÂtØäãà6í1&yXŸ…ïà®qUÅ—ÃvØ’Ä2Ø_Â8ÚbøM˜ˆúa%xF8κî•Ó½_vk`]1ší,gËêU{Àö¸r\6;ýÜàÓʵÔ]ÇqmvÎ þ¯æŒ8>×èÃFîC[k‡-)G¬\8ŽX¶p²-aù³±~á8íGâ8bÍÂq؃¹ªäº¬ó%?ðÜÅ'móôøªÄÍo%l>,~ó§höq?÷ƒÏÛæžW£ý±¯AdÅš c’l‘2q? oFܶE”M³pŸWfˆûC®ÃÂ}^õòE–ìÓÿî;Æ|‰uûï¨åî£VD¸ïýÆ}Ô±$ÅU`/ËçKiá>xX¸OlÔµä#‡'šØL ûŠS¢ŸçS&f2¦\úè3íÎ_¯®ö#¾XCÛÜmÛ<`ÌD|mûµãË¢¹§¤¹1,=Ášùt°©'à&ù}Œ'ðœÌ6Ñúqí*Eù›[OZµöÔ¸ò+ÿ˜žæu,è[Ú¿&Ô+ä‰4m•¾1™ú¹ ÌýI´ñ© Ü«ä–"ãºZÑ-?—˜žV܃'9Žç‰›Û6¤ŸN|A½ p²'<?§<¼Dê!ßYŸ@C7Cù޽‚ò8 ê!ßvüijÞÀ±Ý¯Ú)±Øã*<1Ú³ŽØíáÓÓ=òÞÄz_?Á<‘÷¦Nò¥¯Z_úÊ x\5`lÔÓN/þ9í¦oá ãþáhç8Ècw<®8Ìö¶òÌÐuÆv|éKŸùêV®Ï¯©Ûƺ0Oöa ‡}·åÚó±'ˆÛj¯{ç²f‰{Û軞ËÒsã¼V.gœcšzÎÓoèlG>©‘úÖs.¬˜O`}õðZzú^|%>$²¤l¾W|V\H|^Âæê9ÊiÊ ù\rÄ+5uËê‹§­y¤|å²õ|A鿟f^s©Kÿ¡RDúg$ýWý‡\"cÓg‘çµD7õ_hÌy27»Á퀰«»Û¸·lë€g¨ÿNÆ©ÿžmHU±‡¶ñ~OѵØ0õâ QG¨Ø#t$õ~wÆ—í})ݘ³bŒuùG½µô‘t ãWÔˆ­‡{”eãÔeÛgb']–Žö#ÎîY¹ÝéËö¥„¾Ôw¼„d)Üa¸ðáÓbëæÂóöwßó%3Ÿ>xwjœ^²¬;f¼þRÌï=~–žróöwÝÓ0å]moÿõã` ûðcŸu¤íA›´…æÏÅ|Ð~çÅtü5 ,æ÷½½WóÛŸþj/æ_ÚûX¾Õ¡6ÞþînþÇ!¢O>û\ct×vÙ~Ç5ì»ÿqÒÏì²ý΋yµ /ØUûýÖhWQ>Á…ìªý΋yµK/ØUû×8NêXzáÀ®Ú8W;0³"¯ìªýŽk,ŸäÀ0é‹»j¿ób^íÀú vÕ~Ç5CöXáÀ®Ú8×:°ä¯ì²ý~k4gáþÆI??°Ëö;/æÕ,¾p`Wíw\cüV"'}q`Wíw^Ìý w&³½øžw 3Ÿ‚ÀͳÌ'UðªNæÓ1Éž¿Ì|‚"YŒ5;Ý9¬ö×CuO²YÞ˵üæó5ÉêI2Ÿ6IvS<{Ä=’Õ–d>Å‘B,F³O;ɧ;’=ƒ•ù\O²¼bö}Ú˜ŸúHV×”=üÜdÏdÏû™ÑÛ˜ˆ&«ýÏ>±ÏXRæó!)&£ywÑêð2Ÿ`L–ûÉ|ª$ÅjãóNfÄï6Ñöm?iŽió \—ÝcȵՃn‹N`öíÖ·¾«Üt³¹ñ)Çh{Χm{» Ñ΋ϥŒµÍû–öôØ^»õç}E«ñ{]ÙûŒ9hÿ[Ü´»çÝH{¾%ó¹§dÏÀd>¯2ÎÑÆä-Ç“½c;~‹w)ƒ3Ó=ØnüÓxµ¿éîk3žÑ]åє÷’øÓÚ£hë¯;ÆÅúpäa·îgçÖàÌçWÅÿ³=ÏþßÚîze‹i¤4ÁÊNÌ÷yò_ó¾é”%Ѽëi¼«yÀ‡t^íö¼7l÷P³Ó=WãE>á‚{¨™O»àîiæ“/Éâ/Ùé~­í/ŸËHÙé>«·ñyWØj×2ŸˆÂk³YϼuìµîtÙë)ËŽ}d==OšùÜUì!ͳÄ0É-î’éìq/4ϧ;c𼂻sâ¡X[Ùt‰“çpR¼íµx÷§²žÖ+CÕ‡æ4y=Z6ë©RËHncÏUÝ“rS†cÁü™3ÏŸy­Lfm,äœwU,¤šsæV?3hÝ S†'­öhtÝý' ù/küàô»à2ÝÕÂøºÃuÌ¡÷5·Ym뎂p!Ú•’¬'--ŒÀݱ¬§-w"Œ‹–Ðö}.q¿Tx­ ^ªóÒÝTàQôû|õ.Ï÷ýLÆæ“¹¶F»ÝÖÇŽóòâ·:už–ÎüiòÉzнB¼0‘Ë|R /–Nì°‡Åó6þ”…xÈH„첦·dM²iãën:ä7ê ¿t¤Õçd>£ŠûèS:èKá@Ùøâ _Îíqf»Û¸3N©ÛsI1ç…;Ñ9â¦ïŒ¥ºk£ë²ý~vŠÕ~‚Xª»6º.Û8ײ’CxáÀ®Úï¸Æö)œôÅ]µßy1¯v`é…»j¿c¼Ø}ŠÀ'}q`Wíw^Ì«XyáÀ®Úï¸ÆøIÒKå…»j¿óbîw`ø“²c‡þäÑù Û¶Oyß²ÓΤMÒ¶ê°íi“vØö´I;lmÙ Ðó¬ç‡ËgQ£åܳc½¹Ý ÌzFÝží—¯í>[ÖSëVw!ÛOþËŽÀ[+²/b+}úËxÏ!ë¹w»ö'ßo˜LÛÜÞ©˜¶¹Ý‘¶y…ÐxGsÐÝ\³ ô 4m:§»ÅôðÞTˆË¶²<{澸§:mù ?Q6,ìJî¡•nM»>âÛy·¾-›.À'ܲûà‹É6ô° ¹·6¬òÔøV5Nô[u¶ÿºÏ)ÛÏFgþ …бFÕõÖT=5lÌY—ms`dƒ¬'s±Þª»‘qú¨U•Oì*göªÇïò _{[BvzÀ¹§ú%øìŸü¦#bª4ÿ€5 ¢×½X·gu&«=-"Ø5ÊEg7ã ÁÞ½Xs¨eÏÍü<ÍÙí•ç‚åCà&œ|ˆÐp^U5]ÖÎ’Ž=Q=b)eÝ¡Íz~ÙjɲϫmúŽçÂ35”ŸÁsOëÞïô9Ä'¬¤ÏQ6_éMòŒß|¨§çÉŸ¼ƒ“WLCüLþ´R’ìƒÞÃpË‘\Pް?º—5úÃg¢Uø‘z©ktzGÄä½ëî{X¾‹ÕYe=¿Þà£ë=øôUo¼Äé»ÄÙ/òŸêô]èÓïÇŸ™˜Ó˜fñb#ãÄ+ø:Â1Ì“u†¾Ž?0³m\ Âaø:œp[>\i{uÂdÌ_ïÀ€¶cOÜÑG1|Ë=¤ÿ7ÛËöB…¤vÔìד‘D‚;¾Ë7òþ:©zÙ~?Ýiq¦ûœôsCà²ý΋y-ËÍÇìªýŽklŸÂrã¤/ìªý΋yµË/ØUûý?÷)R>œôÅ]µßy1¯v`õ…»j¿ãçqÒvÕ~çÅÜÑ7¢þ§od÷Q§?tCSg¶£öu²{N—¥Ÿô9íýÆ\…âøyùLö†IvóÇ•cÁÛuÓgjqûL°£eSÐÆ¡­a÷\¦ boïMÛùý +Ø­ü3)x÷gÚ5¶‰Þq„¦w™ÿ ?Äœ‡Þ_ƒÝDÛvß‘°¸¼›qðº|/»;hùIeùaȸªø{\>búNohÀפ–‘ƒ¡–ÚöÏ왡éŸ%·ý3{¿lúgö§ ¦]IÛV÷ãÊ‘FäÇäŸyäfx^Þm1ký–Îݺ“!û7À§mÏ£ÈFö>RÖŸm±;ò©Cf€÷$/áÝ®»˜o¹XQœü¶ÀÜ)ý¶HN÷¹òòÛìÎ×ôÛ”߆üžü6ý û™éÃyø¼{ìáçÑŸsÈßÐ_qGµðÈKñ}—¶Ÿç#Q½ß>Ÿƒ]ܵïÛÿó{…wú±hŒÉ9ûå_zÄ6˜ÇóÏÓõˆ‘Ìùç¼×UÊÌùŽüïšØŸ'œ~'ò4̇œ<}P‡\4ïN"#Ô!oÄû•öðôM΂u÷þ(ýTϼW!Ÿl@.W&Ècý¹×_‘‹?ø³n¾å} ò³î ¹U; þ'ŸS.xÿQòrÊQÙò%wmú²’G¾=E9÷øV½Ã”ëuÿ.{ý¿´ó©Ä½?¸ë)ðîÃôk#¼'úµÄ½Qá¶_ ¼’_ ›zØN¿6C_táa]¸—·ø»²ûÓßµ'O³ã[ø©¼l…Ìþ íŠoŸ•^ù<¼A›Þ¼K+·‡÷w³+ë}ºé+WÖ( ÝÁþ~3u™Û>´ÝÝ™zª1?Æíæ[­nÅíi­¥¡³Ž<èÔ›ô¡¥­¿Þs…îÖÛÉÐS<#Ñì_óÖã7tÙzkÔ[°¤ãч:½®ä ÃѾj%¾5Dñ”æ÷öç–Ÿ=˜ÌÐ%ñ>ÇÇYLí4—–;ÒžðnÇ0zdüýþœ}MëÛeç>ÆûÛŸþý¦ÿ­õÉöÔ…YˆáÉ^=©ã³1%ãßþ›õÏÿîødXšca)¿ýÃDZêevýù›ÿ êŸåendstream endobj 43 0 obj 10294 endobj 47 0 obj <> stream xœÝ[K5H Ñ7°Äe&b?ʯ#H€Á!ÙÉs ä±”°H þ>Uîî»·&¯µ[„¬´òÔ¸íþ¾ªï+÷ì䥽ÒBÒÏ4xvÑ}{Û‹ó¿»—ÖQX â" ÿìÀÏÆÃðîÞ ñW—VŠ1Z…k½:ß-VJá$þzuÚÝènuiWqû§q€s_v¡7ô/òñ³ ñýï+â:b{–vÁM@9±Qb#…’²×øÛôb{ѯÄkÙíÑ+a×Ùûh¥S+èEðу[ ·Ö=Ø•œ  £ó+ñ Ž H xåµõFõ]ôÓ…AJ«ã$ Ò„€³žÓ,o ˜•¸‡o8ëuÀUï®­…Þÿh{³ûa‹¨• D= ®†:†´êÝtƒTïp B½_­·Ï[±k< úù> ´}˜Òø["]éè-q»Ñ½T2ÂJ<&εÀd< IHs”˜Œ³tfÀ㋇ôÂHc)“éè ­Bða%Žè… Jº8»¾xg;¤67{ã4Õ—×6xºI«lï´mXé «ïDÑû”‘ò¸‚#3Õ‘³½×ÂûÑVº@¸ÑGˆSJv¯¯¼-ô:Ô£žíú~*y=:…5°s»#Ê[TÈú XËñÑ«¢Æá Ê…Ô*ë(åª(nLåJ5ÖÑ}L¤ hc°+Šd˜yü8Õ”v1ºZ£iV[ÊÕÃ}ž§Ü†µ_iÚÂÎâ¿§e‚Vv¾ó«Ñ¹Ë­NÑcoZB¡\!Œžªwm©hÜ vìÄŽq½Gv”ûc*X¢s¸@ëÞzCOÛ¢î;„®ÞJ|‰di'½Gq¬ŒÆ~y=‹>Hÿ#*ˆƒIPÄb-‹„`Aû‰XÊP½C‚µ‘ìˆüÅxŠKŸ¦Ž+µ”fN>Ö¢ô{;J§‚ɧRWž·–ŒÐz¼Db<£n3Q±1*:OÞHV:…ؘàVK"·³Qt«'Ù­î§ç`2:óµÏ‰Nï,9Ùfëå4œ4ïV|D,»`Ëé/hu‰/⬑cÝÛ³Gx7Ki>>9pÿ§ÜÓœÀC“òZ:´è¡ËçßMe‹‡t5Û8cq›‘›3wmm Oæ46&”ÛÏÝöÆq‘²¢òÌÏÔ³/ݱð•‰ú’ëpYαݧ)AA0oY™2œ]×Ô¯—>»OÌ6?»ŽèŠžwƒìœ5ÕVô¦ì³nCG( a:€áyÊ—‡±¼ÃÜ¡O Ðô|Éâ¼öÇþøg¹F=zLAÞ£?8jêõd4˜·oȹÉtQÑVºBèù”ÜŽÈ/m0ÔKsCÎ}´ð¹Ý;>%³!úÂÙ’ÝëdO×6D$¼r‡}RÜéÜÍv¦­Aé}…­ç¦û$óþKzÀõà燓²ïÇóvýá -Ëé×u¬ìAëjT:Hå$ºÓ⌷çõWöÊ} š 2†‚k.Oéø–ÑžMŸê„”_VtFz"û©y¡eSžß1ë¸ÞIåÓ#¸€¼ìmÄh‘ÑíÏæ4>) DüÓñ šÌÍ ËyäE\ ñ uäØô1n Ec“ɺUTø¸ àÓ’ bØ[¢ÅMw}wôŽÝ_ àíOêéax€0´ëkôA«±p~}•+ Þûê“T7ÁGeVŸïßb±»^ÙüYé0\l[%ÜyÜ·€g­¢ rxÞÖ‡‡~g8xóxmxNÃø¡K[xøðΛūËaw|kO+¬?F‹l¼"¸\@„ƒÓß,^[¾ôG88ýqñêøÐ_ÂÁè‹×Ç×^ÁzV\¼">'—àÄ7‹×‚[@y‚S¯ nÙ%Œì¸xepÍ5gdNsl¼"¸€—èd—p0²›ÇkËnÀ×^y £<6^_{ñ 8.‹×Ç×^ÆHV\¼>PéË èppú›Å+ëoÄ·€þ§?.^ßúK8ýqñúøÚëJÓŸu®¥þÀö2,¡?ÂÇéð5Ô߀oýÑ­sú#| õ7à[@ £¿„¯þF|íõ$û÷?6^Ÿ7ýrH08ùÍâµå—à- >‚Á©‹×†·€ø F|\¼:¼æÚ£ÿIÅi׃g¥¢ïÈ·_ÂÁˆo¯,¾_{õ%ŒúØxu|íå7à¸,?6^_ým´ñ½ÔZè`è‰2}£i\Ä*çV_¤ohi.à8àÜ ]1ã³5ÄH·¸ãëV÷/†¹àendstream endobj 48 0 obj 1901 endobj 52 0 obj <> stream xœí]]¯%ÇUˆq#IAÃÛ½s¨ïW$@²ÈC2ObÇN?p<‘løûìµ×îî:çVÛã¸= bÉÙS·ººj¬µ÷®žëÏNîìÃÉáŸExÿåÝ?ÿ¼ž>ú¯»ÏîBè§ìÒé¥Jâ§w©^É?¾{þ“Óît¥Þ{ö²Öç­K¤ìÜ©8ù×çÜ}ø“»ŸÝé[O?ÿwdîgwíñ?å÷_žþå™ì«ŸR;÷rzö¡¾HÞ“|==ñ§'îä]8‡tò%C;={y÷öýéßž¸s .”|ÊOü9–ÃÕ þj)^þÜÙu—\«÷§ÂORÈÞùûÓä'=ç\›>å‘PD~ªr«%ÝŸžÉœâ[ê2ÿ]<ìSqøÃóáãBòîp-º¨ ɦb*QVúP&ÅØZ‹Û\Æä-ê).øˆµç^ñºìó¹„üγ7îþõ™¨ÛŸð¨›ÂꉶUéÿ¯í+mÿ1Î-ª õZÝaQwÎg_NµšsßÿÉóO6ËÊ^zí©/^ÿ|Ä›ÓÙµS-bîËÿqü¥gônˆà_<ø³«¥ŠÊßUS®°¶<JhbÔ` q œb¿?ýæáI>»ºÈ¿•‡}¨®À à)Gµi”'XTÜDŒî»t]å9çÄ$ïzó!‹ dÞE,}=±TáÞEjüuÖÂWEñ4߯S/gÇxÌËþ^=qzç\c–•ž½€B>”ƒ4šlÿ-è&ø±_î-ú'íìkt2ãwAÐ'U9Å‹mÆÛz¸R’‡ºü¹¦¨Ê€ï!ät1ù÷˜KNªD™’]ˆ²Þ; (NÌqñv›ž€w6]œ{]=Æ ³ˆ¦‹ ¹fh‡õjþÅœ§8RîÉ…eÃ!·‹×^¬"˜€¹|E\=û»g?QgK¾CïmÊ{{N-ûbÎT› s•s±ôú9G7 –}²ØêIç öeÖü`|Ý`Z;q ~ª.Ýöų£iÞC”Ôj¼zxœ48Ïø2Y´žS‚·æ¦)* Éx°Xe7o‡ÿ!uq¨oÿƒäáÁ;ãþÿæ•ܱãÞeX5×Hñѹ»|9䛆µØË§XFŒ‘r|rD±y:GAAÏt ‰÷Ðé]£3BJˆ¾ÐñŒÀß~O§P.‰á‚èv©ÄèÀ#çÎ5,ÿÑž©>„QZ©m{™¯È´†‡/Ó–Uù¿ùæ*ö9ØçÅ|ö°Èh’‘ìßÚO€·–Ì7¶“4 “'ˆ‡“`L’¡¤oƒc J¿·'A;ã ¾¯…±ÄLÈ׉þó˜_ `ü{–ª­¦tÌcY°=9 ð[ÌÍaé±.j£…ØâZ.4Wš¤¤â²õ"93ÊHrÿ«5u‰ *ù¿*‹Ý=Ç'†Rl—ص9ÔÎñïƒPGòÊ£/qqc9ž4»zE¿’ÉcŽ_žÕV£úv¬³»äóQ?¯P¢½ÁŽo^ð 6dŠ|dÔÂÞžÿ±ÖB¹ÌvgXû¢T_-;®<²âÜo†:yî…âX1Júéµø¥eýc«ëÚÑU÷¼2ûàXêOGÿ6|œ„ðÛ·AÇÑh-|tÌ#Þº%9–Z¿…m}©Qu[ÿùZ=}ÂÎUe}lïÛ²¯ÙàÆmÊÛiæ(¶Mâ…µ¼:Û^`™Vl5Ô fÜÛ ù†AÑI9ñÏË’ò9V.Mµ9ÀëØýOí$IHúÚ Eáý¨µú¨*Lx²;-ùÃ7ß)ÿæÅqÅßãJSÛ´óTãR+ÉŽàzȬÛkS¾”=(r4ͯæ˜ïý÷(âÙT/Óßàã–ê¹xívÖ ÛÑ𪼋·\´r·ìp'³Ø!ô±>§ÌÓÆËNývˆ‹Í ¦ÑJ¢~dÙÍ?`“Š_£!/ÓÖµä¾Hî¿–eÖ= ñµÇó¯pK1d¢Kÿ¥_èéò.d¥ÁhŸBYž,ûnHlÍ1I Òã)49K€Ä;¢êé¿ïÚé§‚¼old½óÌS£¬Ü{qJYÁ¹z’Â- 1|…`KK"eáéŸ~Å|ǧ³K#뵫ŠÛn%¾dÉÀ߈÷„"H]î¨x¨÷¦—Ñ­vï¿÷Ñ;Šñþן?´³ÄwèËäâEñ Ô‘á¤Ξ§’÷?‚˜…KÃýwõÑÕ&SSïØÓÇ_)(á‹ hÀ·PMCó<•ñÐe\²Ámô?×ù“ñ¯¬Êp‘:¬j½ŸrBRõÀŽ™*INî+üï{â~¢[Qâwr.gq“/88öÛœÓýÆõC“éø±ç¨]ï¹pŽ¿{Y²˜rqŽï< «¯âß_ÏùŠ'Ê~n™ëñCOT|9Ú2’hL-s=~ì9²¿¡eêŽeê--ÓÚіѲ&–¹?ô5ÄÛX&æ)?LÇ#†ˆúð5'h`J Ó1Ä:'†Ùø1Î]¼1è~'Ä0?ö·"ì|F ³ñCOt01è~'Ä0?ö·"Ýù„fãÇžèXbÀ~gÄ0?ô7#†:/¦ãC*¯=1Ô½Šaú!†ºS1ÌÆrŽ”o@ u§b˜{Ž[CÝ©f㇞è`b¨;ÃlüØsÜŠêNÅ0?öDÇCÝ©f㇞ãVÄݼb˜ŽH b–לT3b˜ÿੌÏ+†éøAÎÑêñÄÀý>&†éø±ç¸1èÎ'Ä0?ôDÇ÷û˜¦ãÇžãFÄÀ?&†éø±':”t¿b˜ŽzŽ›CšW Óñãˆ!E÷ÚCÚ©æ?bHóŠa:~ŒsÈÊ7 †4¯¦ãÇžãVÄæÃtüÐL i^1LÇ=Ç­ˆ!Í+†éø±':–Ò¼b˜ŽzŽ›CÝ©fãC}í/ŸUSb˜þ@ˆ¡îT ³ñƒœ£Þàò™ûÃlüØsÜŠêNÅ0?ôDCÝ©fãÇžãVÄPw*†Ùø±':–êNÅ0?ô·"_jhùä³þ‚ ;Ñtü© ·ém>½ïLïÓéÉͧ_/ÓýÎt?Ÿv¦‡ùô¸3=Χ§éi>=ïLÏóéegz™Oß±êõø2}Ǫ×ãËô«^Ûô¼cÕëñeúŽU¯Ç—é;V½_¦ïXõz|™¾cÕëñãò&Á#ᔾ"×_oÉͶäæ;CÒô}Í”r°ìI2U;²¤1ÏZþ·Û’CêôãG©ÓÛ§ù9dHøA?½#z±}#éÝé§r˜7e>¹ó]ò§|?½4Ù—| Îyp±)Jïß%—åOï’÷‘ròËS”°–ÌUYA«r‘„EŸªò/®éý;¾ò§w|3dîO™$k½÷±Øïë~kéñAí©ÔL³ýå¦ÍÕ>ƒ)$¦ê’{ZÿõÐõW f³DXóàÊ  øm>òó/Jy[ÆK´)*r†â„] — 9«\¡òUn²§ŒùRAî˜#I±ÈÝÁX!SÆ»‚>ÛÅ!Dv”ŒTËx¯Ïƒœ(«Qý s€5]ä<È…2öãâ sÍ€³;?È2è¥ë¹zt¹7Q‡È\?é8×L:Ÿëäjâ³EiŠÏJì*e©Ü!ÇaœÏµ]ã¹²ßæ¤´=Û¶fÞ%ª—=Øþ1Þ7½-û¿açËiÓe[GõS¹2õïÃ`£°ÙHíîðܧlíâê­«}ͯ0ÌßêêK«µšVߣ߆џuæç\‡þ(§Í·ÕÌç9Þ†ùMEœXÉñô/íƒd´‡Õb°^ÁÛ>´gªÅ~Rm­– —UG5R¿’á‰ìWÿ¨K½AŠXëø¢rŽÊ y¡Ú,ÀåSQ 8¢$UeT¯¥6•Ã¥¨, ‰œ)‹ÚRI:?ÀÿŠê" àM%$•;dµqˆò)kœ„ˆ8ÉM÷*åª{ŽEö™¹¾¤ÅøËhºNìxVm#%?樄äíí©,ûIc!Á6©q|=UÝgÂ{åð”åYÙ(eÌɉ²èS~ 2ôŒ•±¦ê\d<â&Û~’è'¹ºÊ±s}œ1Ú~’ì3Ú~2úß¶`A´ýÀbæüŒùÔ3¶+2ç/bäüŠuç£WBrfîSBYU=KF\…ªó3|W8AeàTà»2| ¨¿á8§c¾Æ°<¿ïª+˜_l ó žõUß[àožçªøÅˆÂD*Ã|¢œãWA;ŒÕÐàoŒóÐ⊱ªX4Î]×ý(gC‡½ˆw¡WÅGì3¢Ð9©ìó‚›"+V†Av”5jÛ6Ÿ¶C cã²¾[æË{òfP¼#þd†¹²ÿ¶î¹E·…¸æ©‡ºàšèÇ/8…ß ]UêãôU„«¦4ªè­PV½)ê„¢ØËìèaÇHûB&VdØ"úIDqCepdÈœ»ó[Äx0ßnÚŒd¼—¢gL•ÿ¤¯õUÎÉêÃuóÿd1‚g³Åæ·ÆZ$%è9V‹S¬Ù¶ø<£É|¸p‰M•-f5ömo8o2œ?'Û[DìÛÞ"0ÁöN¶7àm¢Îò†T¹øOjœï—¸œaìDàv6œl¨áèÿR‘uýŸÏĨßÎ|W ZóñY‡uèÿuÙš®`‹BLÀŸÂ3<[’'¶G`»îî"øB¾TˆÏ Gá—YÏ‚0{ë~|µÒŸ°¥¯÷²ðÑÂY䯤­^ËáÀqÆÇ œe|LN4.ƒ¬ü¸ñqø¸|\%ú³¾—öUùXÆ‘})â.|œŒ«§¼ä>ô 9ƒŽ“_ާõü‰¸Œ´jñ'¯>t¾G>bü”«ç¡l\c‰x•cÇÁÇÉ81{ÞbÉ|«k<0¶r[±ÞbF±>£³ò ãY± Îi{Äqà&œpÁýH,(ˆ‹U¸’¨ÜÓÏE¹Š~V€kópŸ‰ûxj¸—Yø¬z'&ê»ÈqYUåZgX!óYÅÇg±‡@­ÈQB+Î/VØ%GÐèÙQnÀ;=oUܬ\ùf`šls”Sà Û|¬™ë s]q¶oræ{•³‰ïhª/X¬<"ù¾g<Ý”×éoÍ+góYàQ Æá.Bðɸë£[Rà³Àß@ìhš8> zã-í“Ñ÷Z++¯´®ý3r¤FÛÂ…}å¡ õÔ§Ö+žøÒ5o(§¢~‡Ü”Ït}å]Ÿ5Ÿ —÷*üæ1ÑåHÎQNMœÓל=zÍE4f£/Zh®X›j’[êêZSJ¶Ö‘1ê»Ôߢ橬Ÿ¢æ‚¬±"àß꿘£æº‡Œ½YŽR´&cŽRJ]s”Ê|%P^kA“‰d•ùJÝæW®©õ%Ç üÁr{¯ã~º~jk jûç>ѱXöZ¶7FÖ‹úiKNCìKýgúT=ÇU·(Ç,ç‹Nk8Ú©E޳>£Ýµ>3;2ä­#‰?±c¹f—xæÓÊ=ž8Ö™'qò ÏøíêKÄCöˆ“]óQæMý‡\¨ušgÔÀÙžxÒÔ—ˆ3-«Ìg‘ÓâR/â•æ‚8¦|ˆoÍkÌòYï×\œ±o1ÝZìWä† ÄŸhØÜK†9Š'í‘ ÿ\pIqÏðJeÃ1ʆØãÔdÃOì­Øš8 cœkXJìå:ÐÛ‚½ès„Æg‹bµÕ Šá|Vk'ò`Õ\9JäÝ–+GÏg½òŸUNTß H[D¶ZEer“ÖQ¬÷Šæ¯¬o‹æ©ÔgÑü’˜\ô]–‹Ã#1<+¯QŸ¹)o²¾Òü˜:ÌÊ•äÙ ˆä”ÂÆË°µñµbÈ’û*wc“r%õ¦yBì[¾hy0Ô$ùësÄ`¢¿R¢¿yäñ‰þ晳wзº×Aÿ–Ï0ÿaÞfyÑÖ+JÖG©k^´äW[—†. 9œåZk>¦9œZÏë¯éç=û‚|Ú_Q6°./}ËØ)k.‰²½Cìš—>OE¿8°ŸÙôΗgО³õ ´»¼ôó)/=4Ùf^úr ÿ€bºp"göläüÙ%®Ù ŽËÙ±.qÀÖÞ©kÄy¯ÖË]wúò\gÎäà+þ­œ×ÈÁÚïiô4™Rcl;p@c^å5òºòbe-‹c¤Ê:ðo9x@)-y=íÈêÅëBŸVÿ(Cî[ì\Èur³ñ²öxœÖ=ÑzQèñе¶°þ Ú…RW±æ@\YMæµ®bî‚6ßRó¡Å¼ÔˆhO/1é5xv¤ ^xèÇpÇ+bóW¯¸F\ðš“1O ꉌ±@žØzcžëÿˆHëŒÃÒCëCÊÊZ÷³VÓ\‡ø”ÈqHo-‡`·È~íåã`Înáì™+°Öôe“5blŽV@ÍÞ›^—ý¬ýpÖ‘Œ…4WcN‰UÎeçMkOBkOŒ ÊÍì9!½±.b‚ºUn¦ÿ«4WK= >³”y¿Õ¦mÅq­S­ö o”Íg¬FB›eÅ;ÖoôìÍjzõÏDßPÎÎü6¬~ u¯þŒYü¹ø¿Êð¥ÂÚIãk‰#àuu›Z[m´Ø†VæŒe7Äø‚Ë ñÎõÑwlŒ@OjV¯#ÏëÖ†~ºÅ8òÅN_ULG²´ÔîÝî.^­Ï¯—°†“²õª3°1s\RoÁCΕeæ£z·’éoÚçÏì‡Iæ„Û‡².5Ö¦Ú“oÌsÖiìë¢dN¾‹Ô%5ö"|«9ë·ãÁz ÚK±¼ua¥ï¦ª÷œÃ{ ë¦õBû„vƒŠ–ýÒÒ>D!~á7áèGÑÅúz§`}äþeÉ›Êzï›…x𑳔`rYû]Yï œÉ¸S`ýZç¹™ Nª&#†‹õ·ÃÛ¸èMÙz°EŽ&7~¹§2ž¥í ð7»MNÝdàN39¬wEó{¯ÞYØ{õÎÂÞ«}H{/ó2Ê)¬=$¨fÉ阫v“ÄÜRòÚBk/Åj½åNÊš [_ü­npiíÙè•y$^WÍUÙ»B{.Eæ 6]ú.Ú[¶^KIWý•bµNXï X1oØsàúzÏgõïb­ß ÷V«iMVX#*7'vú6ŽNëÂÀìÈ~»œ"®õ½omá¿jZjúÀÚWëTåi«#kz­q#ït#åí~!òN×mã¬ÅcYïlŽ£äYjýûZ—+¿Z-îyR¹ÿõÞ:z½¯õ¼ãà=(kë´õ<ØGI¦·°ÕʺŽõc´Ïaõ.8oéýh}f}£¨='»»A™¬^„­³Õs~½{ªÚg²šyÒÒ“Ó>Ÿ³ºM{~\ǵ¡nÛú…EûZ÷X¼Ô­ÞÒ>ŠåEû‚wÚ/d>¤±í>KïþœÝm!?ð&ûµîaœÆ²Å¯ÕãÖÕ{ÉbrÙâx²àô¿àbjÁ • O‹¬6Õ»Të¡*^Ù{Çì½zOjïeÃUÍ¿›Éz'›7Ìt&{kz÷g÷¹€Eüç (cN²þ10Üj\ð—Ýh-kwy¹å:z—TˆE¸öH…•Þð~§ë÷‹¼OÑ;kê\ï•xλ¡šy×W»^UÖÄ_Õj_ÍÈGÑkÎÄü»iDÞ„O6žEsúV¬@^E"O:Dbu¨ñÕíî¹i§ß:بÛ7+à¬n߸À¯úòŒ[ò°%ؾ?èC­Ü‡Z¹µrî;úR+ǼÝw¤íûƒÈ<æâ¾£,ßøÔ…Sù}D±œ9¬}±æ³^{e…ùj_‘·ïVŠå†zÅZMo`Œ⧸­~5_tú@««N33.Ö‹Õ­zÏVê÷Ùjø7m©µ]&x`A&v(V.µfïkì­ ˜‹ÞáÑ4æ‹ù‡ò1óª´ÅÈ[oD1ÚînôN'ÚúŠAÍz/}åÝ ÷÷¹n2¿{Ðo)ìŽ?ð^ŸïUžv[½¬ÕZŠ1´X,S­kOë?«k£ÞX.¨\˼ ­«Óõ› oñÃoc)w¿Ö»I¿Û*–/†µ¿ª÷¬VûjŸŠ=[æŽv߯qn÷ýYï ,oÓž0kMÅÖÌÿØc™wê†qÛý:\p©eKÚê]öÀíò1û~äcëõm~!Û:y×<€9wqi]_û{Vç’Ö=뇳;¸¼Å­Ü´ßÎú†ßsXo@ïÅùï¤y—Áï]¼}³¢õ4󧨶~ud/š9½öÏ_‘ýgöxdwÉa½û×ïuŒ¿AÅKO8 ÷\A{¼Öó¨ÊßæÏqí¯ÐϽùY{ªŒ‹´õ!ÿW¹Zì Ocµ ó׼ŚÛî³­?4޳y½³×{îdëk߈¾´î·^8þ•UÊÖ‚-ì{#ý.ÊîæñéJÊÖ3uå`í+dæOì{±¤wä™vÑ;)»³×»ŒluyP ÜzªÅ¾1Ôž}7§˜lß9ê]¾·ï!¯(Æ.½Ìºâ³á¹}ãVV 7üß8¦ SŽ)Ç”¥>.䘂ú¸º!z²Þ©³>þÙÝÿÒüxendstream endobj 53 0 obj 6717 endobj 57 0 obj <> stream xœíYÝo7 Çöx}Ü€N{³‹Y“(Q°aź}kç&]Š-íâ| Y¤ûÿ‘º³ºúçÃ~(Ò}¦HŠ?Š?ž|¥Œ¶  ÿ-…£Ëæû×Qþ×\5Y¡ñê²HÅr+ž5ûÏÔ¿M±”sFK¶>ž®Lx4FCÿ}΀d0dè éØ q©Ÿ’:rmh6y¢Ž§h³F[.GðŽöçš´»”eÈÕ)sË”ÔTŒ:øRÔ_Tç‡bÍ1û¼iõùÞn½†¤bÐ0ðz·“týî¬ñÝö¨"~e00ÅD``A5¸@¿, ‹¶ ÌM `©ÿ #ï-õp£O«úˆ>0|$SmYÂï\X:as«C1%Ãkß“C•E €“U²éè‹w,™WÌ…Õ¿ø ëÆÚÛ¶J†¤ç…ЯTaü1ÅH»³õa½_9\S©kœ†G”îÒm[*Sâ .ÉQ'ÆBwN¿äsÊH¶ 4FÇCÔ|Á¨½á†æS(M•ƒvKêxc9qz…ÊïSƒ§ÕÎ}ömós@`‡zaу¶O÷ÓerûõNÙ|Jk!ûÒǹ˃o Ös'– ô˜;šŸr @Ê2ö=aRxVd}HYC銔®Äú]¾@$̹Ubb¾¨3ÐŽ_vKÜÞë¼{î`~÷ÛçŽnwqÇô<©zþCé%!ïPk›R*5Fr¶ƒ å^ãbJŒå mMRÕ8Æ:\üÕÔ!Ò¦ò.º~‡ã&]ÿóÍï6JÖ’ó.¥{Ô4 ”f&9è¨Ì°4Úrˆ§mèÀ_ EÚ.î°8h*×&í`+ÛÀ1w-J}ý€ñ¦œ!Æîu|è÷á îK/a AŒ=íKz—tI$FèøB’‡¤Ñu'‰ÞìiD2d7\Pâk.îΦ³|j«‚™ç‚÷/8C«s¥R rÀÑKÇçSÄDJŽ M$t¶LÐÌY09K7¥ðïRæÆº }5º¡É·¥—yi°\N ÕO9ÅÑ$€>ÝqàNî{Ôu•©·ÜšÍ'¢5¢¯ã;æŒÇLº­!¬ç»¾FFѪ¢–pÉÑOŽX] 4¶-K<®fÕV–£¢ÌïYUmßñ[šÈ˜]¨šv=‘9ô|[µí‰¬ÛÝFÙOå …þ‘ìZÊ6\Ïagé òš)á°@ŸÀ¢œºƒÞÚšÕ?ô¼Ôh®2©rЫôåÄ ä:9Ht ›¿fî£;Ø7àáØÊû¹K±ÜomJÃk;"Û5±—å[ºlÝRg¿zy^Q·dô³®°Œ§·)^Ÿe³–Ä@œË·6ñþœ[åkì’býl#µ· ²fƒÕÀQ1W½`ý‚ÌŠÔ³#¹s“‘æ6%qÓMÌü 4Î>>íýÎINøÖ¯ÛÚFŒ7z Fèyx‘òèÍèFý– (¶Wg7¶[¡²7ª{|h Æ×¯jòu\>ßû¤)'hÏ¥×)ØÎüÚ¥;åE®ŒMHñ±[#öpÔI¡”Û§Gê|¤Î;R§ÌÜÈ•Åòãô^«LÉ×`i³«¸ú£Ì¿ËÈN—ók̵þò'—dïüÃÁ«æ.ï©endstream endobj 58 0 obj 1431 endobj 62 0 obj <> stream xœíYÝoE <îcyâ2¼ÝUºe><~B om®I›Ð¤¥íAJùÿ%ì™Ù]Ïå6 mDEOªæö<ÏÏöÏöæBéÞX¥ù3,^Ÿw_?‰êô¯î¢³•× Îó*ðòâÖº,í©?»¬ ½!]ïNGàµVAÓïÞv›GÝã.ŸªžüX${Ñ¥Þñ¿ü@®_Ÿ«ïÖd*H=µÞäƒè0Q­ŒZie´í-(k|o“ZŸwG õíR÷ÑšÝBm–+Ûc‘Ö‡ôÀB€…:X®LŸœC»Poèy0 Ð,ÔZ£÷>¦…ú†e$ç[ù§$c‚­Î½åJ“ ÐO õ2œ¼#EG¼Éóæ·,ä"zŒ u’w s†ð{ïì‹õOÝ÷kÂÊ(þVeq XéÞTŽû¸ zŸ°"\\ ”€ ¤=¨ûZ|²\ÿ>ù„ŒÂˆ€ƒkÆï|lâ|Œ¼Ôžú~Ysõ팆z½£ìÓ£ µ.ž$€«(6°†‡é½õ8 &¡½¥éÁ Åâíôi¥ð0­÷IZ[´^ž¹¡‡I“omëæA¶Ú,¾Ö7)óaî¹¶ì£]qÏG†Ú?% cf”¯@Y ¬H@[ãøSŽcF¶l°¶÷ÑqÝY¿aŸrÊC ”ñ¯ØÌà¢O9ᙨRMþq-e^f¼0YÇx1ëäåQ±Á§˜eB´‰aÂ×gš¢À°.ÄPhmX?cÊJlª¸ZíÍh¥÷ØnÇJËž-WÐ'CŒØˆü¼`kø²âñ2ÆÇö¤C¡ò€U†˜£à„öFï8NöÈôÅ »³øE÷vưuc˜j„ ˜ìšaI½6:ŒÞyÅÖd€ ¥î S‹DÚ_’Îë”*5ÇàØø ‰»öqÕl­!U䌔r1!_ôPï- —Àg螇؊<ÌeÂ:õ<FžY,OÚÆÁò|êíX>ž¸Éi¨M+J~£ts¨ÍVîµÎZ ȯlê£ï™È!VL¶¹s>ªwÄñޱ¤JÍ¥Ÿš£O”‡&$î Wvû”î½W¦Ñ†‰;ï•ëíš^Y¶qb}’‘hå 6ÙÓ•8Iš{½¡¿+é²³¿›6ÊN“•„Ʀï_kŠ«dS\áü/Ás{ݯKŽ&Þ›3«lawPSfÄ߸‚Pyö-ÛIV{‘çfô:ÔZfXÁ"“©dÇ;ï„G —Z.3~6àp¦ºÈ‹ÖB¤¡ç S©·y”’i†­R°Ò{ 敵Kì”Òí¼Ub6óey5ÊúrJ#îù® —³ÜAXŠØ«Aâ!#u, êîÂ##ìóü †ŠÇ¶"é‚ò:ú€­‡kìAn‡šÛ}vÙ=ŵsÃÚgKRxG h°Opï Bìï¾€ÕË5L2qˆÚm½¹¯%$³Œ!òk¿ãÜ5'k£ä`¶tHÑ·{íŸ J~Ë)d4–Þwzë!e$ÛQŒüe°‡ƒµùav‡´hx“hî£XËZù¿'®ñÄíÕeþsÄ‹òvUyRÒÖv?–ñç G úý…ðÚŒ¯ÒU’MçÊkcSóe§!\䟳ZMû‘+‚¡YÄØeÛÚÜh9æÐ_aÃÙN˜.ÍR<9ÇoþÛ{V0tLãœW¦9ùÌÔß!WÈòíÐ#>o®ª´>ܤÒ63ÔÎ7oò6Ò›gvpÛÎ÷t_p[ï9åZ¦P-áÙOBtÇÖ¸Kì’#UÊnf®|®¾Ê”A¶¥+gý÷?aé#i2Ó_lw˜ÿ¢Eendstream endobj 63 0 obj 1545 endobj 67 0 obj <> stream xœí\ëoÜ6Gâµ³ÖöÚÉ&Í£­ÒÄ鮯+‹¤H‰½ûTàp@Ñ~hk4š»¢×ëÁ%@Òý÷KJ¢8\ͬ´¶wã¦I€dÄð1ïù %çUœ&ŒÇ©ýíˆ_D§_çñ/¿E¯"Îu,Ó,~QRÊ’ÿ²|®È_£§'ñ˨ÜIk-™Ùëõ/Í™LÓX¥æ×?E?ŸD_Eå©ñ×ÿª 3÷UT$Âþ* ýã‹ø³3Ã—Ž³"Ñ*>û¹<Èœ“±<ž³xžÆ,å Ïb®T’›/¢ï¦q>›§ Ï•br?·R*ÎÅ4þÆ>Bra¾/´lÿw6g +Œ`Óø?vœe*ÕæžÎÒD±"³ô‘¡µ”2/¦±œÍER0e7úÁŒg™H•¡¿ôÓ™d2Q\þûìóèŸgFzÛßFúЏé Fø,Éù•^%\Æ¿GEü¥ÿóÈ0–2-°-Â(H«"6lÇBf"æ…ÔÖƒ¾ñ ìÖ›Ò±J¹pJã2áEœë¤ÖÙµJeFN9½è-@½ è@ߘ1‘áùtFwýìåÌDC’긄Ñqʤž(¤=ÜË]r¶\l& …Ü1L‰}T[* -Ýêße8–«Ää#Át4;{͵JtšÚ˜0ª2¢ýï¯cS!mjè¶é @M”r“9´¨äÈUžÖnáºÍX3%fdI¦ËY&3½+ÖF{Íôg¯ %nœí7KÀèl>šYµÖ®úEtvr•ÅÜôa@»¥—N‘ÿSܘý,Ë7r.ÙmnƒLI9=ã7NÜpå-’W(šÛî7ôíföp‡Ðá Ø­¼ÙPbÝ{]ð\ݳï•t‘2bîשI9‡JMÍk•ÛŒñl÷> ?ô‡€Ž›#QÆÍñc0îÝmÌxØrï>f¶M°daÒv³¶óGhÜ›B¹Í …> Žóô£f6åp—»ÁùX–Ø!ÎyØ*; \¦Àu†ûÕQA”$.3‹,tz^@ˆ£!\Î4®©üµœßÄøK0<ô±t%v<óÚzë¿mWLýwÊ*Û÷¡ò)ÆZšëL;›ç‹¡«šQƹuŽ¥žëŠ® ÖÌÙ›I­?ëµêš)i»äRX@î]‘ð+”éW ?£ÏF‡¶4óDæ™ \~ŸX:DSþ1û€°2î´#b| 2Ä›?[_pʆ'8ãÉF£®â1–¨sµ4ÎŒ[4£û G¡mBÙLêò §Š?'Í’ÌG@2·òc;ÀrëUØ |°„­ {í;Z¸ó™ªù«&ìú@è¸Yùl †ŸÍÀà  ÿèÍÚOê„8~9|½O°åH=} ;7~Á´ xuMBCß Åʤ÷ ÀÊRD€í‚³Ð µ ,n‘§àNN–!ˆ©ar: €m×%V c”Ë=j:ŽÁ'ÁùT´zÚÇ«7â)¡è-b!vf ÿakFåri Îq™ç=0z Ð!/œÚªñ ÇÍÊ µþ}è Ar{»Ùñ±#ää°jÐó,a¾÷(Ãí¶Žß‡AüòÂà+¡ýÕTØ»¬ÍÂ'!ÙJØixÚRPýšï@¨0O,l¹ôþdÇ "¡˜Í½WÕü]ÁÔ^ƒ ê’ÿ`#º>'2t‚;ÄèÂΦöÃ2ö¸µK•NÑäS…Gæ!In4RÈ“&…¬Ó«rn/ÍÞ¯ªeY›WÁ¹Ô¥Új^…Žví:mÜêc¢]m+Dù¾î­p±Z–»XvîÄ%[º¥º=ì]ïò®wi:ìš;8²_Âù O³Æ[ûu,ž¾Ó¬ì×±xö,ŽºXÇâé[èŽaÇ9Aº— u,êµ,™P‰æ+u-aÃ]ÄnŽ…Í> FW9gI[kÌþž ò575Y–Ûoœ‚÷r”°Xº€ý8ˆô ¶@µ4—ò‹7Ì(0ÇÆ „¦`ö¥GëÌ”J¬°ÑyÚçrº òÀÃÒÉù$§O{|ÎwÀe5u³ï/ÖÒòM?€a…ù{Á²PG!ߘJV_ ì!ä]´ÈÛAo£nñ‚¢ôŸ‚.û,œgÂ~.YÌY? ßÜe;cö' ²¬úæÌ£×JÒë„'@ínƒ"¸µvÐÙcbÆV0Îq+¡=kôÚž~¸Ÿ[ºÕPP´kРk¿GâÜ"¥>*_æ2-2•¹Bdž{Œ‡ïTÛ S—wñT /D‹˜G¯êé0Ì ™ÂwWßM‘®.3D_¿Aú#tö#WmUÝS´­ûÞvÁ —Ü6ïò_w&L*HóÿP 2Ó|ao¨+Ü'€~Ü®fTúS@ÿ= ý™ŽÂ?œí.Øà&š~vMíLo2CdÂ^'˜ÂÝ|æÖ/Cn¥L¹k‹œ±À”0/ÀÖô•<Òü¥VŽùkàæ!£±GÕ6½ È±^°š|Œ‚ Ú«°2E Òw›NÐm0!Œƒä„g¶1@ï5+SrF#Aˆ ý5{×—S¡<ýˆ´ï•½ÌêÁgæÇ¨ž‡½ª`žI{cË *é;v¦¢¢÷*VÝÅ”àg÷ª¼|;µ*6öij'<4ôÄtºKΆÁp8> Æ}*ÄjÓ!òšZ–¥‹aÉß`t<ªÖ=ê¾ïÂÑ ÒQ`ÀM(_2Õü$t?tB»4ÍUf ›fÈûÎvRÞíaïeÜàCwï>XWÃ°Ý °}YM‚_ÉLPWÛ¢ÜçeršÞv­J5ñrPÅ×—çACmêÜ#Ô¿KŒOP¦1¹ýhƒÿ½>ÚÄ^é÷ûh³X)„¹úý“Û1¼}¢<¹g:$º§ã+ùTg‰ä+—jxGã¡ÜÍ`W©ŸMu¼tÕF!Á(°.¾t€dœÕÊóš ¡LM§x)7JCB%Ûë-xµÆ—åêâ2 kZNfÿ/ð3µÚ’4û56ô¯œ Hýhl> stream xœÝSMKA½÷qÿ@ê8#L¥¾úë*˜@nšA"DÔU4+¬ùÿê1†ub4q†G÷«zýªª×@ÈÔÖ8Y…{΄u©É`5¡Ôà÷`y†oáE8Ø‚ë0eªµFö\7ç¿SX$‚Dþ»9 Ë­°&UØûü 8w jû¦M|²‚íÑïUÁ Öãrrã Ã@ 95’«À¸ ‡|êÂ"$)vpÜš‰%ëà¨d‰ÜÁU?0R­’kŒ1—Y&PŒ‘SíàÔI‰+9ü:qÔ’zð·~äÕƒ—½¥‚ÙêÑø%ìŒnž¡-7 þyC*nÞ/PÞºùçu^*ú=d^YQ½óE°X3ßÁ‡~¼|I¥“iÌ cúC앜)3Ê][÷[Í-Q«íTôš3«´B3Fõ5Û[‡…¢¤ÙÁY;0m ;Øà,> stream xœíXÝo5<îcÄHÈi‘¸t‹íñ'/H•‰·6—¦IIšÏKJ RÊÿ/1c{wÇ«\hÂ%y =©šxÇãùòüf|-d§´ôë‰Ó«æ‡W^\üÝ\7ZGa¥W‰rDþÙ?¡3yÙìn‰¿š$)ÆhÊúx1ˆ0VJá$þ÷ñ¼Yn5/›tªxõk!÷º п´ÀéÓ+ñbzEaBX,ÓAxŽQ^Ì•˜K¡¤î´ e‚X\5og"´sÙ9c4ø™Øm‘VÁD5Ûí:¼33±lçºSJG˜‰ ÚAªèfâŒmØmçª ¨ž‰óÖªØYЋߚŸh‹ôC[2±[d§¨ÐóȦÜ',¨7ĉ-Úõ¶¸Ø¹ ¼ï¼#[f_´‹?F·¡ŽÑG{ï ¯ãä@Yíú²>ø~¹÷¯6*iŠ‘°#r³ 6@q¹ A"½ï)26(°q&ŽÛ¹Á£œ¥(lĤƒqã½ALë(déƒ :Œ‚!Òn «r€›/ó²‹0ei.ó˜ñ¿Éò½uºfú…h¬Õõ†JÓ”6J5aâ‚,&cÄd¤LÞN´µdò‚i÷–„†&ÚÑ•ÆÕæ,[ƒ!öVUñ¿eÔíaN ¥$äŒúå§‹ò]Ë-V í§õV—Àjé–ÍR'¾¤:A™0lк³> \œQÔw0Ò;%÷LÓN‡ÌÁue [xˆ!»Ðí¥#S° ;päŸMбÁ ­JQ&v0>k2¦£Ÿ1Sâ*Ž ŽòHø;r¦‡ˆçSTVÊ~]Iéw¶ÚJrií›yoë|'ü )g§bA‰¹e Öâösge+Œ…œ*.„@‚Nμ0¡‹È@éWÄ]@–„ŠNZko<5±s± gõƒtôaƒÎˆ¨ŠäÔ*ï&z²ÍœëÀTãRxPës§Z aÎZã Ðì¤ÊÕ1;ѹ_Óëì½Oê¨Å §sér¦¬¥Ûϸkev±i°ÓæÖìqi铲§$%x› ‚h¬(þ{oU:]æÀëk3/.†{À3∘•óÓè÷íCªX<ù“Éãé9ró +—^Õë,3ŽÙE®çÃteÈܹ§hʰíìpx„¦,›Xõd;èw µõ¸L~—n öǺ„ËEa;Ó‘0|ÙÕÁ¡¦Æ1*HNz#U ]Oºèk€æð¹¬tÚI:Eé'\ü°›G‘‡ë€Jôxô?ñéZû ð`wj9Î+P­!öKˆU&†Šì« µg¬‚òªYZ=©fU+1¶ Õr †éÆõ}VçF1I`pX÷÷«›ûrÝ´.Ö˼7¸Œ äj¾f;9÷d©‘Ò–†XKU“/Z'c‡þ\ò~â’EޝsŒãmwÿbè;9GPng¬TÊ®ró:El'¬ªq«nx®Uhɲt<‹-_°Ää!¢òØò ‰œ·nUÔ÷8ˆó“X˳Gö):”÷VÓ¾,Uć|?13Ñ<Tè)í ºØXaõ>+ÏHìåÁÚR뺿bø,u^©ÉØ[fÌ4¦àŠàû‚ÚÏÛg1Rì9®pZ)‰ÏÆ›ùbø V­ÖߤÒ“•\kÎÓ£ªŠõX}”u–L-)§ûnÝ>R‹P²†·ŸCù¡\kgbbª3wéLÎÄá(S÷+Çl"ã¸Ä‘nõnŽX_!—U࡞‹gâ ‘гñíïqÞäB~:°x"‘§xÓÂ`…’+ð‹#­Í1ÒÑ,›•D‹‘>M]†E lCp.&³÷ø…‹åë—läçëeº 0¼D!=#D> stream xœíYÛnGUò8|éǤtUß“(()oÀ‚qwc;G‚ü¿”ªž[õ쌽„ÝEJ0’é×TwŸ®>§ªö½Ò  Òü¯¼¾ª¾½ÔÅßÕû 1)§­ºÊ#ÏÃ?+&ãvxYÜQUÙSJÉùúp1¸°Nkå5ýúð¦:¿SÝ«ò¬êþ¯Ý€lßW±1ü“Èñë+uwCë‚ ŒW›ó< ÍbÁ¨5¨5Yë&xBã­Ú\U«¯êÍÛêç ÍŠ–Ò˜’M<]ñùg ©1¨‚op2é¿Ù\º~o m·¹'+õ{ Æ„n¥žÔkÝx V›•:å!Eç «[õ›B +õ€Ç Èà²^cc´×V>í<ú”èóÚ4c@i±á‘±Ò–5}ë]˜üáœßpèxü®vNÌÓÍoû:£EðÚ#mÚ3úob=h°sD£-àr\Ø$Ìÿµ1ý5Ç4ãÛ¾€Ø¸`˜*6g-˜¼P\\©—bÜ­Ù&ËøAMD“7OQ2¬è±%®X©‡ ¥žpzÊCŸœ¦½^Ôkׄà5ù{žFÍ'%f'¨¬Ô‹øÂ§<¶—ìÒÛ¼œSyRòuáÚkÏ8ÞòˆÔ¹w]Àš`ã¸}^“TЙ@_–DN>/à*¦¾Å·>±×‰QqdsÁíøBuKu.-#_¼°´ð"ÊæWD>=ùŒ ×d&šl°DmCoŽÖÙÁ{ðÐ{ ¬ ©´?ãK K6oj"ÆWy¿’‰<º&B¢ ŒÅnw…*^Žœ=òíÙ6ß¶|ÿr´¾àÐqž‚)©ÿÑœwÉÍÝ5ÅT’ühÜW¢vs…I–xL8Cô1„r,m×HN+órž„nå3\Aª}òeD‚KFžgÒ¢ö._Ð"µ†Šû1wc¨MwRm:lúŸ!¼?I1s[aWI‘Z1Б§…~“÷h Æk0Ì}²èIÎLë´ùʯ¬ÓfVà¸@8U·™€ƒŽˆ¥b=ç1Ú˜ÂD9åJççð»í_Š™ärY‘m׬™ãeÅPrv[aSÚ<É¢ht˜Ø_N¡kµâYN_W¢RÚñQ%ª¬üåVŸ“EpFÆ_‚&!°˜§töÑÁ2Üb.ùXFC/yR<‰Ž/yT“rûæÐ’×í®Ð¼ èdbôn¼D9ïRcHÇŠ)±°ƒ€rGm;1xËã`\—cê m&Š0V' [‘/j—ŒŒë#€èîÇÏ»Ãýé"m/7vÕEIÛ’lÚL„Š©x ™,HÓg¦V©²š)ÜïPZÉÇŸ¦¥Õ ÑýÔVR# ­?»•'ÄKj`QèÌ4 sÑ#[nûêÉ~šàYÈ m¾$[8ÑOë–Ž8¿Û©³»œÍŒ6²6ß¡[.U]Á‹åkß^Ç ¸MÈ_‰[ÀmÒœqZÀ»ÝÞ_¾q"”²¢™kŶÉ4tñD±5Ól½(Ÿ¤8È~œŒ–±”*$£üÖkêuiZþÆ,÷ýŠ•î—dëL|Óc¿ œÖœòÕžƒŽÒ¨íâIæC_Îùsžóþ’œ?¢',—K’"â6±6&gJÿ‰ÉYbê²Lß,äeÍ=¸ù¡È*¶jÓVÕÞ½ËîS´) –”¾¥o£¥ò-;ÐùŽ'ËÇsíÂ)pX€0)ÄË r–Rĺ vaËÿÐX*úû<‚o®ÊÏÁòÝPt HIMà!D9Ù!ïUÿHH"endstream endobj 89 0 obj 1590 endobj 93 0 obj <> stream xœí}[¯fÇqLxùH©X Ø ‘qù¸ûÞ †.¦ãaLÀ$O,À¤‚$cÈdÒ1ò,=ëÁÛ/²ˆ Ä~˰‚ô‡R«Vïoï1I‘&3æBHk\ìÓ—ZÝÕÕÕ«zÎ~÷¸œB<.øwî]^|­¿÷'‡w1ŽcYòñÚ¥ ñÁ!· S|ëðŽï¼§1F Ö×{ß;w‘˲ëb¼÷LJû/^=„#þµ*!,㘊µH%çcl9ÌJý”ìŸã;ôã+VÿåÃrêµ…Þï^ûWБöúÁ¢óÝÙwò‚½|ïúø­+›‚qÌý4êñê¾c2H9´ãp¼³ÃO1CÌ'çêúðÆÅ1]ÞYN5öQÇÅññ© û¯‹ãº¼“N¦~ðï.ïÄSèe´‹ã¿¹´©ª)—|qüC“s޹šüG&×ÐóÇúËbó‰¶Ë)ÖšSEùVÿ{¨ÓS¬Á>ËÏYQJiýâøÛ«õãÅñ;ÞÿXLÿpy'˜:­›üoÑ´Œn#½õcJe©«š±ûÁë€{[L¾oå)õÞ“7ˆ§j°âªZZj‚Êe¤S+á»W/^º:¯:k]þO»¦©­D<ýÿ…øÇ-6K­ãѽb›îcì±÷5{­–¥õmS˜°Aª9ʡ؎£Ù’µqŒK”îâX*å&N9-m¶¢„¾î(sV(Çż‹·ŠÞ/úг_ŒÙ¯ì²kã­¨úºàõ‹ù𰏳ù›×ø¬ayëìò>Öö²½òÉúØí¯%Îýs; Û`©šYq‡ýÒe:åRºxó=ß Ù¶ßÅÓV:ZÍåâÍw.ïäSê­µ‹'/û©›Ñ–‹çMZb[ÂÅSÛ·FOn==£/±„/~å2Ø^ˆ½>ZÁ Øfìgc¶úcT³Ìâð ˜àÊÌåœlnƒÂTÇP)ÙgÌgò³,–%Í%®­\ª¬ërk6=^s ¦·ü¹÷;Ø—æ5\ouÖëw\µÉHÚ§Ñm_Qû³v8íPœ:×ejäÒÔÈeïÛkŽ3º©ÑGhñQ³cZ~&–“ÓöÞGñ‘CjÝCݪ[ŸµúÜC_½,§ŽH{ȶŽ„á;'ZÏÑÜ·•¯U¾ð•Ÿ¾§–mk”RO¡ÄÝ~8oƒ³‹ª˜*;mjÒ§ü`ÊÝæN‹~ŘקL;ÍÑN1ï3W?f‰u¾ubó>ºÍ«gÞa·‰é½) [ämšÖ§Ð KþI‡~ýÃ'Ê•ˆÃ;lS~0å¨Ù@TÈhk¢¶NÐy‚¶‘Ú&h› ­KÚ®ƒÌ‰úäzùÞø„CßÌ>° Å|¹íƒl³Á}ð¥³ie³ìÝÍ¥­¤ü¹ô©mÇ|gÅ’í0ynûù—ß·7VßÓ0K~ª{y¥œé#¶òHÙë—ÙÏ”ÃV'.[[–×]ýækaÆ€ÿâJÖÕ>˜å…–ô¾¨*e«9<q±#¢È 4ÅB„æë•³ Ù¼“œ-œ€˜Ñ3¸0<ÉÔÂ#Ê w54 ÞýöéÝvêcºìjx+Ju¦>+ªÞd°”#ƒ%»“¬ÁÒÓ0ÅÒJífŸר†J#<9}`ŒóÔ¥Y°_1v=ü“I‰Î×ÏKÊÁNÉT Ä&«á<\ü(ARì3HZ}‚¤¼ô@~ª{y¥œ×KÞZ){ýà2û™rØêÄekËòº«Ï )/kˆ{=Wimá$Ê ’h—ólóÉw2¥E^'|1‹;Fëã¹ÍÍáÌõ»¶:)âÿ°4Á~ÚÀ†ëîáÅ»¯ÿë{úLJ¿c»âðâïúŸßúýoÃáÅß~#–ï^ü¿ýíß±ÿüýßüÍÿµ-æáÅoÿçzýΟXåßú­ÃÝß>.?üÚ3ß|âwŸ½ý}üñ·ùg/ÿÙñî·ì¯ÿòÞþ»í'·ÿüî×ïš|÷ö^}û'=‹¿ÿÄË?»{û§?~ú¹»·í/ýäGÿåo=sÄ¿a?¸ûÚ]“ïÿâs߸uxéîñÕ€T†¤Ô u³t5H¥ÈAêrîa9÷€D¤¤.çÂäüCX²œƒ`’K “ý£‡)ëùˆÐô|D\ô|DLz>"6=‘=‘’žHUÏG¤¡ç#rÒó¹êùˆ<ô|DÑâ*Ã’ÔèW‡¤E¿:$-úÄèW‡¤E¿:$-úÄèW‡¤E¿:$-ú5øÃ-ú•˜´èWbÒ¢_“ýJLZô+1iѯŽIŒ~%&-ú•˜´è×À¿£ç#ÄèWbÒ¢_‰I‹~uLbô+1iѯĤE¿:&5úÕܸý Hbô+ ‰Ñ¯I~$1úÄèWƒ¤F¿’ý Hbô«A’£_“ýê˜ÄèW`R£_“ýê˜ÄèW`R£_“ýê˜ÄèW`R£_“ýê˜ÄèWÇ$F¿“ýê˜ÄèWÇ$F¿“ýj.O~$1úÄèWƒ¤F¿’ý Hbô«AR£_IŒ~$1úµ6=úÕ1‰Ñ¯µéѯÀ¤F¿:&1úÕ1‰Ñ¯À¤F¿:&1úÕ1‰Ñ¯À¤F¿:&1úÕ1‰Ñ¯ŽIŒ~&5úÕ1‰Ñ¯ŽIŒ~&5úÕ܃ý Hbô+ ‰Ñ¯I~$1úÄèWƒ¤F¿’ý Hbôëzô«c£_ÇУ_I~uLbô«c£_I~uLbô«c£_ÇУ_“ýê˜ÄèWÇ$F¿“ýê˜ÄèWÇ$F¿“ý“ܧ·’ýê´èW@£_’ýê´èW@£_’ýê´èW@R£_‰I‹~%&-úÕ1‰Ñ¯Ä¤E¿“ýê˜ÄèWbÒ¢_‰I‹~uLbô+1iѯĤE¿“ýê˜ÄèWbÒ¢_‰I‹~uLjôk•ûô–C£_«Ü§·I~­rŸÞrHbôk•ûô–C£_«Ü§·IŽ~uLbô«c£_I~uLbô«c£_I~uLbô«c£_I~uLbô«c£_“ý Ljô«c£_“ý Ljôëûô–C£_‡Ü§·I~rŸÞrHbôëûô–C£_‡Ü§·IŽ~zŸÞ"&1úuè}z‹˜Äèס÷é-ǤF¿½Oo“ý:ô>½ELbôëÐûô1‰Ñ¯CïÓ[Ä$F¿½Oo9&1ú5E¹Oo9$-úÕ!iѯ€$F¿:$-úÕ!iѯ€$F¿:$-úÕ!iѯ€¤F¿“ýJLZô«c£_‰I‹~%&-úÕ1‰Ñ¯Ä¤E¿“ýê˜ÄèWbÒ¢_‰I‹~%&-úÕ1‰Ñ¯Ä¤E¿“ýê˜Ôè×"÷é-‡$F¿¹Oo’ýZä>½åÄè×"÷é-‡$F¿¹Oo’ýZô>½ELbôkÑûô1‰Ñ¯EïÓ[ŽI~-zŸÞ"&1úµè}z‹˜Äè×¢÷é-b£_‹Þ§·ˆIŒ~-zŸÞrLjôëûô–C£_‡Ü§·I~rŸÞrHbôëûô–C£_‡Ü§·IŽ~zŸÞ"&1úuè}z‹˜Äèס÷é-ǤF¿½Oo“ý:ô>½ELbôëÐûô1‰Ñ¯CïÓ[Ä$F¿½Oo9¦Ï>ýúêa9âß÷¾wx÷ÐO ÿxÁ^¾w}üÖÕáÅ×1Ÿ–|¼ºf§1F^¢9Þ±éå4âqäSíÇ«ëÃÏ_ÞYN%–âÅ“—Ë)Öb¼øÊ&~5Bo6™·.mÈš[¾xöòN>•Vj¿xó=+í¡Ærñæ;(N½µf½•RO6ýß½zùªø{‡«Þ8×êq\üê¹ínô§.ó)ÔÔóÅW·Â[Ô£ö:.~Ñ+äTMÚ ¸võ(¨µô‹[_Ï^¦Óh5—Gµ|é s}êËÒºYŽýkSnC棵ëÇëUŽåzO&-õ†ýáÒ½ƒÅ¬³ôÉc–G»s°Õ”¬/« 9 Ô¥ÜÛÑ[¥ˆVèË¥{ÃåÙeׯ[MÉþ¸w¸ÿÂgN巯ߘ‰ÇœN=ãå»Ym|Új®ùlÌ#¤Õ \¦ñ•P«™ ¦™mšÆÖN=…³qj›’± ›Ùh–D³äîrª‹Ë9CN.—pŒ}°~éY§È‹Ë u:g¸¡NcŸuì‡êÌU¶½øêE&öÜ)›>=ùX:Ûfs9.ʦg§þ1Yy£n1Cn²éЊë‹õÓf?uˆËìÄ,„úG·¶Ù¶Âš¨§•1Á›œ¨§¹k¿«¹mýÜgéùÏas>#æ“uÌ-×6fó¿¬6fëÂuX öékÚzÛÖ:.g;éeÚUÙìx±\Ž;û ;»Zvö¹­Îhk?i‡Ù¿ÙsZmҜʴCì…Jë8úûÔû‹ó ;´}4¶=5÷,ö~˜v‚³É&qµ7s¢\#ø0ç0ÁÌ94`Û}îý ?Ð×5uÇïk ¹Ò†á»â´Õ™zâxŽ‘v2|ÝBáC¤ï‚åœ<6_‡³/%ÖŸ?ûÉ´úX›Ê²ù^÷«<›–Ð7ÿL?LÙ}¦û7›â¸óçð·%ÒÏ£ÏÂ~0çëY5ψàc­gÆjÞ€LçŽãê<›p¾§Ás1Ižq ÎâfRqÆ1†Á|æDyà|$ƈõÍ3NÀúæyîcns›çlÂ9ËXÅÏÜy¾ã,.ažÑ Îëyv£<±ÌáüX™Éó\† ”y^CÿÒ9Öè?„]\9µsZf?c‹UÐOå¼yÿ•sqž¶%žõ¡Ï49"Fbÿ uûAøÜÆÄ‹¸kÆ…){<6ã·Œ˜ó?ûåãEÄx\;¬{'vÛH¹v8—;Ç ˆ©zgêñ$û„þkœ™=þdýìq)ûIe‹Qa3cÆ-87×86ìbÚà1ð¼xº+g?kÌüÀ"}wPæeî&'L™'sm«|ÿ€»—]áìÀ´f~)°&ð> ‘7eF¢<Ý÷ò’8Òw^8¾ó`Íà¨Ò ó§~í鯯—þ¿üõ½ý·œxøòÏ^)çòÛß¾ýW(½ûÄ3øöOþæ‰YéÇ?½›ÿòN|ýíŸüèÇO?÷ïß~ú¹—ÞÍ_ù1ȃïßúO|îø~zc©7L>^8a¹a¦ð1ÃI7L>f8õ†ùÁÇ gH¹œ©Jpª”+ˆCʤ(å R‘rˆî…àä(å pOR‚Ó¥\A R® d)WPº”+¨AÊÔ,å È‚ÓWPº“ãpt˜ÀbrJ—brŽ“ãpt˜ÀbrŽ“ãpt˜œÒ¥˜‡£Ãä”.ÅäxzY‡Éq8:LŽÃÑarü-€“ãpt˜¾ÔrBLŽÃÑarøœGÊ19x@-Ää8&p„˜‡£Ãä8&g,RLà19G‡Éq8:Là19G‡Éq8:LÿÒ›”+brŽ“8BLŽÃÑarŽ“8BLŽÃÑarŽ“3%&§á¯Îë09„#Ãä8&‡pd˜‘arG†Éq8:LáÈ09„#Ãä8&‡pd˜‘arŽ“C82LáÈ09G‡É!&‡pd˜‡£ÃäŽ “C82LŽÃbrð‹‚„˜‡£Ã䎓ãpt˜‡£Ãä8&p„˜‡£Ãä8&Çÿ®“ãpt˜‡£Ã䎓ãpt˜‡£Ã䎓ãpt˜‡£Ã䎓ãpt˜œ’¥˜þŽgWÐ¥~ã1áè09]ê7ޓӥ~ã1áè09]ê7ޓӥ~ã±ÃbrºÔo<&&§KýÆcÂÑarºÔošÆy‰†áx¿5ž þ¢f<^]Þ¸øµK+H9´‹ç/ï,ö£¼Ô~q rèm„tñæ;—wò)õÞÃÅ“—Ë)Öj½xj+=^öS-Ž‹/m?ò\¸ïø™Ë²,ß½zùðÒ@ú²´Žµ`K9¦c\F=^¯²ÁHÙ®ªaØ/|ûÒ½C¶þ(?0ytʹ´ÙjJÖ—Õu¹Õˆº.÷b5Ð*z+ôٯٯì2µA+JèëÞáþ Ÿ9•ß:¼~c¶”c; ÿu§ÚiNOÃ*J+¶ÔŸßÛÐ{›aüÊ¥›à¨ZS>…šR\{È5ï¾·¡ìàùKSý´,cg\eŒj»~ge°±ŠÉÌ6-×S޽S€Éj¡R²‰mP¶EX–DÙ&`¶¢”½.åmZ¼&²}c_Þoó¾8š×p ÐjÓËíj9™ÿm)Cû4zÎSû³v¦}ËaÕ¹.‡KyŽÙûöšíŒnjôZ|ÔìÀú?“ ‹¿ÉiûG߹^>QŸpè›Ù%tGjµÙà>øÒÙ´¿²Yö‡nŽfŽ2Žn.}jÛ1ŸÇY±dÛÏm?ÿòûöÆê{æ`±õv~ª{y¥œé#¶òHÙë—ÙÏ”ÃV'.[[–×]ýæk‘÷Z\ɰzÃk9-é‡ýW¨ t5àƒ²ùÁÑE]ªË1B..§Ù}åbš]nø’ ì#+ ex wï3ä9¹\mK·­ZìýèPš÷½Ÿêåæc!³<¢ŸÂr胛9䌶ÙûVÕ¥QFy .7È‘m;ú Ýåþƒ×I>.ñ¦ÅúG Ù®˜&³N@ywŒ¦œÉ­R¶9ÉuP.#e›O®À”s£Œ~2û´ýrb„±"ë#˜;Èåu2ÊêV0ÖÂ:6dHƒú7ÓÍ"kÊ2õÁ<§©ópÙç6› ‰sŽ!C*Œ¡O¢þ9›þ‰sž Úf–7Œ›XÞQ'y?¥CvÝ Ö1WÉ.û¸¥¡~`ý~‚ëY1ÿÉtª ú,ƒr†Üwr¡ =—´«OÙÌÚäÌþ½¼rÜrî§ 0JœÛ‚µNœs®mKn216Ç•8è?uÎÚÒ³ëY0W…å®ç6zžóäóÖ¸Ž>'6PQÚ[>c¡Œþë@ÿL¼´Î[´#m³7b¡­¦iŸ¨“¦Ý.;û\6»…þ™{ö_YkšÛ¬ƒ~ú²íî},ƒí̽†}G_±…>'šk˜xß§¾¹¯kÅžõõŠ˜çB#ì°pÎcªÛÞ®Õ'@ŸBý#ì°p΃ïýJX¸gCqÿC•Ý/ÑwaA™¹s¹Ÿý^éì˨g?Yè7ÜÚý~µ¬~rõ·Áeúá)/«_]åL9¬>yóÛðõ¶á3wØŒœWK¯9ºÖìëû¦mçI¨\<ð0™çAAÛH_Z#Ûb?Tîó€ù­\ƒ›¨~^ÙÜ¡œ~0ÀFëœ_Ø1b_œ1´ã€=À ÌLnà7ÚpýÌ=ÛZq]íD 3fØÿmžX3¼¶q[1¹/ó<€L_ÍTCŸvcî0ôÂúüN›6j:t®«ŸƒþÈmz¤p¶õQ–ómÛcîO3a³»ó¾²3m'ç±î=~2÷d\úX÷j ³mÂ-ˆsžAú¸™æ>, dˆi¸çVýo'‹í+ÛÔ¿C.ìg@æìl€<ôYH‚ÑN<žKìÇ‚7“§¿³yމk£/®çâšÔXLÂôY%à7Éñ¬*v˜:”³G&¬ÃïsúhÄJ‰þÚ¿'çـׯôSðõ3>ò3`“=ÎZ69Ï3:äzœgLœg0¶òy¬Ýõ©èŸØKƸó¬òqçY‚{]âþ//¦>çØió§ÊswÃõlȨó?'Ì'}Ÿ³-s7cšü†ék„Ûf£Mú×cÊyž%ð·Ü#3g9côtü¹gIø³dùг;ÌŠ®wŒŒŸ%þ7 ÛYlÌ_âv1G&OÄyn²ë„x3vCí¨¶ÖÝmÎ,\¢ûÊXêøú™5£Ü÷€-¥í‡î>ךážè6a2±qÿ,T²q»Ç fúØGîwlx”»m˜Š¨ïg³ѾÉÖýÛ=[÷µû‘¹ßå9Söý^wå}WŸ¼3f`…hM¤Ö’{!jú| ük¤}DÄö&êç2ýKtÙçWS“]c¸½zhaÛÓÛæäùŽtÆ?"}“û,ΗŸáÃÏüX°NÃïwW…HµˆÉëxÿ; y#Bb“½\}ä™fKбèãjÇÖñü‹ß§ì¸B9×á®/4dØ:ãJçÝ¡CvÝýföòBÙ¿³˜™ OŸi“¡sg[ÄV£³-âšÑÙ1Âèlëxés›ë?–M¦­Ìò¸“Ù?|ýi'Ïþ1'£¿_îG;ÌØ¿AM íìQ9ý¢âr†L= jb|Á­¦…vÒ:êÐNš©k2ÛšZ‰~3Â_›<¸ Ó6àÆLö:8Žìö>{FĽW½¼sbÜÌ3vA[÷én‚‰1‘›‹ê¬SQ‡{xtà*ç{˜]®æ9úuòlЙ>kiÐaÆH.ä pÇJËz¯Âœ¬|š×aŒ0ãñès>ïéãöy'ÀX}Æþq“s8×{[ÛfŸCîf\ÑM&·SòY‡âÛŒÙ×äpêܞɬƒ$ÊÂ3©èä%/§_®açĵ¦¿† œk”àXLf¹¯b~xná4™çÍâ6Æs(œm2ÁL{NØÞsø¹kòmÞ–w”æ6Ì»xóudÜÓÜfæ«·Õæm}±^¼gƒ.°µæÌ÷ç¤ø>¢=Ð~ˆwÚÕ™‹[m‰vØòfŸóþJ;¯›=¯Ül=׉»¶s_p¹=0>?qÞkÉu›ñ»ëϳ¹³Ý.Ô™<ŠïÇræÒ·ýžv~ lþ!»ßàïþ‡g Žß9Ï‘kÇ3¨W·sžýeY×Èü•¯}T.ž}=„u}Íס~àY3êj&{úa߃<˹¿flákij¿9vÞ-Z«;ž7ßÞü\ v;mõççsrÝɳϰ;.ÏóÈ÷×zNÝù…ýÕwçQŸg–Ÿƒl[|Ÿr,î#b)迱­ŸÝóÌÍqwûþeÛ´?¯}o²mÆÂØk`,÷]æn½œwד1™Ç‘çøõ3ë,co´]Rvñ‰Ç-Œí‚¿Ô`lÇxÆõaœ3c!ܵfŒÄ¸ˆ60ã%¯Ÿ|NhŒ¯–ºÌ¸Ðçg1ã´5.ü1¢ß"®3F\ß  Ÿ!ß=÷̣ηž,ŒþžÅ®!ˆ!;¥ì§o|Dö:é;/ßyäM—5 LtÞè›®¯ýϧÿþÖ¿<~ãá­gìg^úÉî~ó Ëu÷ößæŸ]}Ÿÿñ»_û×Þý©Éíüà—Þýñ_°ü‘‡`?øƒ‡oßþïÞÑ÷ŸxùsÇ'ÞÿN˜À˜ÊaÊ7üÐóŸ¦vï=ÿ`²£QSÒóH6ªaBTSÒóH Ëaz>Y.9LUÏG ¹¡† 並¢ç#êÐóÈHÊa*z>¢u=„«¦¢ç#ÀʪaBRUSÖóC³ô®&=ÎÒij˜‚g™‚g™‚gé/Må0éq–)jq–uIr¹OÇ$–û$&­8‚˜´âÇ$–û$&­8‚˜´âÇ$–û$&­8‚˜ã±Ü§cË}“V´rŸŽI,÷ILZ¹ObÒÊ}:&±Ü'1iå>‰I+÷é˜ÄrŸÄ¤•û$&­Ü§cË}“Vô8KµÜ'1éq–j¹OÇ$–û$&=ÎR-÷é˜ÄrŸÄ¤ÇYÊå>ñ 5ÔrŸÀ¤–ûtLbqD*z¹OÿÅubq„c‹#“XLj¹OÇ$G8&Á8B-÷ Lj¹OÇ$–ûtLb¹O`RË}:&±Ü§cË}“ZîÓ1‰å>“Xî˜ÔrŸŽI,÷é˜ÄrŸþËeÅrŸŽI,÷É_,ç#ärŸŽI³”Ë}“ZîÓ1éq–r¹O`RË}:&=ÎR.÷Y›^î˜ÔrŸŽI,ŽpLbq0©å>“Xá˜Äâ`RË}:&±8Â1 Æj¹O`RË}:&±Ü§cË}“ZîÓ1‰å>“Xî˜ÔrŸŽI,÷é˜ÄrŸµéå>“XîÓ1‰å>ýƒ\b¹Ïõ#cr˜ô8K¹Ü§cÒã,årŸÀ¤–ûtLzœ¥\î˜ÔrŸŽI³”Ë}âìj¹O`RË}:&±8Â1‰ÅÀ¤–ûtLbq„c‹#€I-÷é˜ÄâÇ$G¨å>I-÷é˜ÄrŸŽI,÷ Lj¹OÇ$–ûtLb¹O`RË}:&±Ü§cË}“ZîÓ1‰å>“Xî˜ÔrŸŽI,÷é˜ô8K¹Ü§cÒã,årŸÀ¤–ûtLzœ¥\î˜ÔrŸŽI³TË}¶ärŸŽI,÷ILZq1iÅŽI,÷ILZq1iÅŽI,÷ILZq1 Æb¹OÇ$–û$&­Ü'1iå>“X´rŸÄ¤•ûtLb¹ObÒÊ}“VîÓ1‰å>‰I+÷ILZ¹OÇ$–û$&­Ü'1éq–j¹ObÒã,ÕrŸŽI,÷ILzœ¥ZîÓ1‰å>‰I³”Ë}Ö¤—û&µÜ§c‹#“XLj¹OÇ$G8&±8˜ÔrŸŽI,ŽpL‚q„Zî˜ÔrŸŽI,÷é˜ÄrŸÀ¤–ûtLb¹OÇ$–û&µÜ§cË}:&±Ü'0©å>“XîÓ1‰å>I-÷é˜ÄrŸŽI³”Ë}:&=ÎR.÷ Lj¹OǤÇYÊå>I-÷é˜ô8K¹Üç(z¹O`RË}:&±8Â1‰ÅÀ¤–ûtLbq„c‹#€I-÷é˜ÄâÇ$G¨å>I-÷é˜ÄrŸŽI,÷ Lj¹OÇ$–ûtLb¹O`RË}:&±Ü§cË}“ZîÓ1‰å>“Xî˜ÔrŸŽI,÷é˜ô8K¹Ü§cÒã,årŸÀ¤–ûtLzœ¥\î˜ÔrŸŽI³TË}öØärŸŽI,÷ILZq1iÅŽI,÷ILZq1iÅŽI,÷ILZq1 Æb¹OÇ$–û$&­Ü'1iå>“X´rŸÄ¤•ûtLb¹ObÒÊ}“VîÓ1‰å>‰I+÷ILZ¹OÇ$–û$&­Ü'1éq–j¹ObÒã,ÕrŸŽI,÷ILzœ¥ZîÓ1‰å>‰I³”Ë}–¡—û&µÜ§c‹#“XLj¹OÇ$G8&±8˜ÔrŸŽI,ŽpL‚q„Zî˜ÔrŸŽI,÷é˜ÄrŸÀ¤–ûtLb¹OÇ$–û&µÜ§cË}:&±Ü'0©å>“XîÓ1‰å>I-÷é˜ÄrŸŽI³”Ë}:&=ÎR.÷ Lj¹OǤÇYÊå>I-÷é˜ô8K¹Ü§]ÛårŸÀ¤–ûtLbq„c‹#€I-÷é˜ÄâÇ$G“ZîÓ1‰ÅŽI0ŽPË}“ZîÓ1‰å>“Xî˜ÔrŸŽI,÷é˜ÄrŸÀ¤–ûtLb¹OÇ$–û&µÜ§cË}:&±Ü'0©å>“XîÓ1éq–r¹OǤÇYÊå>I-÷é˜ô8K¹Ü'0©å>“g)û|õ°ñï{ß;¼{è§„¼`/ß»>~ëêðâk¡C>-ùxuÍNcŒ¼Dr¼ƒ™)§q÷÷ׇ7.ž¿¼³œJÌKíO^.§XK¨õâÍ÷.û©‡ÇÅW·Ò§.ó)Ô”âÅq+üÒ¹æ®ý¾Ûg.˲|÷êåC¨§ÚW¿w¸zá‹_» ñ”rh·P7ô6BºxóË;ù”zïáâW÷ŸK?¿«ýÜÇWø T=ËŸ·V©æ)ý⹯g1pi¹æGÔüâV÷Ë—¥ÔS( _ºÂjú²´Žø×Í*æcêK8^O¹-þP{SM?9Pºw°ÿ×(?8¤s k<[QB_VrÎ6¥¦\ª­1Zeo…¾2ûõ12ûç®·¢„¾îî¿ð™Sù­Ãë7·IR9UôœNsìMãÙËt­åñˆ©îmãW`Ø%Žºß;þ%ë!—”ãj³%^jv° >“ Ó¿ÉiûGo¤¸ßEaÝD­žë³å“uí»è«—åÔc¯ voæZ‹Íw‘É`¢|­ò…¬üôå8µ¶ÖÏñºgÑ0¤ìËf³|mÞ©Lùålsâ²O}÷òìÊeÚiŽ¥uïÓ| „Y‚ÎÏ ó€Ù´ ò1ŸóàRéÇm7­O£–ü“ýú‡NTr—ß©Ð2å”sª®P­èˆŠ&W }̉Z;¹F'¦mꉚŽi˜¶.e³™mNԧЋõɆ¾™}c=aÿ¶€ÿçûàKgÓþÊfÙº9š9Ê¥d3øséSÜ1%!|±³f±Ýl¡ÐùçïH¦ï1ð6 ÆseöcrØäÒ(£N©îËè·¦Ïub™>å\Î~ÎcaþSrOÅÕK«|°–Óz> b2û-x‘t=åð©<û£„ŒÈÞ~ä›X—Ýø+åÔÆlEÉ_7(Fû1¹.µ½UõV諲_£²_Ùeׯ[Q/ô5#¦Ï–Ê71YLdúD³ÀH#ÿà€fRÑ|q}¾º•}aŸaÕÊÿÃЩcª«{ Êvo¹ŽÅ¨8/Í\]²e £Q†Å[ êrŠy¶¢TY×åžpEÍšÞ~øÏÙïð¾8dj€V›^??tZµC `^êlxâ€T爽o¯9ÎèV~¾5;ØŸÉdètsÓvS¡“Ùs<ÖRNñãDN8þI"'œ‘Þ<åSÑê3?¼ÜëSþÁÚ NåÉLƒ§º9Ì‘áâ\Âòž™Á'׋Á'úçDNîúǧü`ʽQ¡ŒŽ¨¨×§üÑuîgØ_wmÍ>L6·K )Àm9QŸ\/Ÿ¨O8ôMEN¶ ìP)gË㋜èzp³²;Ê ìñK€Ü)Gºˆ­Ü#ÌYßçyöC9õ­Njç¶³¼ïêFQÑW2­ÎðÁZNK:¤+£×ëUfÔVì²—s딓ÉcP¶@Æe¡äHÙ)–DÙ–¢0â.‹õY—BäLt9”ç)[ýZye±@Û ÚûÉÍê´ÅÇÊË"âAÙ"Ç–\ÏŒø°Ö·î¬ ë€jóq«¤þÙºÈ=DÊ6nŸú,S[7¿Ý|¸Nr¡Ü WÖAÔßb“;#âä2뀀¡ìÑq‡Ìú‹ËŒŽÍY˜Ìè¸×Mn2#ePKcáÚT“YǦ>0mÂævÄe=M¦MãH”qWÆÁ¸:ãh®gÆÁyCpnËÎúv¼Øúù¼‹ÊÂþC²Ý¼dÖf— #ú`ó\ÚOhK¼n2ðªºÀÖÏ›œLæ:.ao›²µ œ7tg¦7eÓ¹—ó†±TÔḋ¼…Ä eÙ1oå±RFgÛ±•£Ïµ¾¹³’h‡· Ê ãr¬h?KGÀ~Ì7›þeÃØvØKÙæ„öBÀü4ÊóÆ5Š˜gÚX0hóœ¸óÏúsžXÁÆB‚Åñ—Ìõ­X_®K/Xwê0`cÓ¿àž5 ýöõตŽ4í 2÷NľaÞêÊf“c-,÷qÚ¼­oµÚÿ´m›ȼeFØ!×.Á¶ç¾H°ù>X{¡÷Êý˜±ég›ËÜwÀØçÍu@&®¼@Îô-¸ÅöHÿŒóv$wê“1nã¾Î¸Å6ž… ÀÌÿ°~…Ïa`±ý¤õ ?FÕAJ÷¬ô·6ŒùÃ)ƒÐ¦ܪë2å¿Z6?LŸY sÉô½±mþþ§pñ¼ûìÏSÜü¼Ë܃¬“â¹mNáÜç¹¼nL‰]ȱ3H(qWÕiÝ^ŽîûÌë“ð©Tœ.£ù©ÃÝè½åQÙë`¤ï¼p|ç‘D]ÂYc?¾áäã¯ÿï¿_~ø·þìx÷ ûã/ò£»/|ãû·Ÿ~îîí¿É?{å3ëöòÃWnÿù0çv÷µ·òWç?~øöí¿¹mÿuûáöJ}†)¿o¾p7?|åo=sÛþëëŸ{öÞŸx$žrÓ¡7ž›~ý¸ñÜôèÇŒ§ÞôãçÇç¦>?n<7ýèù1ãi7ýàùqã¹éÇÎÏM?t~ÌxúM?r~ÜxnúóãÆsÓ›3žqÓ›7ž›~Ôü¸ñÜôƒæÇ‹ÇîvRþ ,7ýùqã¹éGÌO¸éÌÏM?^~ÜxnúáòãÆsÓ¿há1ã‰7ýK7ž›þ Ÿ8Š? “ãâG‰G‡%~Ôññ£Ä£Ã?êx„øQâÑáG‰G‡u“ãQâG‹Ö÷™ˆGˆ-Zßg"!~´h}ŸÉñ(ñ£EëûLÄ#Ä­ï3?Z´¾Ïäx”øÑ¢õ}&ââG‹Ö÷™?Z´¾ÏD“ãQâG»Ö÷™ˆGˆíZßg"!~´k}ŸÉñ(ñ£]ëûLÄ#Äv­ï3?Úµ¾Ïäx”øÑ®õ}&ââG»Ö÷™ˆGˆíZßgrññ£]ëûLŽG‰íZßg">±­ï39!~”xtøQâÑáG?J<:ü(ñèð£ŽGˆ%~”xtøQÇ#Ä?J<:ü¨ãâG‰G‡%~x”øQâÑáG‰G‡uññ£EëûLÄ#Ä­ï39%~´h}Ÿ‰x„øÑ¢õ}&ââG‹Ö÷™?Z´¾ÏDññ£EëûLŽG‰-Zßg"!~´h}ŸÉñ(ñ£EëûLÄ#Ä'v­ï39%~´k}Ÿ‰x„øÑ®õ}&ââG»Ö÷™?Úµ¾ÏDññ£]ëûLŽG‰íZßg"!~´k}Ÿ‰x„øÑ®õ}&Ç£Äv­ï3?Úµ¾Ïäx”øÑ®õ}&âùL󉯖#þ}ï{‡wý”ðìå{×Ço]^|-´cȧ%¯î£ÙiŒ‘—h Žw–cÎù4ê±äzêVãúðÆÅó—w–“-x(õâÉËådK¿´~ñ•Mü·`ù2L¾ÿ©çn Ÿ»œNÓ1}^,ô¨_ÚDî¨ǺÏòRûÅ3—ve9OU*x³ŸÆb ›ÜM®PÒäfrn”Í¥X(g“—Dù¬]^:΢@ÙêØh”“ƒ÷‰T‘Ç.[yh”ÍÁLäfý¶Í-š½Ï\çïÞ‡\¬ÎÒ+eëg¡Î9[ý¥°ŽÍ»ÝeºËÑô_"˃ÿN4Ö7óÞOø»=»Üýï9&ÊæŠG¥Ü Ö©³Ï‰4™cYGY?BŽ,÷¿;É9L>nôy‹£m²ÿþ9â5›—ázÆŠúÁõŒÅeÖÏ–Ûð¹sn#Æíƒýøßî‰ò¾‡2̹Wï' 3ç^|΃sBnÎ)4³²¯”Éœ·Ú`9°·æCI@XŸ[fèÓb¥lcµ…õ R®}¼EnÔañï˜MÙß{sU—)#Ô;eÓ¡T×yñßÁœ+eÿž`¦lXÊ(Ãÿq®¬¯-¼Ëã\Žù_ë7ÿ½2…õîèPû6ng}jX(Kb?ÿFØ06ÊX—:ÂyNÛ|ϺÑ~BÂ]f}ØÌjÛ­žíÚyÛÙ?÷Èâòà~œî#—¹ïRÝöZ†LK2m&ù¸…uô¡m$ØÀ ®4 Ó~òâ<}N€á¼åßB»Í•ó–|Hay†Ÿi¬_ÐvöYüïz²ŽéSÂô?îÓrÜ|÷ }`§jê3àKã”ácóô±þvú^üÞ~îq÷ÉëbãïüvØùó¸óói{v‡œ6Ÿ?f?}W>Ï w 3†¼ž»$Ó‹crrq+¾ÿèqì¾/øúÙM,ìdÇî÷MFÌ#Ø gΗÉ8v3㚘wuü(”Óù¸7ãÝŽiØÓz|‡ý±ÞoèèOðÀóè732¹²5vh¨D ZzŸ /Z¡wrÞÕ²¶¼OÎýS;ìÄPæXè§°ŸŒþ ëPÏ)cVJÝ•‰þ#˜ó;þ2í1úÞŠ GÀ¹QâlŒ½šorÞÕ¼Œ­ÎRÏý„ÑÏýã@rè×1ëYvÝÒN.Skxm¸&h O3µÀÎ…½âTˆï;y@f¯ðà±1¼Uœ–ëà‰ÓZ`ûg¹ouV}¨ÎÙÈx?ÁïÅÁ‘Z¸_µ¨)ìdŒæØV9nõÛl‹õh³<ï丫3Çu-²ßöì”Ý»D†-;ãDJÜ™ðˆ›ŒúÜ]S.[ýÐwrÝú A…œ)ç]¼¶}kíµ.®ÎÂD+Ïð ÉãE“m¶“Ÿy1Ã'œe´íË&·¶Õ©l ’Ê®Ÿ2ÑÔŒ¶e‡¸.S;x,´Ã©”ýt3‘C$6œÔg‘†{~“ÑvÎ5""Dôç¶Ë,‡cœäe"›ãR‹„¿—šÝŽØk™£¡E2îu•x°³Wž WÎ<¼T®S;÷ÞS²\¶~rÚú§/&²<ÇšúLí0Zw?è#z 4œgBDç›ì‘ÖØÉí\'÷Ù(ûNë)»Ö}‡²OÿÅLÉëµ×\· g¸LÌü»¤ÄSüÝTÜä9^'ÍrÄœi‡ Ž YÚ¡ôúo­-¸ûÙS#6øû2æÈˆRñøºÊÐtz…2võÛ®ÔoSSŒÜÒ† Å ÁZ>õ™Ú! \5ôZçŽÇÈ•çr†¯­ô„þ¸òÌ… Ùÿ¨i ìç\îrïg•÷‡vÖ”õßšØjåþòÖ›Ë}7r'~œgÙoÄVÛ®~ÝõƒÑx.³m]¶>ËNëY¾êCíªß]¸‚ÕßÒÄ3þËy„ÛY Æï&­gå¹þØõ3Øÿ¹í’·>—´µ–ǵþ[k­BÏ Ìmz<ï©…m„i -lå8c–ó죦y¢é;ýçmNYÿ­u4³Îë)÷îÓû·ù÷æ\N»ò¼ÕGyãOíFØ4˦Ñ,_ÇZtÞb¯W¹Ô3þ^6œ}®¹¼–—­>Ëã&O+w­çÙçý¤¼õ?ËWv™Ež5š^ -ƲÍö˜§0¹æù\^võ ûZßVdÌuöÑÖò¶«_ÙÏY‹y®auFÝVç\¾¬õßâh~Ó»^å¹S›gÁϳjrZG3yZjß•]ý±ë§¯7I×ÚäóªMjÕaÙÉëiá#ú—kÚZO/ïÍÇGžkÞýïsG¯ŒY>½__Ö;ìl;ÏA×´ìÆ*y›‰²úšæ¿é¾­ÃŒýÐ+;Ž|–q»±O/»ú˜ë︦ó¼síæyçó8Ë×q©ðà>x={ +6Œ6ã Ž»÷´…>våc½{Çì¿G|Z³×Ï;­çÙçhÖò°ÖkmÑékøÉ'ÜÖ§7瞘GØÉÈxÍxÇoúK9—‡‰`äï m˜çú}§u/;yõ;>ZâJùh™³ .6Î8hà÷ZO/7 )½?¹Ûéå½ÎŒq—Šq§õ*ƒ“ˆ;”i])oÝüT`¯¼É<*ûߎȔg&Ðåº+w9luË iaësÞœ1i;”mžÎQƒp x’©xï4G×AOþ„1y’µÜå¼Õ¡Å“{a¼Ã~BÞúSkŒæXSjG†ÆWLL›#£5o8dbèýK [yDÛ6µsÃ3ŽmË,ÇhÜý«ôM^o;Þë¼íL™-Ì‘—ÏÄ0ÙíHÎGØõ^ÎG¼£RÄ7Wr˜²žÀó(5Lx#‡)ëù<ŸQÄ·or˜²žÀ[95LHÈaÒã,KÖã,KÑã,KÑã,KÑã,KÑã,KÕã,KÕã,KÕã,KÓã,KÓã,Kä,» gÙ9Ë.ÈY1β%½7ŽIìþäÏòÅîOŽIìþä˜ÄîOü+!z>Bí͇c»?9&±û“ÿ5±û“c»?9&±û“ÿ%+±û“c»?9&±û0©½ùpLbo>“Ø›`R{óá˜ÄÞ|8&±7À¤öæÃ1‰½ùpLbo>€Ií͇cÒã,åÞ|ø_ÇÔã,åÞ|ð¯Ðêùµ7À¤öæÃ1éq–ro>€Ií͇cÒã,åÞ|“Ú›Ç$ÈYª½ù&±7ɺÖý‰˜´îOŽIìÍ1iÝŸˆIëþDLZ÷'Ç$ö惘´îOĤurLbo>ˆIëþDLZ÷'`R{óALZ÷'bÒº?9&±7Ĥõ惘´Þ|8&±7Ĥõ惘´Þ|8&±7Ĥõ惘´Þ|8&±7ĤÇYª½ùpLbo>ˆI³T{óALzœ¥Ú›bÒã,ÕÞ|8&±7ĤÇYª½ùpLbo>ˆI³{óá˜ÔÞ|¤¦÷æÃ1‰ÝŸRÓ{óá˜ÄîOŽIìþä˜ÄîOÀ¤öæÃ1‰ÝŸ“Øý ˜ÔÞ|8&±û“c»?á#šjo>“ØýÉ1‰ÝŸ€Ií͇c{óá˜ÄÞ|“Ú›Ç$öæÃ1‰½ù&µ7ŽIì͇c{óáM{óá˜ô8K¹7À¤öæÃ1éq–ro>“g)÷æÃ1éq–ro>€Ií͇cÒã,åÞ|“Ú›Ç$ÈYª½ù&µ7uè½ùpLb÷'`R{óá˜ÄîOŽIìþä˜ÄîOÀ¤öæÃ1‰ÝŸ“Øý ˜ÔÞ|8&±û“c»?&¹7ŽIìþä˜ÄîOÀ¤öæÃ1‰½ùpLbo>€Ií͇c{óá˜ÄÞ|“Ú›Ç$öæÃ1‰½ù&µ7ŽI³”{óLjo>“g)÷æÃ1éq–ro>“g)÷æ˜ÔÞ|8&=ÎRîÍ0©½ùpL‚œ¥Ú›`{ó‘— ÷惘´îOŽIìÍ1iÝŸˆIëþDLZ÷'Ç$ö惘´îOĤurLbo>ˆIëþDLZ÷'`R{óALZ÷'bÒº?9&±7Ĥõ惘´Þ|8&±7Ĥõ惘´Þ|8&±7Ĥõ惘´Þ|8&±7ĤÇYª½ùpLbo>ˆI³T{óALzœ¥Ú›bÒã,ÕÞ|8&±7ĤÇYª½ùpLbo>ˆI³{óá˜>ûo>^=,GüûÞ÷ïú)á/ØË÷®ßº:¼øZhÇW÷Ñæ4ÆÈÁ@ïØlô|2“-5ÌÃ\]Þ¸8^–S½¦‹/]ÞɧÐZlOnâó—ñÔCõâ™Ëqj!¶zñÔåhÖT/nÙ—ÔRºøÅË;ÁJsHûŸ¿ùÎ&ÿêy¨]ÿO]¶SmKÉ¿d]•P­ý›ïm?úòÎrŠ#…éìUü@Åö=l£}u§Ì*íǵŸ—z %}÷êe›ÒQŽ¡ï§tÎh(ùd†ŸXŸÑ_¸ §QCŒO˜¹×žmrVé´)±.)^|Þ‡ë©C‰xŠÖh¬ê¾ùÓ(ëžÙÄ­æÖý:EÞüNXN½¤fS#†DÍц#ÆpªÇ«ß;\½ðÆÅ¿ †¨–}w˜œuäM³Tç TóÍ>ØÏ…Ï^Úœ¦œ±øf¨Ë0E¾ÄŠK½xîÒÌý´, z§\OKîGêÿG6û;…žÿyj”46ssÑ–fUÎÐ*ÍEËyI»|ò2õa38 Énæ|m¿¼“ŸØA;ø!Š`šÇ©¤Ôâºlµ>2( e±ÉY'‡éê'ñ…¿Qendstream endobj 94 0 obj 18024 endobj 100 0 obj <> stream xœÝ½mÏ-Çu¦gK‡/Ú,‰š±"PöŒ…ç¡q6»««º«ÁD–GÎÆ,ê ©f(iÀÄœ òYþìÆñè‹‚"(/€¿ùx~‚õ‡R÷½ª{ïCŠ6²zx‘ QìSÝ»¯zY]u×Z‹‡Ó˜ŽƒþÞ ï<9¼þærüñ8|pHi=–!Ÿ¸4«øÞ!/)Gñ'‡¾v|ÿà'­ëZÆö¬¼?"—a8ÎCûׇÿîðîk‡ïÆ£þnUÆqXk™ŽSÆcץש§©ýuü_õø½VýÃpªó2ÖzüxáÍÿîžóƒƒñT! í?èž|á²üΓãwîZ¬Ç\Oë|¼{×D (ËñÑx|4Ç!R>¦1Ÿ†z¼{rxëæø'·Ú«¤!Íåæøon‡SÎ)Ïùæø?¶ò<Ö¼Ž7ÇÞ>OušÖtsüo˜Jš¦›ãŸ_ÜðJ+¯¥”¥Þ¿sûh:-iYÚÍÿÃEËò¿ÕƒÆ<ú…Ë›ÿ[Ý\‡a™·¦aÞ,Ê?ôۭêç¤2üèîÃwïöÎQ£n½ôÿ·Í†SjM–Nm,0›lðõ<¬SŽinCäI/—!Ç)ÍÇ”Çù8æ)EéC*ëå÷iJ”Ó²ö»¢¤gµº*OÓR\WåÜ~ÔwMenwéY.½sðo¸üÞÁ¿ì²ßÆwEIÏzGn>¥Ò¦Û8x¾ýûóÌù¬±üdŸöÿ°‘[ŽšÌùrè¶7íCwI§µýr«Ñ‡î m´Q;ÎóÍ?½NsåæíÏÅ÷oóiœ§)E±U­ëÍïß>ʧº,S½ùêùÿõm›ã:曯ì÷_<ÿ+û¿p[Ê|ËôÌN­…Ú@ö€›Õ€ã¼h¶©­73Úzsh½¹ýù|ùç?8ŒËóoÛ®´zý„êõùÕ×O¨¾>·zž_}»þÑêã'TŸ_=}BõôüêÓ'TŸž[ý1àš]ü n-2£m$ë3Æò<"¾|.¾x[Oµ ÏõÙ‹'†i/j¸,šÇS{û'[yju–Òfì¤9½,9Jmî­Ãå6÷VÍz×X÷»ÖíYª«y8ÔÉuUe+t×4ú®ö,—Z]ý†Ë­®~9j,û]Ëö,Û§Ý0}ÖÞýi~ãðHÉÃ#UÙ˜/ž»ÿ´¼r.¾à/^]Öqjå6jJ³¶7ÿLWSjË¥zó¥f}ÖF´Êd5C5ÕZÇgîûçí¾!Õa–©ÛŸüµ} ~­_lþæí§ >OÐ޽ܺ"¥¡w`JiïÀVmïÀ4-½= ã®iÝ;0ÊÕåUŸßµú.=kçú7Öx®Ù5Æý®q{Ö;»Mù ½òUÇܲƘK^à]iÌý7ú¸Mm‘øÌˆzpq_‹m)×}ã§Z³ÔvS[øy…âr[aŒ©éi^s+Õ%­$Æå÷´ªØjäý®¼=Ku[9·åŠëº¼¬Gß•“ïjÏr©ÕÕo¸Üêê—£FÚïJ۳š}Æ^ùšª}TO©½ÅœNu_Uûýr ¼,sT­Ü/VÃï‹ý‹÷écD¹µÈþÕhmÒ¿cÞ¾c>5Æ|þjDu¿kÝžÕ¿ãzþj¤aûj¤aûj¸Ô¿.÷¯FÔXö»–íY1F>c¯ü_àC7Ö²-¼»™ÈcJýƒ5çÒJ e®}E4·5ôWÏ5w#’ÎKgO—uõÔkA•—çUåâòârL¹ZUN.·Û¦µ®*—Áe×oƒQåÑå¶‘šÚ÷Ôå)«ì{KžZ9ö0¥$•=Mˬ÷)è}r<Óï0U—«~+Åïj¸&ß;k·´ŽþÝyÔó‡(§öüºFyjϬ5ê·ÇMuösæÜžS‹Ÿ?—öž5ûÝæY÷N~ŸYïSSk°½ÏïPÔwkŒ‡2ç9¨ñ¹.qÝs'ÚY’e›Ëñ õ\ S™=£³æÂc5ìFŒínOöÏï^öûD{æ\϶%®Ïõ ªcsì?Ÿl£/Z¢Ž9öŸïÞmkѼ×êeÏ“Vnm4Ì9Êa{YëI¿k+×Vž{ýµ•ÝŽ©5€Ö–K”Û‚µ®qo3Q©Ú–µrû,T¿¤©jŠgj½Z‡åöÌ¥ÿÖ,ig‰ús{ÎRâ9í瓌ƒËíÞ—­¬{¯‹Û€=¦¹?§Å4»ŸZ¹Ý;wÆ(ÇsZß´åÁ|Qîuf•ãwõY›K~:×ÑïÎóôñò¢ß-û;ϵžY†tf/ا¼·IØÄ$’–yÝÛséÏq;oí¦u|ª{mí¬¾«Ë¸õïÞ/9Ÿû«Ù¯´æ^?©{ñ¢ß‡óxÜÆ‰ÊÛø±›çóõù|ïØûѶr»ÞÇ¡ÆnÛ‰¨¥[³n»¥9v-eÑÒïJxé{&1´÷NÑ©ÍûKØv©Ý‘¦em´l‡Z¹ñŒ¶%)©_õh—)ÍQ_}3ø{Þ–ZÜÄ»&µéݯiR9úLÇcÌ˔ڥq Τ5öš£}µ(YS´W[½ŽkïûÖ-c­}n´úuN[?ù£âvTí«UT¢í¼ŽZ]g¨^×ùú g.у}KŒÑAK®%G¹}:Ç>†Qu&AËAm ´ÄkŸ•V¶½Y«~+¾2q{yqtQ.QGïbY¹ê™¶÷íùþ­xçQÏœâ=ÓkB•'¯ƒ¥¨N‰²Dµn ¿Oì?vÕVc´›–³›}{í}ÑæOkç>'U§îó¿õKŒ¸UËÓãÄ«Ü>NÚ£ÇX—µ~Wy}õ¤1“¢ß5†ÜǼ1÷’æóïÙ–™oÓyìÅ\M²GÛø¬CH.kœG»µy}1Ηóø¯Öú½UåxçUâA7Í—ýzÞô†Ÿ<€Ûõ¦Á ý¥o êÞïÆÆKù'½œ–¾¶©*ÇúJ‹üf Ž}]t¾žÏõóy{à:aWâÞ!Ö*5æX\ß~7¾q£[`Þ¾q›R§Nõu½ëGÏVRÎ^ÏN².k’´@œÄÚZt‰Ò;‡©H§‹˜æyŽò´lwE)4—s¬©¢<{¥±ïÒ³r<׿‘ã¹þe—ý6¾+J¥ôÝþg핯y„¢³“6Iò<ž–.y‡_Úê»íÚ>wÞË~—/D¥—΂äï¶?oã¾íêâ¦©Š›—}rÒP./~Ù''uúqŠ/¾xÛ™§öaþÄC”¤cŒ¬™÷üC”íÏ?zT°>ÿ¶íúGªOÃó«o×?Z}ü„êãó«§O¨žž_}ú„êÓó«çO¨žŸ_½|BõòÜê×ÚËK_4û¼7û¼ß5_4û|ÑìóÞìóÞìó¹Ùý½ÙýËQ#ïwåg†Ìg핯9Rr²Ée8õ/××.;ýÓFÊGÏb§öža ŸôròB¿•mñ¤•WAuj‹æ›­IAyœúõVgãÞYu¼QnåÖ4ƒE•V®­ìëÔ>­\âú¢:ý·ÕIñœ¶σ7­ÜÞgèÏ\‡M¸hå]ie‰ñnj±.°´²Do²[yÞ„Í)ªã…{+O›È¹]ïu,~ö{ëùºŸï¥«Ý»¶òd1Ö⛼„ôQ¶`»ôrÞÄ™VÄÕÛDŒ^”¶²ØSo·Uí3žÛ³”½‡­/T§ö¾˜ÕþÑž µõKü®ÎNÆ©ìýåVNêß~}8÷ûTÏã¡-’s,…ãúÔûZõcÐÊÓÅõ>®´¨n?`‰kÝ'ìÚŸdùÊ[éwÃ8[Dnµ†2l‚r+KòFpjë³MtkŸF ÐfzàÚ¢Eík¡sÒ0l}ã_ݯÑ÷c²—¢¼n¢Ûd‘!DóÉ‚Uˆì“6v­ŸÜ7UrlZù<^ÇÇñ|ÝßmÕ§~Šç¬çùÐ6û<ÑôÚæOÛiîóJV%ñÎmöÏ=¼æÑ›³V®ûüL“úÏëI{º­/Ó$ó!ùäÍ›¥V–I[â}&Íó£)çë½~ÞÏòzý<žŸ“â=§¼'¿ÏØyÓy̵®¸âÞÑv$ÞyÔõq9·IÌ ·ÕàÍtkÃöüN&5Á6ÚÞMí?D_¨Nê}äyýRTˆ:cц¦úÉ4ú€d޲Nz?ú@%÷qµl"oo“ñþƒçy“>,‰6,ÈFÿóyÄ89¯)ÞÊQ_×vÝc²×ë붘ésä'j9õö,—8Ÿ"l³U’S»^ŽÏÛ¸N: Ék³±Oz¹ÊhÍmJæXoéÓ¦RûäÕÔ¯¶§Öܯ7üÝå’ž¥º­\ªgv/ë묻JÜÕžUúsõ¥?W¿\úJo»+Jíâëÿ{å«~ý‹œI³<~ûB1œÚpí¾Þ0|%v³óX曯Çqä¼l‡Ô½†¼gʺ\îf/γ¿ñ¼ª÷¿ýá¶OùÚoZiÆ [/zlÝ{lÝ{l=÷˜NH·ËãÖcyÜz,JÑc.÷ëe=hÜzÌÏÚzl½è±uï±uï±õÙAö{嫲º~YN狲Û>m=–ÓEå‹Ë{å½ÇòEå‹Ë{å½Çò¹Çü½ÇüË¥ï'·»Ò3ƒì³öÊ×d%•ÏÀ ‹Ýa_’ZÇìKÒgËš•>ÏoåéâzÙ—­Ï–½lo‡ÏZc™?ÛÿbŠ{‹—°%Êó¾œKÝ·)³—±„Ò‘à¶Lñyxœ¡¶²~k‰mœ Gýe?ž|–¾®ñ|/aÇ~ÝKÛ({›2Ž^ÚÆ2w–»V_~ÍޢŲiö²,¶eróÎü¶‡CÔY»½sYK½©_¯ZöëÚVÆ3/—cw.üm÷-ÿŽœºà¬¥sŠmÐ2j ˯EKÌð¿˜t¼”Ã/cÒñRι—5Ij¯Óê—x7ù)äí©£¦<ûL´•Û½s´¿Žò¼ö:²kÞË}M¡ã¨Vî׫ÊQ_‹µíº–×[}•÷ç¬ϯ¿»è}–ý·Êœ÷÷ÙÞ¿u[ãv–ðO™t„–ÿ£•µu›{Qí–övK±ìv{>Óα5_¤ŠŒ5ê{É>÷~Éû_ÇÑÛ–FA­ßÇs_oãAãgö±1ÄÖÓcfˆgÆX*ù<Æb{c¯ÏÉX"ÇXþšÃª—wŸ)ùií¿å¹°\Ì‘¹ÏÍ»r>éÛõoò€}”Ö>†½e~´OÊ>Çó¾%ˆùÞçWl–³MØÊ^úÇü’ÏÄ^Îûö lÎV>o!æi=_¿ØḆ…ˆvxf ±ø”`×Gh,>eüÖï~Ä&^lÓƒ¹÷klÓ§s[§ÝFlÛ# ç­û·ôñR̸ÍÏmû¾ØO¦¤mδrŒ]·Ws¶Û[»£¾|“ºd´x|ĶRGÜm¬¸,?®fï¢<ÚÞÅ^`ô8‹=„ìÂã@ÓWçE.KúÂvTÉ,qÆßʲ_>So{ Í“h“*;;NqoMûß§9cæêyË¿˜ojÊœb^­–hb{½–a·wR66{'ó¿Ù;¹ mön{Ø;7G³S”w{g•£Û;7M_כּ=ò´Ëq¾ÞÊÓf³Zy·}y8Û¾<œmŸwëûõ³íkuÖ‹çèèÐ6Ë]²ÿnÚí`+§ÍZ1ìv0[ÛïvPŸ¸ÍjêovPS³ƒRë6;¨éžÃO¯ Á¼ÛÁUö4Åü_mcŽ­ƒ¤¶>$óŒñͨÕß³!ÊÃ.ÁÉm)1wäYÕÆIŒ«b;c/[2ícLc²?s²¼ud»»ì¡ÓëMŽªƒeø6¬–=¢\×ͧË.’­1¢l{¿µØ–…IJ”i—RÛ²Û6ùt±LÒç¬e’iÿ6ìkK£!ÉÌ!“¤³]îöz¾°}¶'ÝÞ]È$ÏÚ¸³Lò·~ÌÆYê6®ÄXû¿ÁÆ-gçïC·qõ,ï8B°Û¸°Y©¯êÙÆ• >µÝÍ{;Ú—´K”riÜlœ\_7IªÚÞ…g¼ðôØÚäJ¿nãVÛ²°q¶CŸO!™z;~%VÐ7ér­g©mõª1?%Ó Û<œ7óyLÑ¢%o6ÎËÂnã¼,ì2 ?÷mÎø9£Ãü]Ê£%ÇÒ·Ée³q­ªçdìV¯é\ÇÒb6o›šÚâä(ÏZëÅ;KËeˆ²ìo·wIö·äxfµí›]V–øÝIö±ÛÁIójö˜ÎÓx¶‰“meŒ&)ây¶ÿŽMr¼¬8ÑÍVNZ‹-ñþ ÂÙìöQ9|š[9ï6q {Úë,» .lh/÷úŠ€æ½þþ|­O=Ÿã·ºm÷—ý=»l[—Ψõfé캞âÞa_‡F[•èÓhù·­Ú?GûWo¢–Žï>R?z-æ¥fŸãØOk”õ»“çEµ6 me<§ÚeÙåcIí6:^oöo^Ým´ÕùþzmÜÖMŽö”è²¼·?mœ÷ï¢ÖkÿvÊþ.}¾hŽÄ|µ6zõñLÿîfKÓ5æãºÛh™±ö°øXR^£l?ã°Ñ5ösQ.gmŸïn£«6ÂFÛo{³Ñ¶¹Ýæ,él£­%…^¼f ­=Áf£›u{º^¬Um7½\ÉF{du=]Øhxí6Z¶ÕjE_­­É'y¿ÍƒÂÒôsI1mí]¶OWVHÃÙƒùÃbµÒž6Åw!·rí+•cDêÃÚ®ÏǾêÙËÖSÖ¾2šÎåÑ×»õl÷†  vé­#gØÒ[Ag‹¥·¾.•þ娫®O}YDÚW*—¾zU°Úˬֈ‹v©¥BjB—¾:¶‚½ízF½C¬4Ë¢ú±3*IωYö;ǪA“rÞ-Û$˜‡íðpR¹ ©Åû¡KVŸÄˆÑ†mîÞŒBšcÅ4@¶ëZ1õúIJü_¯$O½9ášÑ{†'íÒ&Gÿ$ÃVb—´ã.±kKÚ±ö6LÚ…”mÍàèúeÕ¯^¥Yõ#b é#Pº÷s™Õ§fOE£n Í¢6\Â[=·6iCÌ¿Õ,ç±tïðöUjå^§a—9XdØJ|u’ÃK ïPÞ–\ãz³>%‡‡íÔ]bEooÌ2•ðäÔoMc÷m,iîÞ¤*‡wµÕÊØ=TÅ5v¯k™¡{–g%I‰çŒÖ(ãXò¼¬á%›÷¯'s)ݧ³Êj‡g¯w!cxóNuSoF¯š‡¹bKžÀÚ MKD5ÕÙ9áé©]]éu´œjŒQôLD)U{{oQ2ÓE”Ìx%³^DÉÔ‹(™õÙçlÏWôÃö»³#WâúâH£¨ïsî™<ŽÛîpôQA¬lÆØIDD”6¤9wÏäÑî˱{Pç>NíØ¢’Ä‘ÖþÝc~R¿tÏþ¤þêý¨ü2[®Êsxê& ­Ýk·µC‰cÀ¤sà2݃=iüÔWYã*"Ro1öÚ¤Ã5Æg{f k98´nqÞ,l‰@’}(sŒÕ¬÷œÃ{\C¹,áA]4†—ðdVDZY‚½YõðL.z·ˆú²«h‰È°'náÙ®¯N›¿%æµlBx³kçQÖx©a%¾ŽI A‰¨š¤Q 4iÇßmZÒ×tîã_ý¾Û"Û¥ð¬îöj¾°c—ö-žsÿêœíáz¶“ÝÑÃö3VçÝ®†­z~8}wºB[–i³{Ín—íý­—ˆüëv¾+~fÝ‚‚<ʶbP_÷oD ;¹¯0öoÊzþÖhÓS"òÏôöm’ðÒÛ܇%”¥¬HÌQ€Þ‘—ÃÞ-”ˆ¬z¶ìïìÚ¿¹å¢ìon첿¹ñLçmïåt®¿Di¹(÷ï{dš#–äc‘Ló3òn¬”·5Át±&˜.ÖÓÅš —ûªyØ×Z îeûX8¶¹¯ ²"Fúš ­|÷5A–E_´Ï´µ{–ƒCÿžy·ÔûÉbjï?ûZõ~µr²õ·„°mM à´mM°úù±&¨1žú*²îkLlk‚Å××eÛš ù}MÐìÆ¾&È1¯Â‰+íóÊŽýÛ? u_ØQb¿>ç5„Ÿk©[Ûš@Ó}[¬s±&¨^Äš zþÇš z^Åš@ŸômM°x ßW)Ûš@ÊÆ¶&‚±­ ¼†èkEnk‚YßÚ¾&Päì¶&Pé¶&(šsD§d­ûš kž÷5¢óJ [ã³»mM uCŽHžIõûš@{ÛšÀv|ŠßÕÎl_4¤}MPÎk9ïlkí´Ú7&¾73Ûš@;³-ÊHß¼mM ϶&Ðî3ÖŽüÙ×K=¯ Êx^¤óš` 5¥¯ –óš`Iç5N¿ö5Áø_xMP.ÖÓÅš œ×u¼X¬ç5AºXx7Ù×KÚÖÉêë¶&ðÉM_H±Ø" uâµ­ ´ƒÜÖ²¹}Màµ]_È)¬ŒýÛßúk_hœlk‚uÞ×òÛÛÖú>ík­ ·5¾÷9"”Km[dÙŸ¾&Ès=¯ dßB5IÅëÝø—¼ìk…ƒlk‚Y㳯 $,lkÛêöÏûš`ñþ*Þs‰oC\÷wnèY¼–}MP5×úš zÿk‚êu@ü–*úš`õ·|èv&mk‚I—v[d»´9y û·¿Û±Kû6Ÿí^¬ ²÷3±&;ÙwÉþçÙìê²ÙÛmMÐípÿ–¯ûš z=k‚Õßø®ZË&ô5AØÿ¥«÷Ó¶&° Ò¿í;R÷o‡×ý›"'Çí[#¿¾&ßþm²=‰6·?á¶&ðx[Ç®$¥}M A¯Ôµ^”»ú5íë€éb}0]¬ ¦è÷xf¬ÊE9ë/]ñZ.Êÿè5ÁGãs>ÓÙìä\t*÷/œãm/Ë®³'¦üàØ~AùGgØ’|Ö À;Oßy|xýñ÷Žÿˇÿñß^ÿáqœ¯ÿkÿû;öÇÇñðú¿zk< ?:¼þ'¯ÿñÿIûÏ?ûÿâõÿþø¨ýÉÿOïýÇ'ïÿ‡Vù_þËÃãu<ýå7_~õ_éáÏ>×þõóoýâñ·þîá˯<~øò«?ýÕ/~ùò+ßþYûÏ7þúñÿÿó/<|úà þ«_üû¿:>þ܃ö¯×Úådz®>}ðÊßþú{o>øÂñÛí?_}ü˧?ýÃO[­§oüÎñs‡ï>>~ÿy4Íphtf ¢‘;‡fÎ$š•d´@ÑÌ$+  )‡FÛM!Yiš"ADS@V  d,éƒh2É Œ•d¤ôh2É èd€C3 $+ õ D³¬@HV “ÔÁ’Iê`)$u°’:X I,…¤–™¤–™¤–™¤–…¤*·<‰¥V”:XQê`E©ƒ+J\QêàJRç£Ö•ägcÌNZ4?Ó`vÒ¦Áì¤MƒÙI‹†ãgcÌNÚ4˜´h8~6¦Áì¤MƒÙIוägcÌNÚ4˜´h8~6¦Áì¤MƒÙI‹†ãgcŒŸi0~6¢áøÙ˜ãgcŒŸM]I~6¦!©ƒ ?ÑpülLCRA~6¦!©ƒ ?ÓÔAŸh8~6¦!©ƒ ?ÑpülLƒR9~6¢áøÙ˜¥rülœ.¢¦ø_@vÒ²“ŠŸM§ì¤; d'Ýi ;é ¡øÙtÈNºÓ@vÒACñ³é4t§ì¤Mƒñ³é4t§ì¤ƒ†âgÓi ;éNÙI ÅϦÓ@ül: ÄÏ&h(~6âgÓi ~6ACñ³é4$uãg4?›NCR1~6†¤bül: IÄøÙ ÅϦÓÔAŒŸMÐPül: J¤øÙ ÅϦӠÔAŠŸi@~6e"ùÙ˜³“ ÇÏÆ4˜´i0;iÓ`vÒ¢áøÙ˜³“6 f'-ŽŸi0;iÓ`vÒägcÌNÚ4˜´h8~6¦Áì¤MƒÙI—‰ägcŒŸi0~6¢áøÙ˜ãgcŒŸh8~6¦!©ƒ ?ÑpülLCRA~6¦!©ƒ ?ÓÔAŸh8~6¦!©ƒ ?ÑpülLƒR9~6¢áøÙ˜¥rül ÈϦ’Ÿi0;iÑpülLƒÙI›³“6 f'-ŽŸi0;iÓ`vÒ¢áøÙ˜³“6 f'Ýh@~6¦Áì¤MƒÙI‹†ãgcÌNÚ4˜´h8~6¦ÁøÙ˜ãg#ŽŸi0~6¦ÁøÙˆ†ãgc’:ò³ ÇÏÆ4$uägc’:ò³1 IùÙˆ†ãgc’:ò³ ÇÏÆ4(uãg#ŽŸiPê ÇϦÑpülÆqùÙ e'mŒŸMÐPvÒACÙI e'mŒŸMÐPvÒACÙI›ãg4”tÐPvÒ¢áøÙ e'4”´i0~6ACÙI e'mŒŸMÐPül‚†âgcŒŸMÐPül‚†âgcŒŸMÐÔAŽŸi0~6ACR9~6ACR9~6ACR9~6¦ÁøÙ IäøÙ˜ãg4(uãgcŒŸMРÔAŒŸh@~6y%ùÙ˜³“ ÇÏÆ4˜´i0;iÓ`vÒ¢áøÙ˜³“6 f'-ŽŸi0;iÓ`vÒägcÌNÚ4˜´h8~6¦Áì¤MƒÙI‹†ãgcŒŸi0~6¢áøÙ˜ãgcŒŸh8~6¦!©ƒ ?ÑpülLCRA~6¦!©ƒ ?ÓÔAŸh8~6¦!©ƒ ?ÑpülLƒR9~6¢áøÙ˜¥rülÍgÚÏæû‡á¨¿?üñáƒC=MúË.Ëï<9~çîðú›ãróiÈÇ»wuÛi]×<¤†p|ÔÚ¢¤S[F”y:µ/ÖÝ“Ã[7ÇÛá”æiXêÍ—ÏÅo멎%=÷ÚK·òiªµŽ7¿·]{ûÃsÅ/ÝN§u)ùæë[á⎷†ÓX—uœn>›OãüÅã?òõÇÿÏýÎñã“©C][νP˵UÝ{umq÷^@][ã½PõÚRï½€º¶â{/ ®-üÞ¨õÚúï½€º¶ |/ ®­ß ¨k‹Â÷ª-»y†"×–ˆïÔµ•âû5^[0¾P×ÖïÔµåãû•®­"ß ¨k‹É÷êÚšò}€š®--ß ¨k+Ì÷êÚBó}€Ê×Ö›ÛP9ÅLCÑÄLAáÄLCÑÄLCÑÄLAáÄLCÑÄLCÑÄLAáÄLCÑÄLCÑÄLCÑÄÌÅ3 E3 E3…3 E3 E3…3 E3 E3…3 E3 E3…3— 3 E3…3 E3 E3…3 E3 E3…3 E3 E3 E3OÌ4MÌ4MÌNÌ4MÌ4MÌNÌ4MÌ4MÌNÌ4MÌ4MÌMÌœ†™'fLÌ4MÌ (˜˜P01ÓP413 `bf@ÁÄLCÑÄÌ€‚‰™3 &f 'fLÌ (˜˜i(š˜P013 `b¦¡hbf@ÁÄÌ€‚‰™†¢‰™3 &f 'fN(fŠ&f 'fŠ&fŠ&f 'fŠ&fŠ&f 'fŠ&fŠ&fŠ&f6(ž˜i(š˜i(š˜)(œ˜i(š˜i(š˜)(œ˜i(š˜i(š˜)(œ˜i(š˜i(š˜)(œ˜¹ @1ÓP41SP81ÓP41ÓP41SP81ÓP41ÓP41SP81ÓP41ÓP41ÓP41³AñÄLCÑÄLCÑÄLAáÄLCÑÄLCÑÄLAáÄLCÑÄLCÑÄLAáÄLCÑÄLCÑÄLAÑÄLýOäqbf@ÁÄLCÑÄÌ€‚‰™3 E3 &fLÌ4MÌ (˜˜P013 `b¦ pbf@ÁÄÌ€‚‰™†¢‰™3 &fŠ&fLÌ (˜˜i(š˜P013 `b¦¡bæ÷ÃQøãÇzšô—/\–ßyrüÎÝáõ7Çå8æÓwïê¶Óº®j‡GãñÑp,)zŸOK:Þ=9¼uóµÛá”æ2ÎóÍ+·õTÇ%­7/Ý>ʧ©Ö:Þ¼xþó·?Ü+|IÊ’ç|ó _,©Ý”Oã|p¨§IùÂeù'ÇïÜ^s\Žc> ùx÷®n;­ëš‡ÔŽ©Á§tÓx*óñîÉá­›·?¼N­·Ç”n¾t;ÖeÎ¥•åSYÊ\o^¼­§:ΩÜ|õ\óí÷oóiœó4ß¼´—ÚÅv×T—e¹ùÆ~׋ç»ÎU/žÿÂí£á4Ö¹Îë̓‹òùw¿ø)¿{ñ°W>rSçù²]ÒÚY—¼î¯]븽À²ŽS{ÖÅJ™Oc™~t÷ÆaœOõx÷§‡»×Þzæ…?¦¦M¶â7Îg¶—Î7]4ÔÜŽéTR//~R3ìxО:Í%Í7/Ÿ[äâU.zú÷öxå|ñËûÅ?_Ó¯NSê,iÒ‹ª/\´c¼VNãöZyΗÏzñ²·g]\üÊmâ§aXÕøß½Ól8ÕaXêx{±ÍTò õÿp|²•'6K+s+-9JïÒ2,Q~¯•ÓÜk,û]Ëö,ÕmåihŸ«÷zylÊwM£ïjÏr©ÕÕo¸Üêê—£FÙï*Û³Þ9¼ûÚgî•røÁÕlÑÔ¬ÐÚ~¨Î§f¨mŒ.zý«çâÅD¼W/h°”´¬7ÿLWSšòP?a^_Þ÷ÏÛ}CªÃ|óµËQ|ñ1^ÓÍ7o÷!×öa­qç䎚Ö%Ê­Ár£kåÑeiRвëd—Û¦§•ç(+¦ýKå©uóV6ï2\”ÇsyŽv˜ëE;»Üë¸ïæ—Õ¶e<÷ËÖ×õb ¨Îïœõ[S¼sÎ㪞Çۤ礨#£±ÛÑ¡rÑ>£ê„ÈC‰µì{çzΘ£ì5®Ÿ3Õ‹ëuÙïí×ר?l¿õ“ÞKZv3·ô_[×­¥ß CÕjyŽ-šK¼ë³e˜o‹Úb‰ù¶¨–˜oËœâ×\Ö8›âÞEceŠ:‹Þhò».U¿›ãzÕożZª~+úfY5æbþ,j—%ú¾zŒ.5Êú­˜U¶`‰1'‚ö‡QníBŽr{ÿšúõöÌ:Žê3ۻܞY‹ß³æöÌ㬪Mjç¨Mêõe#Ö1ÞM6b6©sûÝ5lJÕX\ç(kܯ1Oj›Ó0Åó[_NÃ÷6ŒiXýë ÏB´á:èsì) Q§uî”âý×$Km¢Wi_ü^nÏŸÂ{šñîå©•s<§™ÃI“Úåö̶rmm2•˜‡Bjå¸Wª}±¶ér¾>ëûzNkÏóóWýnÝŸ¿¿Þ9æ³ßsÿù‚K{˜«k³û­â9m^µö‰çûƒsum6«µg´ùšÕÎÓ¹Íc·¯–ú%úT‹€5Þ¡Êæ®}œÈF¯1þ5DÚƒã™Z>Ô>öŠÆLØh ©vSKÓÑÄcLc/GýIc2Þ³N«ýùIÏ{YÏ ›[õ[Ö^Ö\ZÏ‘x¦íuôõbûãyY5K:ϵh؃y8ÏÍmΖ‹¹l[QbŽ—³Ð7o ¤©»Yù\µrÔϾžÏ¶e+»NÌqµÉv]sp{N\çoöÊ6nmŸÊlç‘Ø­â:ûúl7®g×Ê»]kå|Q^7žV®ç²æv0·rº(ëý^}‹Ö˜WCCMkØâ!yÜOQ–-‹q¬)2 1þíó‡þ»²}Ñ;h1>Ùˆ1¾ƒ—µýe öëÓ¹¾ìÂÐKvaÈãùùC/kîÕ^–mû»É¶æþβ­ý™æ ›bÞZ×½êõ5j|oÚ~çhƒï²Ú¼·Ö;1G™O(—§íÛÓúN}ºDUõõÜË%Êþþå?ñ]\ÎcfÎc)å‹1×=¯¶rÚæÆ¸Æ‹òyŽ}¤¼Ï±Q6w¿~žcãs,ÆÞ3sL!½m{â9=Vq˜Ë¶…y7Ö~“N鞨\UîkÅr.Ë6§°[Ò‰[Ùm]®ǚSߢnã½ J1¶J–õaiÍÙÊîã2«N|Ë¢{cL”ªßu]YõüXwÌú~¦èW-¹ÚŽÈõµm˜Æ°ý³¾9cŒÅ¹}šÏeï°£½ÏÑK‰òtQÖ½aïÛ_ÏÎå`Ñr°ýnŽò°Ïg}¯RØ×â¶}J©ãþÝ+³Ú0ž_ŠÚgîJÕ‰o`Én«hÍÉßç¢wKñ>Eì©ö~Q¿åõEï‹Áub ¼.{éS¾—«ïõp]ö¾Žëkßkôqâ±5ë­õµn£föØ û=j÷d³ßÖʺέi¼Nš£¶×•ȇ“Ÿ~ïÍ_x(™ñ?ý›Ÿþ_ºäøí§ûô{®ëŸ~ỿúÅÿÜ/?øÂ~ïá_¿ñWÇÇÛ?¯þþ/~úwç‡=üëǯ>nåÇÿâÏ~÷w¾ôqÙ5˜Êµ€îÓµ=îÓµÝîÓµ}‚îÓ|mÇ ûÀtmï ûÀtm¡{À´\ÛOè>0]ÛYè>0]Ûcè0Õk» ݦkûݦk;ݦõÚ^D÷éÚ®D÷éÚþD¿}¦tõf÷éÚžE÷éÚîE÷€éêÍîÓµãïÓµƒïÓ՜ݦk‡+Þ&žf™®žïì>0ñ4ËtõÌg÷‰§Y¦«ç@»L<Í2]=Ú=`ºzJ´ûÀÄÓ,ÓÕ“£Ý¦«gH»L<Í2]=WÚ=`ºz´ûÀÄÓ,ÓÕS§Ý¦«çO»L@Íòê™ÔîÓÕÓ©Ý& fyõÄj¿}¦éêÙÕîO³œ®žgí0]=ÙÚ}`âi–ÓÕÓ®Ý&žf9]=Û}`âi–ÓÕS±Ý¦«çc»L<Írºzf¶{Àtõôl÷‰§YNWOÔv˜®ž­í>0Á4Ëšxqjb¢Å©™ ¶2lÿd&ØþIL´853ÁöOf‚íŸÄD‹S3lÿd&ØþIL´853ÁöOf‚íŸÄD‹S3pÿD‹SSBEZœš™`>f‚ù|ˆ‰§f&˜Ï‡™`>b¢Å©™ æóa&žf‰‹S3O³ÄÅ©™‰§YââÔÌÄÓ,qqjb¢Å©™‰§YââÔjâÅ©™‰§YââÔü?ái–¸8µšxqjb¢Å©ÕÄ‹S3P³¤Å©™ ¨YÒâÔü?øái–¸853ñ4K\œš™xš%.NÍL<ͧf&žf‰‹S-NÍL<ͧ&&Zœš™xš%.NML´853¡4Ëi3-N-˜Xqj µêL¨ýSgBퟂ‰§Ö™Pû§Î„Ú?+N­3¡öO µ &VœZgBíŸ:jÿL¬8µÎÜ?±âÔÌ‹SëL(ŸÎ„òù&VœZgBù|t&”ÏG0±âÔ:Êç£3ñ4KXœZgâi–°8µÎÄÓ,aqj‰§YÂâÔ‚‰§Ö™xš%,N-˜Xqj‰§YÂâÔ‚‰§Ö™xš%,N-˜Xqj ¨Y²âÔ‚‰§Ö™€š%+NÍL°8µÎÄÓ,aqjÁÄŠSëL<ͧ֙xš%,N­3ñ4KXœZ0±âÔ:O³„Å©+N­3ñ4KXœZ0±âÔ:L³Ì3/NML´853ÁöOf‚íŸÌÛ?‰‰§f&ØþÉL°ý“˜hqjf‚íŸÌÛ?‰‰§f&ØþÉL°ý“˜hqjfîŸhqj §f&˜Ï‡™`>b¢Å©™ æóa&˜Ï‡˜hqjf‚ù|˜‰§YââÔÌÄÓ,qqjfâi–¸853ñ4K\œš˜hqjfâi–¸851ÑâÔÌÄÓ,qqjb¢Å©™‰§YââÔÄD‹S3P³¤Å©‰‰§f& fI‹SkL¸853ñ4K\œš˜hqjfâi–¸853ñ4K\œš™xš%.NML´853ñ4K\œš˜hqjfâi–¸851ÑâÔÌÓ,—Ê‹S-NÍL°ý“™`û'3ÁöOb¢Å©™ ¶2lÿ$&Zœš™`û'3ÁöOb¢Å©™ ¶2lÿ$&Zœš™€û'ZœZcÂÅ©™ æóa&˜Ï‡˜hqjf‚ù|˜ æó!&Zœš™`>fâi–¸853ñ4K\œš™xš%.NÍL<ͧ&&Zœš™xš%.NML´853ñ4K\œš˜hqjfâi–¸851ÑâÔÌÔ,iqjb¢Å©™ ¨YÒâÔ.NÍL<ͧ&&Zœš™xš%.NÍL<ͧf&žf‰‹S-NÍL<ͧ&&Zœš™xš%.NML´853±4Ëqpqjf‚Å©kÿL¬ýS0±öOf‚Å©kÿL¬ý“™`qjÁÄÚ?kÿd&XœZ0±öOÁÄÚ?™ §LÀý,NML´8µ`bù|ËçÃL°8µ`bù|ËçÃL°8µ`bù|O³¤Å©O³¤Å©O³¤Å©O³¤Å©™ §L<Í’§f&XœZ0ñ4KZœš™`qjÁÄÓ,iqjf‚Å©P³„Å©™ §L@ͧ&&ZœZ0ñ4KZœš™`qjÁÄÓ,iqjÁÄÓ,iqjÁÄÓ,iqjf‚Å©O³¤Å©™ §L<Í’§f&XœZ0Á4Ëœxqjb¢Å©™ ¶2lÿd&ØþIL´853ÁöOf‚íŸÄD‹S3lÿd&ØþIL´853ÁöOf‚íŸÄD‹S3pÿD‹SkL¸853Á|>Ìóù-NÍL0Ÿ3Á|>ÄD‹S3ÌçÃL<ͧf&žf‰‹S3O³ÄÅ©™‰§YââÔÄD‹S3O³ÄÅ©‰‰§f&žf‰‹S-NÍL<ͧ&&Zœš™€š%-NML´8535KZœZcÂÅ©™‰§YââÔÄD‹S3O³ÄÅ©™‰§YââÔÌÄÓ,qqjb¢Å©™‰§YââÔÄD‹S3O³ÄÅ©‰‰§f&˜f¹d^œš˜hqjf‚íŸÌÛ?™ ¶-NÍL°ý“™`û'1ÑâÔÌÛ?™ ¶-NÍL°ý“™`û'1ÑâÔÌÜ?ÑâÔ.NÍL0Ÿ3Á|>ÄD‹S3ÌçÃL0Ÿ1ÑâÔÌóù0O³ÄÅ©™‰§YââÔÌÄÓ,qqjfâi–¸851ÑâÔÌÄÓ,qqjb¢Å©™‰§YââÔÄD‹S3O³ÄÅ©‰‰§f& fI‹S-NÍL@Í’§Ö˜pqjfâi–¸851ÑâÔÌÄÓ,qqjfâi–¸853ñ4K\œš˜hqjfâi–¸851ÑâÔÌÄÓ,qqjb¢Å©™‰¥Y¦aÆÅ©™ §L¬ýS0±öOÁÄÚ?™ §L¬ýS0±öOf‚Å©kÿL¬ý“™`qjÁÄÚ?kÿd&XœZ0÷O°851ÑâÔ‚‰åóL,Ÿ3ÁâÔ‚‰åóL,Ÿ3ÁâÔ‚‰åóL<Í’§L<Í’§L<Í’§L<Í’§f&XœZ0ñ4KZœš™`qjÁÄÓ,iqjf‚Å©O³¤Å©™ §L@ͧf&XœZ05KXœš˜hqjÁÄÓ,iqjf‚Å©O³¤Å©O³¤Å©O³¤Å©™ §L<Í’§f&XœZ0ñ4KZœš™`qjÁÓ,§Ê‹S-NÍL°ý“™`û'3ÁöOb¢Å©™ ¶2lÿ$&Zœš™`û'3ÁöOb¢Å©™ ¶2lÿ$&Zœš™€û'ZœZcÂÅ©™ æóa&˜Ï‡˜hqjf‚ù|˜ æó!&Zœš™`>fâi–¸853ñ4K\œš™xš%.NÍL<ͧ&&Zœš™xš%.NML´853ñ4K\œš˜hqjfâi–¸851ÑâÔÌÔ,iqjb¢Å©™ ¨YÒâÔ.NÍL<ͧ&&Zœš™xš%.NÍL<ͧf&žf‰‹S-NÍL<ͧ&&Zœš™xš%.NML´853Á4ËeàÅ©‰‰§f&ØþÉL°ý“™`û'1ÑâÔÌÛ?™ ¶-NÍL°ý“™`û'1ÑâÔÌÛ?™ ¶-NÍLÀý-N­1áâÔÌóù0ÌçCL´853Á|>Ìóù-NÍL0Ÿ3ñ4K\œš™xš%.NÍL<ͧf&žf‰‹S-NÍL<ͧ&&Zœš™xš%.NML´853ñ4K\œš˜hqjfj–´851ÑâÔÌÔ,iqj §f&žf‰‹S-NÍL<ͧf&žf‰‹S3O³ÄÅ©‰‰§f&žf‰‹S-NÍL<ͧ&&Zœš™>óšå÷ÃQøãÇzšô—/\–ßyrüÎÝáõ7Çå8æÓwïê¶Óº®yH äø¨5Æ2žòq¬ÓiwOoÝüW·Ó)—qšn^¹NiÎcJ7p;¦SIu¾yñ|ñ¥ÛGù4ÕeYnÞ~ÿ\þÆm=ÕqN¥ÕÝJU¿rûhhÊÃ\{…%­7oèç–qžo¾öÜ«¿w.¾Ð*LsNc_|é6ŸÆ¹}¨o¾¤+Kžs«Ù~l¬Ë:N7.Ê÷}ñükïï8?ìåóÃ^Ùk^üÂ×[s­Ë’×Ëg¶'¥e9ÕiüÑ݇q>Íõx÷§‡»×ÞºùüeKÔÓÐFäxÙÒçæwÈÓ¼½˜Šß8×|nëž[ô«ç×yå²éÎÐÊi™NµÌ7Çs/ïOxñS.žiëþZÇ‹VzþŸãþ¨Öý[Ý—÷fþú¹íÏw}úûïÅoK™Oc™Ô5ß½ksi<ŽmZ”u‡5¦Ô˜«þ¿‹mõ$Êó˜Êñ½ÃXÓ³e×yïð“Ã_;¾ÿŒ©i·eýñ•? §¿üæË¯nÆáçßzúøW÷ðé·öðåWú«_üòåWT|å§› ú¥ŒÆÏüMþõÝÏž<~ó§ñFÜðôo-+eóÓ Ì/ëïuõÛŸ`9gNWþü–qæ+~Ë8ë•׉¿]œ%]y‰ø[Æ)(S°T”)¨ e jA™‚ZQ¦`Q¦`Í(S°V’)ÈÃH2yÈ$S‡…d ò8LA3ÊŒ ʤe Ò„2iF™‚i@™‰Ä$œe &”l˜3J6Ì%挒 sAɆ¹ dÃ\P²ažQ²ažQ²ažQ²a^P²a^P²a^X²aeɆ•%V–l¸²dÕ%®(Ù° (Ù° (Ù° (Ù°Œ(ÙP~‰(”lXF”lXJ6, %–„’ Ë„’ Ë„’ Ë„’ KFɆ%£dÃ’Q²a)(Ù°”lX J6,3J6,3J6,3J6, J6, J6, K6¬,Ù°²dÃÊ’ W–l¸²dÕ%®(ÙpP²á< dÃy@ɆÎÀÀ)ãLÊW`N¾‚ÀÁ˜‚ÀÁ˜ãpòæ!p0'Æáä+Ì Bà`NŒÃÉW8˜„ÀÁœ ”¯ p0'ƒ9A0'_Aà`Ns‚`N¾‚ÀÁœ æÁ8œ|ƒ9AÌ Bà dCP¾‚ÀAɆ |Æáä+”lÊW`N¾‚ÀAɆ |Æáä+”lÊW`N¾‚ÀaɆœ|Æáä+–lÈÉW P¾‚ÀAɆ |Æáä+”lÊW8(Ù”¯ pP²!(_q8ù %‚ò‡“¯ pP²!(_q8ù %‚ò‡“¯ pP²!(_q8ù %‚ò‡“¯ pX²!'_q8ù ‡%ròJ6å+”lÊW`P¾‚\Qù „ÊW`Ž)0Ç”¯À8œãpN„ÊW`Î ‚q8'Âå+0çÁ8œ„†CÊW`Î ‚q8'Âå+0çÁ8œá€ò‡s‚`Î ‚p@ù ŒÃ9A0çÁ8(Ù”¯À8(Ù”¯@8 |ÆAɆ¤|Âå+0J6$å+(_qP²!)_p@ù ŒÃ’ Aù „ÊW`–lÊWÐpHù Œƒ’ Iù „ÊW`”lHÊW`”lHÊW`”lHÊW P¾ã dCR¾á€ò%’ò”¯À8(Ù”¯@8 |ÆAɆ¤|Âå+0J6$å+(_qX²!(_p@ù ŒÃ’ Aù Œƒ’ Iù Œƒ’ Iù „ÊWPT¾á€ò‡c ŒÃ1Âå+0çÁ8œá€ò‡s‚`Î ‚p@ù ŒÃ9A0ç¡áò‡s‚`Î ‚p@ù ŒÃ9A0çA8 |Æáœ ‡s‚ P¾ãpNŒÃ9A0J6$å+0J6$å+(_qP²!)_p@ù Œƒ’ Iù „ÊW`”lHÊW P¾ã°dCP¾á€ò‡%‚ò4R¾ã dCR¾á€ò%’ò%’ò%’ò”¯À8(Ù”¯@8 |ÆAɆ¤|Âå+0J6$å+(_qP²!)_p@ù Œƒ’ Iù „ÊW`–lÊW P¾ã°dCP¾ã dCR¾ã dCR¾ápò4R¾ãpòÆÆ‡“¯ p0'ƒ9A0'_Aà`Ns‚`N¾‚ÀÁœ æA8 |ƒ9AÌ ‚q8ù s‚8˜ãpòæ!p0'Æáä+Ì Bà`N%‚òJ6å+0'_Aà dCP¾ãpòJ6å+0'_Aà dCP¾ãpòK6ää+0'_Aà°dCN¾á€òJ6å+0'_Aà dCP¾‚ÀAɆ |ƒ’ Aù ŒÃÉW8(Ù”¯À8œ|ƒ’ Aù ŒÃÉW8(Ù”¯À8œ|ƒ’ Aù ŒÃÉW8(Ù”¯À8œ|Ã’ 9ù ŒÃÉW8,Ù“¯ pP²!(_Aà dCP¾ã€òäŒÊW P¾ãpLq8¦@8 |Æáœ ‡s‚ P¾ãpNŒÃ9A(_q8'Æáœ 4R¾ãpNŒÃ9A(_q8'Æáœ ”¯À8œãpN„ÊW`Î ‚q8'ÆAɆ¤|ÆAɆ¤|Âå+0J6$å+(_qP²!)_p@ù Œƒ’ Iù „ÊW`–lÊW P¾ã°dCP¾‚†CÊW`”lHÊW P¾ã dCR¾ã dCR¾ã dCR¾á€ò%’ò”¯À8(Ù”¯@8 |ÆAɆ¤|Âå+0J6$å+(_qP²!)_p@ù ŒÃ’ Aù „ÊW`–lÊW`”lHÊW`”lHÊW P¾‚eFå+(_q8¦À8S P¾ãpNŒÃ9A(_q8'Æáœ ”¯À8œãpN)_q8'Æáœ ”¯À8œãpN„ÊW`Î ‚q8'Âå+0çÁ8œã dCR¾ã dCR¾á€ò%’ò”¯À8(Ù”¯@8 |ÆAɆ¤|Âå+0K6å+(_qX²!(_AÃ!å+0J6$å+(_qP²!)_qP²!)_qP²!)_p@ù Œƒ’ Iù „ÊW`”lHÊW P¾ã dCR¾á€ò%’ò”¯À8(Ù”¯@8 |ÆaɆ |Âå+0K6å+0J6$å+0J6$å+'_Á2TR¾ãpòÆÆ‡“¯ p0'ƒ9A0'_Aà`Ns‚`N¾‚ÀÁœ æA8 |ƒ9AÌ ‚q8ù s‚8˜ãpòæ!p0'Æáä+Ì Bà`N%‚òJ6å+0'_Aà dCP¾ãpòJ6å+0'_Aà dCP¾ãpòK6ää+0'_Aà°dCN¾á€òJ6å+0'_Aà dCP¾‚ÀAɆ |ƒ’ Aù ŒÃÉW8(Ù”¯À8œ|ƒ’ Aù ŒÃÉW8(Ù”¯À8œ|ƒ’ Aù ŒÃÉW8(Ù”¯À8œ|Ã’ 9ù ŒÃÉW8,Ù“¯ pP²!(_Aà dCP¾ã€ò䕯@8 |Æá˜ãpLp@ù ŒÃ9A0çA8 |Æáœ ‡s‚ P¾ãpNŒÃ9Ah8¤|Æáœ ‡s‚ P¾ãpNŒÃ9A(_q8'Æáœ ”¯À8œãpNŒƒ’ Iù Œƒ’ Iù „ÊW`”lHÊW P¾ã dCR¾á€ò%’ò”¯À8,Ù”¯@8 |ÆaɆ | ‡”¯À8(Ù”¯@8 |ÆAɆ¤|ÆAɆ¤|ÆAɆ¤|Âå+0J6$å+(_qP²!)_p@ù Œƒ’ Iù „ÊW`”lHÊW P¾ã dCR¾á€ò‡%‚ò”¯À8,Ù”¯À8(Ù”¯À8(Ù”¯@8 |KBå+(_q8¦À8S P¾ãpNŒÃ9A(_q8'Æáœ ”¯À8œãpN)_q8'Æáœ ”¯À8œãpN„ÊW`Î ‚q8'Âå+0çÁ8œã dCR¾ã dCR¾á€ò%’ò”¯À8(Ù”¯@8 |ÆAɆ¤|Âå+0K6å+(_qX²!(_AÃ!å+0J6$å+(_qP²!)_qP²!)_qP²!)_p@ù Œƒ’ Iù „ÊW`”lHÊW P¾ã dCR¾á€ò%’ò”¯À8(Ù”¯@8 |ÆaɆ |Âå+0K6å+0J6$å+0J6$å+'_A2)_q8ù c c ŒÃÉW8˜„ÀÁœ ‡“¯ p0'ƒ9A0'_Aà`Ns‚ P¾‚ÀÁœ æÁ8œ|ƒ9AÌ ‚q8ù s‚8˜ãpòæ!p0'ƒ’ Aù %‚ò‡“¯ pP²!(_q8ù %‚ò‡“¯ pP²!(_q8ù ‡%rò‡“¯ pX²!'_p@ù %‚ò‡“¯ pP²!(_Aà dCP¾‚ÀAɆ |Æáä+”lÊW`N¾‚ÀAɆ |Æáä+”lÊW`N¾‚ÀAɆ |Æáä+”lÊW`N¾‚ÀaɆœ|Æáä+–lÈÉW8(Ù”¯ pP²!(_q>Óù ¾ŽúûÃ>8ÔÓ¤¿|á²üΓãw9.Ç1ŸóÝ»ºí´®kRc8>j±–S®r\> õx÷äðÖÍçoåÓTkoÜ>Nc]ÖqºùÊm= ©óÍË·Ói]–¼Þ¼ýþ¹îK·ù4ÎÓ”n^¹Ni.ã<·gm_:×|ûÃs….~á…ö ÓœÓxóêÅm[éò·¾áLÃRo^l7Õ±<ó—}°?ôeU(˼øYÛc_å%Û3¿üœæxæÚ^üÚí£´L§<”g~ò‹Ïy»WnK™Oct÷ÆaœOmtüéáîµ·>ÒºqIkÿ÷éGßÛ¿º×|å“z߯VËÜÀöþT´‹—üîÝASãÿÀ¼âÕendstream endobj 101 0 obj 21414 endobj 105 0 obj <> stream xœíZ]¯]µU 8E¨*Dû²+¡ê¤»k{üùÒ‡JU%Ô>@n6¥|$„ˆ&HP‰ªÿ¾³fÆÞ>7ç†$\@‘ ]øŽÇcÏÌš±÷ùjq«‹Ãÿ:¸u÷ûwËòùv_íBhKrq¹/(ÞÛÅr+¼»{ÿ­åËhj­%Ϻ¾þ|¨ˆÉ¹%;þ×ןíî¼µ{g'«.ïþÙË~µ«+á˜ñ­ûËÏÙ._ +«;¿#+ñBÑçåÌ/gnñ9,¥¬),ç÷wûŸοØýéœò Û²R+-6¬wôßW±¬[s]J^/.ü$;lß²Aï¢íðæ~yïàV—rÈ%í—÷g~Mä}Ø/ðJ-%òøÍÙ[CmÞÇýòñÁ¯9R¾ ÿÙ!¬T’§ýòÃ\Ù/9”Ìó"”$Þ*”¼†?Ä–«ß/Ÿ¨ %°ÆOi bØ”'Ös}j¬ü†¨¬­æýr‡gòꮄ 6Î*ÕÞè=lä¹TÕÙÈŒ\*ØÐ±áÉxþöUEÁÃ]#Aà­?9çñó}dJl´ºjÎø[R“«m¿|>Yrã¼_‹¯?l˜ÄÆ‹æÇ5íû8¤”©%˜¹,1÷ñá,¬L;Änþ ¬±""´Vˆö |CÔý°Go$ÿxô}gþ‘KMp÷D}Oµ®•YÆ«5?G­Á9Œ9!¬©JùùmlY6sÍlì¿$ÁcD¨É†³PÎŒÀ[M!ÉŽ˜Kj!',Às½S⹬8gäo]#O-Ç×(yÇ›BA©¹©ëþÇ$1)™„?`aeË‘ÂÇÖú2ÕQ9Þè¤ûxŸŸL+}|È|T-Õ£•f• ¯H<ýÔ”L©\0ñŒxfLõHzVrÉöooJ˜Ž»ÅÅ »0ccâÞåM£Œ«Æ”ŽGÕ”ÜÚÑQ1Êq¤õ°9 yG­=˜b;åA$Ê%2ÃÝÓ:nËéPsþHß<›Dž44¯ÄIßÝiÅ)¤zîú )pI0α£É“k˜ÃåÁ=ŠìûSœm“[Û7zòô“•>>|§ÂöЖ<¤¼¶å2Ãq°-]>™;ÎOá„ÊÆ(dˆ‘S½œÊ[$tGçÖNNíÜùÔBÎ-áÜÁÜ9D¯J°'µÌvÖòc_ÙâxÁb+ÏÍ]l§µþ}Hjéj4Ôåsçÿ“.÷ÕöC¡J÷ú8ýÐÜ™|2Uœ©´ÌmÒ,2S܇¼O¾` šSܬÿï MŸ ×oyv} ðD7 ua’¾¤½˜ËÅ<óx EÛÁ´6&òrÌêìºOï`½líóÍêK ÛÃ7 eãò <è…¾Ã8âÍñ ‡5G GFŠíQJpùï®.å }{çÖš¹aªËƒ€Ãø´žOÈõ±®-…yém¿ãøŽ /·¦íÖÈûm¸ÐÉvÞau¾å­Íkß)ïn'<›ã¶Òó8æ|OÇCÌÃÔí)™8¤÷Ëò<ÂüȨŽZ¬ý¤ò…N»5µÒej­ƒ¨‰Ò ç O§F+“ó÷|ÌÄ7ôPÄmÝ\’[ˆ[©åC6üöŽÉ6qŠ8¤G÷d(>-Ä=_Q‰ó¡$§© ¨ÙÆ}êãœFΕ:NŠ˜a™ÇÞš Gâ½7žÉþ]ˆïGŠní"ÏT|qªŠ£k6Ëë‚,ãàÙ€{†ç«Ì !aëtk'k¾·“•‹52KtÝYÜ´óà^çÁ{WÎðËOÝvî²K(ªb³`Žv*lv:Óën°Ù©î›E¢µ>«5ÓY±Ù;‘Uûyõ&6ã,šØ,g!kfÙØ ‹5:K‘7K‘„†àà$4{8³‚ÇN¡KЭ¬!˜e±²JPì³¹éO›É8æ·Q® ¯{–G¦p¡`ä)(B¦ä¤˜uj&QƬÒu±,pª¹‰,p‹àYYv ]ÙÎkd; ¬,X¬‘YŠ KŽùi3ùî1:',_xê}ùp…ŽÜ/QN–‚„§ ó’¹d“ÈcVîº Ë85Oq±ÌHfŦè–‚`$&¯¬4fQ×eAþt™|w4šzòun3¬Éˆ|±u‰1.ãW‡3¼c×öÒ×\ãþ×Ò0´–SÛ¿xðÄ×+_ìûçÑCp?AmÿÒá,®bÞÿóëñ÷—zΜ®q} ^èŸ_B)è_¦ÆjÄwgã2jdlS ³¨ëÚ‚ì)2ùjƒŒ•¢Q— ûÝpø«›ë¿æ bßãÍëcô—Òçæšâí•ÃY^[qDSì¼0éŒ_›”±Íèu\gžÚZ+~œóäà»?Ï÷œJ.ÆIrÞ”×ô—1kã…ufD0µŒ†¬SøZ'EÜ-ÆPs·Q0D‚oØ:Kè‚,0 «HÆ”¹óÄ|Aè]ZcþB×w_=i7©¸êo”"o*µÞÌûŽ5J]Ìòª“cHÜ‚ëPå”ó Ù;©Fø\PŸû"XOn—8ü —¾ç:vI\2‰Å¹3™¾2á™ØžÅ{2¥2s÷©b¾=¥_­¤˜j@0#¶ 5¹+nÈ;>ÊF-ÔkØ ;Ã(í35ßS|’Qn‚¼j&ü½ðõöM´=ÚñÅ /ÓlJOŠ_xä€ÈÒ3|õÀÇ–‰c˜“8ÒSä@cv”Ùsßôæ¡®.àÑæMàV¾ôìŸA…w^ /ô¥yâjbŸ”¾1'ç,~êñ—Ýù[7¹x ¡×O¬1¬IÒÅÙ(ÒùÔÒ¯uëóck8”SMKYЫ¤\+®á™«éÕN.0|lv×á;IëW™Pši±›NpšRŠq/r¡DÀ¹PjDr¡Ô6(4Ãb‡ÎRÔ3º¿ò lŸ»¯ª}ÃB)H$¿tÀ'àìÊþ¹‘ó[ö_CŒøJ•¬Á™ AÏ'çÆO.O!ŠuEEÍã•ùe‰.®=#&ùR>Šk*\‹¸OC^•ØÐ‘õ@½6$¯oó{b/‘˰x9Ó|è{’²±8oI½ônКCIßÃ<@AŒ”- /ˆöUÃ…O“[A ¸æ…È Çôº4"ù2q^Ôó$Æyö$wÔ/`pÕ2Ыm+Á1»›;s–Wúiý79&jX ÷ÊÏÈÁ£IqÒõÉÇݱ—SЉiÏ­×Bgà_<^¾)–ƒŒ)QNFnáumSñ"T¤ÐòsoœŒÔÙv>^0ŸZñ7CùlõsÓ¾§ãšŒÚ6þ¸–œŒJ}*Ö"Êä6~Ä}ܹ«v~pË阜£îõ1º]Юϒ»ÈðщN6Ç#K&7(TwtÆ/Œhxõ´kø»ŽBc8á‘ýA ¾’J£$ØëÝ!&<æKkȯÓ9*Æût+ŠóˆœâÄ8ëÕ?ãfzû7Ø\_+¿Wì3y•¬‡{)Å„_£TÅžqSyb{²×qb¸ÅVŒO™|-Ìò¹f}ŠàµŠ3Ìò%¨ ÿ_(¤ë^«˜&GùÐdÏŒu¿zìxy¦(µ?%`’V UuúÜC§Ù»¥©ý?6°'WÕNÔåêt.| œ€U?‡cÕé8=8.§ ûIÆ Ì—kà¬öø¢Xl¨Š¡'xÅУ¾–‡ôT¾BÆUì…T…jŠ¡'êÜ ›£CgRùÂç_³ê)Щ±Á};ã¢s tV}Ì/دž?èl&Ï:›ž3÷æŒm¿™÷ØHõ°›CK†ñ<]TyÑLOÃ]‹M$u¿ÿÜ©’bnœ¼“X‡òA炼Æ'cU—Œ`ç¥ý2Ì…ŽƒF0^åIã¡ÚI¬“4Ùhýl:p1*°öt|Šís“ßtJãéÃØ¥âÛìòf3΄iªŸÕØ#HÔ~)}Ÿ’X÷´7°=3Wè9fð•ùOŸÁê>r?h^Ë[§·>¹ßífZ'o>ã=3W”¾7r¼a:‹ð°úߜӹ¨Íì”Ü×Ú‘ïÍz ä{3{ïÍ|Ö¨QòÝêµä{SòËôK¾“oƒ7’ä»î+I¾k¼&ÉñdãqÔ»$9«žI’³OÉm’üÆ!IrUkVòaðI’¼íØ?Âxœ°Ôe|öYÑ7-£Š'oýAää— Âê‘„nÊX(…8:„òèRØØ$!ó­ÃI$ÝQU =ÎÆitP‰Ðq5σA’d»FK"°‰VdùUEÑ G²†¢Lž"®/ŠFH#X—˜"tj„$a„f˜ç梞BWœM:笌“Ð]g­zx0I;Š”¥ãUùIYoˆMå‘ù1FGm~@†G­hPǬ¡zŠtæº.ª$9µ]S(6W:ü2°×ª'6 œ7Ø,ªÑ‘ù{ék¡Sê6€õH«vFÚΤïg›G0¦Éœ‰®‹[CRö—ó϶_ajeOñ]¶3³gÛ#üžµºhlø°Å¡T”hq‚ª`z¤%ÝW€ßõf$±j’DÙb»T‹m‰OÕ)7­šf¿ä‹ùErËâ\rÎ씬ÔîT3×rD2ÝÎJ«µÛ˜ÁrêÒñpzܵ WÅÆAÒ_&i'bÌ$?B›PT$²aôʆÖÍ´ºu<ždÌ+ìS6¶5¦nr+0†&uƒÁ«ú)ÊM£³<ÎÑ:¹iXQåV`2Ði•_n-÷*ÂìoÕ¥¢*„^i¸ZX¥EE‰VP-ôçFR±›é—ŽÖôÈ+€í=ËÏo¬ò9tÀÖA òYÕAÅõv&è¶ÇÍ?Ø±Ž z¯¦èæ{•E—¬ãÀM£We>>iõ§­Šã e¯î¸Œ›9õßq‘Q¼~ABö·bÉ/«ÝÙ½³û?<@xtendstream endobj 106 0 obj 4220 endobj 110 0 obj <> stream xœí]]“\WuŲl¬ ¸HÀuËÔ´]ï*Ф”ª°=Ø[N0ƒeËÑ8eÂ3<óä%O¡HHoùððbïµowßÍ [j i«<µæö9ûìu>ö9k÷tŸ÷7ú08ý·‡‹ /Öá­ï/Þ[„ЇìÒpT^_¤z|{ñÊ3û Xê½g/¶n¼µ6‘²sCqòãÆ›‹«Ï,^XøAÿIïs¢Š)õ!´¦BmŒòßðÃE.KùK 7¶R}kíàÅ¿>ÉÐK Ô¤Í÷&Ûæøàpxv_º ©½ ûWÁI(%_‡ó~8ïïÂÒÐÒØÒ°¸xmoøöRx—]Ú^Zž·b1î o.Ïù%D¿7¼"…Šoµ þ+<¯5 ~\ž÷œ[m{Ã7P9¸PòÞðšþbòN~ù{¼Ò£Y:ïÅjì-¸16ç{Ù®ªYïCGüÚqŒ)W©ñ]­-~„¾n;kÛÍÕR¼4—ä×zƒR>õÒ6FS‰I[“–ckúÂãËÃè¢}ÿÒâùýõëÀ¬†ún»]¼štÛÿwú‰þÍõâ…H˜ú}Þ)¯)÷Ò²vÊ;ú\h•ÖÝ(ëQzh@wÅZšüråŠR ¾6tKn]ÖQüˆ§MñÒd»'îdÄ[È´Å ¬”¬cPë˜1{-÷ßÙt–8Ök—ˆ7õÙú÷]´Ç†ZÆ£ ßÙ<»-GïÒDR†éœ®oß’vóS3¼^^‡Rž7Wz]XëÞËðýÝR†µ¤(åă¢eâ˜r‹² ßÐÅ&c]ª¬¼ï` ;]`KÍüžVÌÑ窫_žú¬küÛ©k€xCì5—BÜ.ý²Ù®!k¬È¾ë [SüîFí÷w%MÈŒ=ùaƒ…ÌøàŽv^\u^Oc-ƒDóiÊŸÒ)¯¾®˜kÔãÒþ÷´su‡‘€Ÿ¨Ã˜bÕxjIâ…<~U»DJ¤n}"=n4×g6æߘUœ•~Ù—`C(ãÐe$°+ù± ß> ¡“Í®éÅëú[Ýë^9klò¦—£æ¦f…Î-½TõÅëÄŽûºq¨­om'3ø](/ßu»›¶²PÊÖîû4ö/×Új+|ýCM[ÏŸÒÍLz^»ðøï˜}ôwÄ«Ü4lã¶üÙN¹½#ëù(•˜¼{hÌQtöb•^ÖË=ØF'Ž[‘mkIΗí|yÎNÇÛ§i=„ë[Ï„.×’§™8ÙØZÖ'¬Ô{»N=U÷]ì|Ø»´{dØ·7ú žÅÖtÐ$gZîGCx’@0ÚRVoÌ´Íw–By¬ù^ vw¦·7ƒ}ñÞé“ä¥~$Ý|¯™™i··g/1WtH”´Û`´ô´YTŒy9&Ú–%G¸ÖòÉõ^‘-%rèüè¶”TŠæÿ[Šl¡÷b?™ž¨Ìæ lþ|:{éb›¯¤ÙãWç1y8_^æ,g”ÖîQÑž³=/?\x‡7ÓßÙDÏËÛ‹—>&i»›ÏÀð4)*ð¾:CâGPŸ‹ùóTBŠ–É{ ³ œšù ÜÕg­%~HYµ$>kÀâ³¶ oPËP[õÿƒæ2ºY› ¬ù OE)ŠZ„÷5?|œžŠ!OÏÅ2yd¶S5Ÿ›ú¬µÄ)«¶€ÄgmX|Ö–á jªën~À\F7ob‹„¥Þ‹ÃÞ:“T=\Ää&|}ÂÕp4£Š­¼aé µ)1QÿHFÔy® dlSj|cäPÁ5Á`Qßš,y &cÓºønüRºwÚôïé(§Ë¡O'|Ýp*vj¼›Cp °ŽZQãN½ æ­WƒQ½*êíª‘©£îÜ/tÔ6}rG581õ¼›ðué¶Éi#×òzD?@G­Œªñ¶Ø°̢à TuX×XGÝ…_ÖQwÖôïé(7íh08áë†Sž°ö¸"ÊOøöµ6¢ÆuX‹ kÐÁ,:¬†tX×Luç~¡£î°é—>”Æ]¶Ž²«¤SÑ8 ÇyWnÈ1L¼ÌÚ½Óršk^OmŸÛ<|D³î¾ÖP÷Åɾt‘×¼…¬Xö>³){jSöÓÇZxXdE=ÌŽêÇ~F'c6!vÅyËFçΛYzÛÁßQã:S5tí÷,SÕP‘›FlðïÂ/ü;mzGƒ5Ç1|ޱ=²ÏÆèÔúá£Ë6vÙÓÞ§6/ϦƟ; ®¼»Ô·ÜÚíÇØ¯ÎRÒŕա8%ÃY#…-*”ŸðíÇxmäPhGc•ÉâSƒ²Ê&$?6Øß…_ã;lz7c¤GRÉøS<ã?Yú¬¸˜÷ÎÚp…æÃý²¨Ãè5Õùy¼ÁVdîÙŒçÙÍ(ϧÆ:|aÝÀ]ÎÈÎ$;дÓ`gI†±sØ:Cù ß~>¬èŸRë $[xØ”’.< ¬ o݈gïÂ/̇;lzGó0iµä[ɲ xí­tÒ|ø³õÖ0ùÙ̘M¨Ç6fÏì,Vø¼҇Ε•2aŠ~ïÚw¶>Q~·Ÿk#‡jD Öw ,Ø ýã´U#67îÂ/Ì;lzGsÃëæÔÆ)TlOˆ5~o–èý|˜ïr@csë ›B^Y੃N?Ó‰ §¨ü7FÔ¸[Gà„ƒ•Fà ¼55þwã—è6½›MM)š«´ÓÝ»›˜? ÿ³ pz)Çjœ¯öO?æø‰‰‡ä˜ ƒPûüü·9;<±»9“Ýz±Åž×‹ xZl‡&'”ÿ A`cD»õJŒ8éJœPÁ;·ó p7~AÜaÓ;š3ØqÊ]M˜¶‡:–êrÚÉî` Ò‘ ½tÖôGÃ5èŸ0É6;Öiò?„÷»eît¹Qÿt(diUSùú¹Ïãõ“kÒêêõGôïýJ”É'\Ò},yãɱYiKG—ZÙ¸½FrîŠ[rÖ–U/HS)@r"мŸbÙ)’ï†CªÝjMHlIYÅr¢Œ(«ØG-áuAj è`6€eÑ–á jR[¶y=`.¿ýágü‰óEÆ]çKÉIb€Í—'1ªLâ ú¸Î–"‹`ïø`J(rØ—8·ž#Ÿ’8×|².ÚèB¶c‘̛֚—Ã÷ôêlVé_ÔÄQæÎl*M1n6§tFuA¹ÖŒ³b §ƒìI:Z;ßiV9Mç§z.-a*!êtª¤¶´¬b9()R3¨$¯«]`œc¦çæÖÚøu`aµÔª‰0ý|To)™÷ïôd”ûÊçV<MÃ#-i­½8¸­·ëë@gÚpwݶÃÕRºì™C)A?™Õrf½Ff«áôÖ"™æþ'+ùðfeXJhö'vº¦#”aÍŠªÒ„¾lÖ¾‡nhšÀA¬Ù«º²°¥ª¦Œ pµuö~‹”î†ÔlØÓÉÆ?CÁÁ†òkË2ÌN=’í&xTá‘ÏizjÖr?êQÕ·),Sj¸©ÿ¹ˆpï8!<‰šÓS³¶¶»²¬Ùá¸i^¡ö8d»âNV_¦§f­í-{“usäzO6†  …†…£X† ¥•¾…Qfý)ä÷Í^xC\ªAgDýhó³.^þéÆÞ\\xE^[\ø&~>û­ç¿¸ðõ×ü½¾¸ð Ï=÷ ùõ[_þò…¿‘i¸¸ðÜ?^ÿÁá»ß—Â_ùÊââׇ§ÿë±ßœþÌðÕ›§ÏÈ3Ïÿüæµ_ŸºxîG§¾yöܯÒûûòã‹×¾tóâoß<úÂgo^üùÏìùÍKï_Îöøì¹¿|óÚ¹ÿ¬Ö.}l8µxþâðÂ1œªÏ|œt*°q’5ÎÆ©yÏÇ)ñňVùbDw|1¢G¾Ñ+]ŒÈÎÑňì"]ŒÈ®Ðňì:]ŒÈúGStœ _Œð/F„À#B抹ñiwåĦÝÁ‰L»ƒ™vWNlڜȴ;8‘iwåĦÝÁ‰L»ƒ™vNtڜȴ;8‘iwp"Óî¹ñiwp"ÓîàD¦Ý•›v'2íÞŸvïŽO»ƒ™v'2íÞŸv'2íNdÚ]9±iwp"ÓîàD¦Ý»ãÓîàD¦ÝÁ‰L»ƒ™vWNlڜȴ;8‘iwåĦÝÁ‰K»×è´;8‘iwãĥݗv'2ínœ¸´»qâÒîàD¦Ý—v7N\Ú]9±iwãĥݗv7N\ڜȴ»qâÒîÆ‰K»ƒ™v7NdÚ]¿€M»+'6íNdڜȴ»rbÓîàD¦ÝÁ‰L»ãë\È´;8‘iwûÚºA§ÝÁ‰L»ƒ™v'2í®œØ´;8‘iwp"ÓîʉM»ƒ™vo|ßUNlÚ½ñ}Wq"Óîï»êŒ™vo|ßUNlÚ½ñ}Wq"Óîï»êŒ™vo|ßUgœÈ´{ãû®:ãD¦ÝßwÕ›vo|ßU×<ßwÕ™v7N\ÚÝ8qiwp"ÓîÆ‰K»'.íNdÚÝ8qiwãĥݕ›v7N\ÚÝ8qiwãÄ¥ÝÁ‰L»'.ínœ¸´;8‘iwãD¦Ý3ßwÕ›vÏ|ßUgœÈ´{æû®:ãD¦Ý3ßwÕ›vÏ|ßUgœÈ´{æû®:ãD¦Ý3ßwÕ'2ížù¾«Î8‘i÷Ì÷]uàĦÝ3ÅwÕ½°pƒþÃñmŒúÌñÁáðìþ⋾ ïaÿªÖ{ïI8 çÝ|SrécÃþáâµ½O/ƒ<’Aß{b>·FWnlà»ËóAÚʱï=ºÌ¹Œ>û×÷/-žßçn½Ç>æ’õ †CÃ97\_ÄæÊF™cï±—jQ_»»/ýÏoÜOþòô¿ OÉ•¡ûß‹ç{ü«?:÷ËôþåOqéæås?}Ù†áâ‹×~ñïó ç~yN~;wóçï_.gl|홋éæåWOŸ9'¿}ñcgºu.ŸîŸX¸ø”ÆÄGÎæŽ‹O¤Šr&§Šr§ŠÙ®xà W<ð+„ÀB抡qÅ=гðé™K/(&½>Dz|ˆô‚ð¡Ò àC¤À‡H/€‘^P>Lz|ˆôøéåäÀ‡H/€‘^è™J/”P©ôøéããŒ^P>LzÁøðèããŒ^"½`|xô‚ñáÑ àC¤Œ^0>Dz|ˆô‚òaÒ àC¤À‡H/(&½>Dz|ˆô‚òaÒ ÝséåäÀ‡H/€‘^>Tz|ˆôøéð!Ò Ê‡I/€‘^"½ |˜ôøéð!Ò Ê‡H/ÔÀõygð!Ò Æ‡G/½ |˜ô‚ñáÑ Æ‡G/½>DzÁøðèããÀ‡H/½`|xôø0é…ÌõygðaÒ ™ëóÎÆ‡H/d®Ï;"½¹>ïl|ˆôBæú¼³ñ!Ò ™ëóÎàä2×ç‘^È\Ÿw®ëóÎàä×ç‘^h\Ÿw6>Dz¡q}ÞÙøé…ÆõygãC¤×çÁ‡I/4®Ï;"½ÐøÏ;ï䞉”ú(§òÜÃèšÝ3ñðüöˆãî”8»<ïÆÐ{É}ïÉ[î—ˆ®y½±émÀ­×!´¨%A¹vC‹㫸`u…"^iõ~T;¬÷˜ÅZÑ«€E…¢Þ+RkÉOOa#¸~‹5øc¾w-Ý’}Òv?Ý|ËizjÖB8j­ª?É|®^¦Vĵßv“!±–ðÿk>¦#Ö’S Ö ×–q"š—ÑådH¬åR&ßÍZ>ÊÔÍɽìÃë ‘áf¹f±>açÕ¦ $Ý&} ,bÑÞU|]x¥0•h.Lµ éÅ| àâs³;B]cZKúRʪ- ƒÚ–²Ú20¼A-Cjë`qõ™Îå·µÛ?øŠìyðI×ÞlQŠ3¶,c £l¥ä±MËòÉeK¬¾ÌÐÇ—iô%ư÷ ]“9ˆ+E°,Ïeø÷N/ÛèBîuïÔæácëúgÖè“Ëóiì^æÔÞ–1I>¾§G™G뎾8f6¿„mŒº [麀‹¸-~IÍ:XKyª<=•.-2Å­Duuª¤¶¤¬ââ²þ©”,PH^W»ÀbK[³êÕZû…åÆRk••'ÞÇÞRZy¿òN½oaò(iT€G@“GŠ'Û-¬Ú[yqp[/n×;âåƒÐ™:×wÙm»[9)´QÌV]¶p6“üÔ|alÖË)Y$Íç ‹à˜’¯^}ä–­.ä N­E#TÎÀÚ{²Å%ÅXDaÖ•"¸èíÔµ%à\€%j nÀ:¢µ;à¢u»Õ…͵·«ôƒb)*¸F]´[DÇæPW÷%ÁÑ°ŽŠCÝð¼ÖÑuVW„_ÖrËV$ØêjĕΘádXëú:Ã۶åÛ × ÆÅR‚‹â0ÃS[:sCžáÉfWl6“›aé=e«hvôV±ÍŽÎØ͇‚çÆW/½jÉøVµ“Œ—n©-Y»Mm&k«+÷„ºØYZF݆~ÎѰú™Ñn À°ÓtÜvŸÐt/kÅê6­[P·cì Ê÷ íøÜó´šQv:¸LåËÌŽ•f§®Û2ûÙüo:¾öêN>£Ÿs0Œçθ¸ w¯u“Õ…ÍdýÜÑŸÖÿzÒ^õ-8&£Š±°þ,Z&Z?Œ£õ?ü67rœï|”ÍÜHi3 §ÍܘæXŠ36óаù™0çû çÍ|[­…>Ãê§w·b¬©¶YG«uW7ë1„Ù:­_íÛi]W¤ëõÞ6q ûM|hZ·X¡å›Å„‚Øbu3bŽÕMÀÁbQWì61jŠcˆ'5ÏbêN±íN1u³Îù5n›ò†ó¬|ÁEr!ëÌ2±REí`¥À²Y©®¾6 t«“àj®c`>]=æb;9Î{; .ÁÖ².ƒ9žNŽÇ\l'Ý¡ÇÍ]_nì~òô™¯Z]$ø«s?½üâE{?{õÚŸ>³º®ðÿ~{1ýòÔé›"o¾píÿ1UøÑ©KòÂÍ}\IøÍ/~öß®ýzcL´â3¢8oÊÿ?ûØWOß*~'N»¾Üø>à”w}¹ñýÀi×—ßœv}¹ñýÀiחߜʮ/7¾8íúrãûÓ®/7¾8Õ]_n|?pÚõåÆ÷§Î#TBÐqÊ|1¢5¾Ñ=_Œè™/FôF#Šót1¢àF6N•.FïébDñ‰/FøÊ#‚ã‹,g©)g¶<,8‘åa•[œÈò°àD–‡'²<¬rbËÂYœÈò°x›—, NdyØìùò°Ê‰- NdyXp"ËÃ*'¶<,8‘åaÁ‰,+œèò°àD–‡'²<¬rbËÂYœÈò°Ê‰- Nd9Ëùò°àD–‡UNlyXp"ËÂYœÈò°Ê‰- NdyXp"ËÃ*'¶<,8‘åaÁ‰,Û"_œÈò°àD–‡UNlyXp"ËÂYVèÐåaÁ‰, NdyXåÄ–‡'²<,8‘åa•[œ¸r–ø²<¬qâÊÂYÖ8qåaWÖ8qåaÁ‰,kœ¸ò°Æ‰+ NdyXãÄ•‡5N\yXûÚ>®<¬qâÊÃ'®<,8‘åaWÖ8qåa•[Ö8qåaWœÈò°Æ‰+kœ¸ò°àD–‡5Nd9Kýên¶<,8‘åa•[œÈò°àD–‡'²<¬rbËÂYœÈò°¸.ƒ, NdyXp"Ëæʗ‡'²<,8‘åa•[œÈò°àD–‡M•/ NdyXp"ËÃ*'¶<,8‘åaÁ‰,«œØò°àD–³¬|÷t'²> stream xœíkeÇužš–¨c ¤(;€“€8)¥›RÖ½ö E¦LEÈ€)µD‹GòP¼È¤cä³ü9‚IòE@€18@€ü†üù?Yï»j_zØÃ£óƤ5^sºvízê²jÝNóÃc8Åt øwnß9<ÿÝ~|ç/Rš5”ãJ âû‡Òï‘]|÷ðêsÇìižç­¯ÞY»(5„c öÇG?9¼ýÜá•C<â_kcmÇŒF¹”ù˜¦)FÓ)Û?ǘŽ/[û—á4µ§éøqá»ÿê~}…ЧíyÎÇÜK0²!ÇrŒÅþÈÍþ‹ýˆÒíClF@ù}“k-Zoã)—ì´5¹¤èm)çùȧJjáȾ(Y[¼ƒ²µÅ›½FãO¹1›ŠvJÕ&"ÎÄO\3B?b,ïÚzÄ\Æ»ï,rëÖªÙÛ[ihŸ\²¾«­eŒ#ôÑ¢¥i<åÒÂ7KlÞ–²uħøvö5ÆÁw,|x³·Àhü)—ZóùÔ†Œiö=bûýÃq’2?ØË·ï_¸´oK9æv¼|›'Øp‰ýx¶Ž!R9Îád?¿sxýìøýs;ä-çPÎŽß;¿°ã•kÊùìø“ó‹dI9ž_µF-N}6ùkü¼÷jòÓöù\ëÔ§³c=¿ˆ§l=¥{:ú>°<·³ãkö@)©´²vZæ­£ŠŽþ§k›ÏŽ?ÆÃ!˜:;¾uŸ Úäfï}fÓö®ž_äS­Ý웿ˆF&·dƒxÛ~ó4My{W滌fÊ{šÒra¯ñdãI[£Ÿœ×8ŸjNo^¾txñrUŠ8Ö‹®úU×ÎØŽÓtª¿v{íV•ü)Î-T¶1”+‹×–ÅkéØû©&,ÝÙ?8¿üé¶IŒbî³Ýrc¯¬¿‰×Ú´OÇnWÎ=/þ!œC„¶9ßÃÌ—ZRíXžp ¶j­/ëSsŒ6õožÛ*»X¸imö”'¬¹m»:Ð^Y{«÷60¹Ìm²ýôgÖGñ­µt½{gOöèOMNÑìÛ[~^»MqÌWN꯶Ÿ<3\ƒòé›O«ÅlK¦pïtäe:Lƒ™g„cO>†=‰)\ŸIéT{† {ù¦ëß`ºz˵»æ1åTíÌÿgÞÄ2íLµÙ¥ìSrï JÂ& ÌçîQN—½¢5ŸO›­”ïíåGÖÞôcÆÄŒ2c×Ï[›¸{Ñ?31›òŠ÷>wQO-Ï!.#œJ½ÒÇ÷¹rmjê¶Öb§c)®ÎéÅ2A© &é—˜Ä÷lPÕzž®¼ñ*ñõ“µ› ]ƒ×v³¿ëðÇ»ßÙÍòíÉÈ)¶d<ƒ×Û=•zY߃»f×á}fí»^v~|€lññòãwv«³ànæÎuj;ÍŸÉªÍæ¯ãBˆWó6ž[Äg§²¾g7°­DœM:Ùga¯’?ÅÐÖLµ Û܇óêîØ+è·Ï‹)î9”«M~d›·•ãÕ›Àmªiޱ`ãg»N ŽñÎëœO½þzg»e`~f|7z‹¥)óRÿ·Øõ—Ò^_î﹫Šy1ìÔ1Kk¡â¢7‹9Ç1s×]Pßs›:‡ùÊ®¾h¯ýþ›BîWTìåÖ埯:ó~ºûÚ«íÇ×kæ«*=c?¹IžìüÙ,çpùœÍ㟢'Û Ó|uööoxÓM >Ñ4¨§Ù î¹Ñ8Lô€ûÕüø_î‚Ýßv{ûâfGa›+]íd·a^„E1…xuͿݚ`Üí_Ý%9M§;ïã›îÞËsw½î's/&¯öé¯,Wì>VÃõ&Æ~ŽîsN®lêu޶ üUSÙ¦qR}xxž+~ô\àš-ý:nðÁxå¿ÆŸðÕÙù0»6{¾o¿ù0§9uD ½{ó—bÙ7Ùk|îEëÛÿµMῊÆÑ”>”h8Å)ÆZ®Û÷¢k³·+&ìFoÎbÚüÓ]œßâ\Mf†E†ªaªÕ&å_R'L¥gLËæH0¨”æÐ=äµéüšùh‹Rs»¯jy÷e™K®ê–µÁþ~¼þÊ}à%­»ÿø>ø5êüšÓ=+tná}'÷0¹bÅ6{s»6¾Üùw×yñœŒû¸ß÷q•{Ì#ñaÏî¿ ©—KŽõ!Î/&c1—b»_ÔáÚͽßý7bÑÝ×D0}ÐVãnû¿cæM²6Ý{6äݤì,_*º³7¶¯XýCo÷*ǹ&Ìu¨%Mù¬í×àãüÜûí²—oÚÀÿd‹ñanƒûú>÷ 3îGº{v7gûÿì¼N³]¸e³=ïÍX÷)k L?R†§ZŽ5#¹h—¥K·Å¸üþ¡¤V].9WÊ%ôem!·˜‘Rt9%¦r!Í&¡/J·|å÷|3eކO¹„¾˜1Ý2ÔØØ™:=Õyna>î–!§`C5»ÏP\¶“n¶]j6¼ xí.Ý>ä:W—ß?ä¶¶huyŠú²¶”熜½µl¶[Ž|ž’ýýR¶¾ÒÚ‚#àSë¸|êO­w»(0ú<Û¾ZF¿Œ£Ÿ¦1¢lf–ˆÒeïÛZŽ÷-£¸ýÀQµŽë“Ã6:”ú•ys/cD”ƈ(³o´ô÷­£¸ýÀQý§Wýè|2 “ž@ãÚ›¹¶ŸŽŸàdv»NsrG rÊpÔPÜkýš£–ºKfW¤]†£6O.—ÜfÊ%ôG ò\ÝQk,GçöT‘ÑÝè5{e8jÖŒ2Gç|\èËËr±!’oÙìr)øãÎËŒ2â #«°Äì² dà (Ælà K ³ëÆS.¡Z‹&OÙÆÀ–³8,@ô ®ÕÒÂG€§¶q}²9½Œök™—1ۥ甼—Ù7[ò}ë(n?pš˜ÓÀdº9}sÓvCæt§QK:º’'W·ÓŽOnâïÇ€TTÿt:1ÏH“WS~ÓЉ¿k:±Q;{zSlÿxÓ`›¦Üýüó›:ÜžßZ~‰!Є¤ÔâúØ“‹†|ãƒóÔ»íåkõâ*ÒÄ/X«–h•R°Jg|ß¡ÎfÍÕàîõ6d\ž\±E·‡ý)—ì\†gî?Ê5˜þàS5Ôvd_”`EÖ!ÃBÅ%Ê?ÅqÍË1xÔ†ü)¿µðÉlJ¨ŠGnf¹t÷ÀÖ½°>íwÓo¯×òãØ8,ÏgO¡Ö¢×²ßlûŸ?yíu;>û’ Á¦²_·Õv;l½°Õì2 ¾\Îf˜%™+ˆTú¼¨/›ëiQ_©aeÙ¢Ç:ž¢T†MyNÉײÍ?5Û-å}A²¶|dkË7³Gç\JilµGlÈ7¹Õ²ÿ¨–Žú.nµÁöÅݦû蓌°+Êf´M`Ø ÙLð„ïÕñËU9ðBüúUÆ÷=Æ×¯réãëW¹"ާ†Ô2©‰ÆŒ ÊvíšÁSv.ÍW@_”àM„!ÛtâÍ”9>åãB_¾±!ßèè¬SDS6Ë3ש•}÷›ŠyjkùÌǶÚ}”MÆ×'ÇÉug0§°X#nóäÚ`ËrrsÁʲEéã¼»ä'×e?¹C¶3èOá\z_~rý~rýÍlÁÑð)—eó¨ ù&·Z Å ´_MٸLJ†ßN¼³ÈH†å8#c2û7 )á[fôS67§ò´¢…×O­ßz߀4[ÙÛR¶Žø¿ŸÈ¾Æ7·o-â[öˆ·p O¹çáñ=bCæ÷]Se jâ˜)£â+Ù¹Ú_¾K‹ˆ dŒ#æÑÂIðÔ`š¾º·¥<ÛÖÁS|{ô¼Ç±ÉÚâÍÞ£ñ§\êq™æGkÈ7yÂbG™©„ÅZßbü­¦ÞkíÏñÜUÛÃìiÞ«ý'Îkë9L…æK›i,¹)3Ñh™q=MÍeè—¹º k™_´Oø&½]fÙeØÞnU^r9¸ŒE¦-o. ÷«’}V¤Q}%RÍý½–y ÞOc˜ÃûÇ…“aÂe—¼ÿÉÞeG‚2-noƒI ÇÙ0†È´~Âɳ‹2¸ŒwUoƒ1D[+xÖ¯ñVy±r~èWD÷Q2Q‹<¡}ó>y771W¶.ã]<´4$MfŸ=e“»·Ïhß«ËhßÝB–-Nîïp<“?[Á>{ÿÖ…™'þìdíSäø;æ$eÎ󄘬™¦”1›—gÈ|ׄñXo.£ýäíñ«’¿ëŠŒ8nö½1™[a@QFùDð~›Í>¶!çµMšÓNŽk?öçö®îý'ŽÙûGf0ùºL‡¥âý }ž’³coø7Ö,¾¯zË»9äÜzî=? e!ÑßÛ‘IŒ>?=ÕÝ:öÝZÏëèòæ¼í+¬Kއïß{ÜçC®lŸ|²}ôý‰öÅ÷÷‰Ÿ©–¸·½ÏÈ=ïíaŠÆìm¸³ŸÙyvêv¦:Æýs”¾ÄÀy¨Op=P1'aòóˆu¾‡+Æ|ÌI©àã©OÈ~®±F!y{œ…†®hnSFû°ê–EÿT¤‡^‚þ1}Õ7Ù×ÅÚBº«Cº«ì>/Kûw±S&ì8jANó8uZvâÛXêG:– Û¡+]ö1á×*Øû} u¥¯ƒ°aè Ì£ŸIüB ãû€ºÒ×åIÚRžV½I2ôf›¡C—=‡öc_Rçú¼P¿„¡/¸6~®:uÜÐÔ#¾Þ½ÍëþèS_u%õÅÐqÃÌ®¯'$Ç™™á¾NÊd’Ÿÿ™:%ºŒ}<Î$¦²&ß3î×G9ðló ˜¦È‹nÊØn&3¤yÎy&3¶¹éÚ¿¬2Häõò™Ä³ß&°¬dNˆ¥ÙÛàL&ŽÙnǹQ®uûœ:ÔÛ£ökè©\J[û/8Ãã½5ôu<¼ÏÆ8y–Æø+âOƒ‹çvð6ĨÆ

    S¯EgŸ¹.'õctÿ`¢.£®Ìu¿kâ™ÏÞžwgd›™÷%uzžyO³t-ÏÐË~þMæ~c?\_×yf¼Í]kxº58ã̘÷UFêÍö¼·çyÉÞ¾ÓžÈ;ÙßÕ«GëV9ø³u¥÷ɰ›gž»ìóOýË>§ÊsçëÅ»¿úºDÚ ¾Ö\#žq†ãÇfû!,w›íì=Ÿ«îwað}…}èóÜpÏ%ÚI¶ywvߟØW#ÔѸW“ïçݦ ѳïÊÓî\¤í¼L»säãôó5ÍÛ2ÏærfÓúl ií3Ò¾ñwÅ’6=7ý¦¼Ž9ð]uè–¾2â—@ öÄýãsb'm¸OÒ̳éwÒ};ì†ÉçÜõ!õ§ßïãa—İÞåf›¯÷qÇ»b6Ïšëg¦ìFž›gÍ‹I:CM“Û¼GûfG†aPÿÛ”6ú°æ¼ÞÁmâÞö{kÒ°Y7ÛÝï è÷ ï¦0î)ÞÇ~u4ÝækŒML£}ÜîD—×»o¤¢ÆýØv÷¦ßYëÝŠû8ã²çé¿cò´ÞìvÚÚâq¼Ð^÷›úÎ"»ó’ǃ5È~·Q—åèkŒy´Ýàr_mXžÕaçμ‡üÓfóœ{™ºÏu÷¸ÛÜr`È/Q¯™ÜW»Ûï<¿ûa2,º;ÐNœýÎã¾ñ3xæý^ ~ïr&ïo×¹¶a{½,þ„YßyÙ‹f}§Åÿð{4޳Á}é::º­Êþc ›Ì=]Çy ‹=›#ï~?W‘þ‡ëw¼~øU™eÔnŸæèö„·Ùü³Œ)~[N´[“N{6ùçy^ue"£ë¸Ä;ƒ¶—­8vÏOBMÈÐÅi¢äÏnö“É}±«r»û©9Óåz™<¯wÏýó˜Ö{Èe¿‡°½–»'S×/r[ï¡Ì³êºøªÖ{fȰ—ǘKÙXü>H<·¾v‰gµu—Ãz'Þ¯®7=ÿå6MÊ«]os¾ÚûÙË Æœ‡Õ?ÈÌ—¹]hëXÂÖ·-zÍÖ½/þ~Q˶îô¥ÊØKóâÇdt½Ümc¿yÿa“an ?Éöm\÷[è«_eS»íáPw{»¤mÿo~[†¹=ü¹èû]hãº-K{tø…3íN×}´†9ã]‹-Ë;Ïc´ †êºÂ}5ÚÃOé[·}ìþåL_|Œ!®6ëÐic gv_b†Ÿ”£÷‰õZôöóªãêÑx›Ò·gË´õ¹|Þ—ö¦‹s·Ãìî¶êâDeèb»Ûù7oEÅmÕ+2ï"·­2ï(×…¾^öB\÷t¡ÿèo+#âzÚYn”BŸÔŸ­iµ­J£ÝÊ÷–¾ÙYeâÝë¶9ãB~Îklëž®Œ¹ÝWËê_›É’Ö½K›hìÅêþû°ëyÿ»]òªsûwÛ­qßû¾l¾GýëueúŽËçÜ÷£=Ïá°é×{ÿ´Æ{=FáãaÝö8?½NëøYä¾]·û9w;”çÁçÇ}>×n“»Õctì‡ÞòbÛ¶¾øšf۶ŵí@ûÅÛS¯9ï%ø}ýèö‹û‹ Θ¡ë Úep7yø(}ó(ÿ ÕÍ?`ûEŽ[V ¤Ý»9m~ uzãŒëý1y=ûR»ûfšÂzMôcú˜Ÿ¼Þ[üRp¿mb¼Ñ«à'×ÑÞ?uVô6ÜWiøi]£>1¶ãkÊýã>„Û˜îçuö_|oä´êß·»¼»¯ß·ýæë2öaØíϸÛÃq··½MìÛ³a£ÚÙô¸Çš| µµõž¨¼ã}Ì•ýûSóÆX9æq~y\Ï”iµ©Ý/÷\aÿ>·¥¶uÎ ì}·µM·Ì‹­m2×ÎuKb|ÅuŽW—x{Ú>Ÿ%¬1›¡ßÜŸv½ç¾læ>ñ½ši‚zrØÓæ×fž—¥ÍбÔËøRºý[vœëÙt~YNÆ»‹Å<,éœWí=q|ìë푞šu+´eþPüZžÓ?2F±Cx¼ #Å8sÌ6å¯àçˆÛëù,v~ž¹²§ÉÜi¦¬`%gÎřϺgK×fÐeoÊ¥ŒìŒ‘2<(Çüæ‹ÈÕŒŒ.êüæK‰r¼•SÁç~ãKQÅW6Ár9¡„`Nq>ÁR,~ã$\¼ÅosP™â»Ù)® Í(þåȬáso2óë/îùeøNNXÙ22üݪ#Ò[ÕQ·ú’êKüÕ¶#"]0ô$Ü’ex¥,1‰swUTí²?[ð{\=úZ0æä^ <øMžvm »µáφô19cœ-™có1û‚üY¦*GyLÄ8=Š K Œ¨xFÖiD3sq¾¦¦æ ù„œ|Þ*stcþÑ>ŒuA?îU#ÂWüÔ'XíexØ0¬Êð°aa—áa#¸Q†‡"´â7T‚“·î%Ö`ú8”[÷ûôHjdu¦ï˜ñ¬¥î05¹Ï+ö|÷ý?ã,t?<#.WžÉÏÎcõbß™û'"cbg°ùyij¼¹"J5y nç7ú¹ÆùâšFD€òÌ35ô÷ðÐþû~+û_>gû¶û|ÞÚsϼ;V>/rŒüµÍcG#à»òmxã:ßYä2<þòæá© 2Уd.—å\Oš5…# Š­Wܢϖ•á%ð*ãÆÂïê.‹·Êsë¥a§¯Œ}è•ôuFNËɵÔ=òð‡ÆàãíÝt䯷ˆ µ@ ܾsxáÖáù[/ÿÝGù“Ãó¯c;<ÿmþùÂóÏÿÑëñÞ<<ÿ­ç¿ùÍoÙ_ÿøþàù}¼°Ÿ|óß¾ÿ—w>ø küõ¯nýÑñô~ÿ‰/û©gö˜ýñ_ž½ûÞ/þö?{ö‰§¿ñW?øù{ÿÃÅŸ=û7åîË_þÑÝ[ÇoܽõÚ{¿ø›ÇÿÇ[=~÷¯ï¾üìzŸÿð«/—ÿcÒã_°?.~ë+wÿ×­g­Õ³ÿí±ß8^¼u|å:[T!˜J8vD•pÌRÂAK §H©3”pàý(á)U€è‘Š­”pŠ”*€S$„ƒˆ¯N–Rˆß á ¼Z 'K©‚Þ¤T‚^B8S’RS“RH’ á ¨„£6œ¥Â†-H… [ ¶ 6lQ*lØ¢TذE©°aKRa֤†øï›(ád™°aöoy«¬ŽãÈØlÄÑIõ:ŽŒÍæ826qtR½Ž#c³9ŽŒÍFT¯ãÈØlŽ#c³G'Õë82©^Ç‘IõG'Õë82©^Ç‘IõG'Õë82©^Ç‘Iõ:ŽLª—8:©^Ç‘Iõ:ŽLª—8:©^ÇÑ ê¤z#”êu©°¡Pª—8:©^Ç‘  ¥z‰£“êu©°¡Pª—8B©^ÿÝ2«Ó‚Tª8B©^âèØlÄѱÙZJõGÇf#ŽŽÍ¡T/qtl6âèØlÀJõG'ÕKT/p„R½ÄÑIõG'Õ ¡T/qtR½ÄÑIõG'Õ ¡T/qtR½ÄÑIõG(ÕK­°¡Pª×p”R½Ä‘ *¥z#”ê%ŽTØP)Õ ¡T/q¤Â†J©^à¥zùß@Ñ±Ùæ$•ê“Tª—8:6qtl¶9I¥z‰£c³GÇfŽPª—8:6qtl6à¥z‰£“ê%ŽNª8B©^âè¤z‰£“êŽPª—8:©^âè¤z‰£“êŽPª—8:©^âè¤z#”ê%ŽVØP(Õk8J©^âH… •R½ÀJõG*l¨”êŽPª—8RaC¥T/ptR½Å¬O¡T¯ãÈØlÄÑIõ:ŽŒÍæ826qtR½Ž#c³9ŽŒÍFT¯ãÈØlŽ#c³G'Õë82©^Ç‘IõG'Õë82©^Ç‘IõG'Õë82©^Ç‘Iõ:ŽLª—8:©^Ç‘Iõ:ŽLª—8:©^ÇÑ ê¤z#”êu©°¡Pª—8:©^Ç‘  ¥z‰£“êu©°¡Pª—8B©^3×”R½Äѱـ#”ê%ŽŽÍF› 8B©^âèØlÄѱـ#”ê%ŽŽÍF› 8B©^âè¤z‰£“êŽPª—8:©^âè¤z#”ê%ŽNª—8:©^âè¤z#”ê%ŽNª—8:©^à¥z‰£6JõŽRª—8RaC¥T/p„R½Ä‘ *¥z#”ê%ŽTØP)Õ ¡T¯Ù7J©^âèØlÀJõGÇf#ŽŽÍ¡T/qtl6âèØlÀJõGÇf#ŽŽÍ¡T/qtR½ÄÑIõG(ÕKT/qtR½ÀJõG'ÕKT/qtR½ÀJõG'ÕKT/p„R½ÄÑ  ¥zD)ÕK©°¡Rª8B©^âH… •R½ÀJõG*l¨”êŽNª·šA ”êu›8:©^Ç‘±ÙGÆf#ŽNª×qdl6Ç‘±Ùˆ£“êu›Íqdl6âè¤zG&Õë82©^âè¤zG&Õë82©^âè¤zG&Õë82©^Ç‘IõG'Õë82©^Ç‘IõG'Õë8ZaCT/p„R½Ž#6JõG'Õë8RaC¡T/qtR½Ž#6JõG(Õk7¨Rª—8:6p„R½Äѱو£c³G(ÕK›8:6p„R½Äѱو£c³G(ÕKT/qtR½ÀJõG'ÕKT/p„R½ÄÑIõG'ÕKT/p„R½ÄÑIõG'Õ ¡T/q´Â†B©^ÃQJõG*l¨”êŽPª—8RaC¥T/p„R½Ä‘ *¥z#”êµ+G)ÕK› 8B©^âèØlÄѱـ#”ê%ŽŽÍF› 8B©^âèØlÄѱـ#”ê%ŽNª—8:©^à¥z‰£“ê%ŽNª8B©^âè¤z‰£“ê%ŽNª8B©^âè¤z‰£“êŽPª—8ZaC¡T¯á(¥z‰#6TJõG(ÕK©°¡Rª8B©^âH… •R½Ày¤S½¯Âÿ~ôÎáÃÃtÊø‡ìåÛwŽ/\žÿnìG;X—oã™Ó<Ï%Úø6=ž"’øídÞÒåÃëg_=¿(§Ø{êg_;¿HÖ[Ííìó›øøy:…Üs>{rkúÄù|êÑ6ÈÙç¶¿d-§Xb;{ãƒíù'Î/Â)Í9–´ïvßäé­«-ú©õPËÙ3çõdþ]Ëû×}þúwܧã/G] ñì¶Og×q­ík~óò¥C,§r¼üÎáò¹×Ï.&è©ýíøŸZ‡ùÌõ3ñâ¥m½xŒ¶‘ê<·0û,!OÇ\k<Þq¹Ì)ß?”Ú¯ÊlóþáÝëÏ?¸r(í±f?.óÍÊßÿïOüïÇw9L_xíÖ/þ«П=öÒß½\—ãúW¯¼÷ìÏoýác·ì¯·žýŸåï.G£où½¯Ü½õ·&ßµÿ]ùÁ?º{ë¯ÿ³~÷¥ß8>öqE3˜¦(ÇÔbÖc*U©w9¦ôtD/z:¢w=1=1e=15=1=1g=17=1Ïr:¢„$§#Jhr:¢„YNG”˜ätD‰UOGÄIOG¤¤§#RÕÓiÒÓ9ê鈬³,Y/fYŠ^̲½˜e)z1ËRõb–¥êÅ,KÕ‹Y–¦³,M/fYš^̲t½˜eéz1ËÒõb–¥ Æ,'Á˜å$³œÄb–mÒ«Í!“˜Ÿ &µÚ2‰ù¹dósÁ¤V›C&1?—Lb~.˜ÔjsÈ$æç’IÌÏ“Zm™Äü\2‰ù¹dósI®6‡Lb~.™ôü\¹Ú2‰ÕæI¬6Ljµ9d«Í!“Xm˜ÔjsȤ³”«Í“Zm™ôb–rµ9`R«Í!“^ÌR®6Ljµ9dÒ‹YÊÕæ€I­6‡Lz1K¹Ú2 Æ,ÕjsÈ$³«Í‰!ÈÕæ8“–ŸK&±ÚgÒòsIËÏ%“XmŽ3iù¹Î¤åç’I¬6Ç™´ü\gÒòsÉ$V›ãLZ~®3iù¹Î¤åç‚I­6Ç™´ü\gÒósÕjsœI«6Ç™´jsÈ$V›ãLZµ9ΤU›C&±ÚgÒ‹YªÕæI¬6Ç™ôb–jµ9d«Íq&½˜¥Zm™ÄjsœI/f©V›C&±ÚgÒ‹YªÕæ8“`ÌR¬6Ç™c–jµ9ÔjsÈ$æçBR«Í!“˜ŸK&1?’Zm™Äü\2‰ù¹ÔjsÈ$æç’IÌÏ…¤V›C&1?—Lb~.™Äü\ûrµ9dósɤççÊÕæI¬6‡Lbµ9`R«Í!“Xm™ÄjsÀ¤V›C&½˜¥\m˜ÔjsȤ³”«Í“Zm™ôb–rµ9`R«Í!“^ÌR®6Ljµ9dÒ‹YÊÕæI0f©V›C&Á˜¥ZmN+zµ9dósÁ¤V›C&1?—Lb~.˜ÔjsÈ$æç’IÌÏ“Zm™Äü\2‰ù¹`R«Í!“˜ŸK&1?—Lb~®1ÉÕæIÌÏ%“žŸ+W›C&±Ú2‰Õæ€I­6‡Lbµ9d«Í“Zm™ôb–rµ9`R«Í!“^ÌR®6Ljµ9dÒ‹YÊÕæ€I­6‡Lz1K¹Ú0©ÕæI/f)W›C&Á˜¥Zm™c–jµ9sÓ«Í!“˜Ÿ &µÚ2‰ù¹dósÁ¤V›C&1?—Lb~.˜ÔjsÈ$æç’IÌÏ“Zm™Äü\2‰ù¹dósI®6‡Lb~.™ôü\¹Ú2‰ÕæI¬6Ljµ9d«Í!“Xm˜ÔjsȤ³”«Í“Zm™ôb–rµ9`R«Í!“^ÌR®6Ljµ9dÒ‹YÊÕæ€I­6‡Lz1K¹Ú2 Æ,ÕjsÈ$³«ÍIi’«Íq&-?—Lbµ9Τåç:“–ŸK&±ÚgÒòsIËÏ%“XmŽ3iù¹Î¤åç’I¬6Ç™´ü\gÒòsIËÏ“ZmŽ3iù¹Î¤ççªÕæ8“VmŽ3iÕæI¬6Ç™´jsœI«6‡Lbµ9Τ³T«Í!“XmŽ3éÅ,ÕjsÈ$V›ãLz1KµÚ2‰Õæ8“^ÌR­6‡Lbµ9Τ³T«Íq&Á˜¥XmŽ3 Æ,ÕjsZЫÍ!“˜Ÿ &µÚ2‰ù¹dósÁ¤V›C&1?—Lb~.˜ÔjsÈ$æç’IÌÏ“Zm™Äü\2‰ù¹dósI®6‡Lb~.™ôü\¹Ú2‰ÕæI¬6§½Ú2‰ÕæI¬6Ljµ9dÒ‹YÊÕæ€I­6‡Lz1K¹Ú0©ÕæI/f)W›&µÚ2éÅ,åjsÀ¤V›C&½˜¥\m™c–jµ9dŒYªÕæÌI¯6‡Lb~.˜ÔjsÈ$æç’IÌÏ“Zm™Äü\2‰ù¹`R«Í!“˜ŸK&1?Ljµ9dósÉ$æç’IÌÏ5&¹Ú2‰ù¹dÒósåjsÈ$V›C&±Ú0©ÕæI¬6‡Lbµ9`R«Í!“^ÌR®6Ljµ9dÒ‹YÊÕæ€I­6‡Lz1K¹Ú0©ÕæI/f)W›&µÚ2éÅ,åjsÈ$³T«Í!“`ÌR¬6'§"W›ãLZ~.™ÄjsœIËÏu&-?—Lbµ9Τåç:“–ŸK&±ÚgÒòsIËÏ%“XmŽ3iù¹Î¤åç:“–Ÿ &µÚgÒòsIÏÏU«Íq&­ÚgÒªÍ!“XmŽ3iÕæ8“Vm™ÄjsœI/f©V›C&±ÚgÒ‹YªÕæI¬6Ç™ôb–jµ9d«Íq&½˜¥Zm™ÄjsœI/f©V›ãL‚1K±ÚgŒYªÕæÔ¦W›C&1?Ljµ9dósÉ$æç‚I­6‡Lb~.™Äü\0©ÕæIÌÏ%“˜Ÿ &µÚ2‰ù¹dósÉ$æç“\m™Äü\2éù¹rµ9d«Í!“Xm˜ÔjsÈ$V›C&±Ú0©ÕæI/f)W›&µÚ2éÅ,åjsÀ¤V›C&½˜¥\m˜ÔjsȤ³”«Í“Zm™ôb–rµ9dŒYªÕæI0f©V›3Mzµ9dósÁ¤V›C&1?—Lb~.˜ÔjsÈ$æç’IÌÏ“Zm™Äü\2‰ù¹`R«Í!“˜ŸK&1?—Lb~®1ÉÕæIÌÏ%“žŸ+W›C&±Ú2‰Õæ€I­6‡Lbµ9d«Í“Zm™ôb–rµ9`R«Í!“^ÌR®6Ljµ9dÒ‹YÊÕæ€I­6‡Lz1K¹Ú0©ÕæI/f)W›C&Á˜¥Zm™c–bµ9%¹ÚgÒòsÉ$V›ãLZ~®3iù¹d«Íq&-?×™´ü\2‰Õæ8“–ŸëLZ~.™ÄjsœIËÏu&-?×™´ü\0©Õæ8“–ŸëLz~®ZmŽ3iÕæ8“Vm™ÄjsœI«6Ç™´jsÈ$V›ãLz1KµÚ2‰Õæ8“^ÌR­6‡Lbµ9Τ³T«Í!“XmŽ3éÅ,ÕjsÈ$V›ãLz1KµÚgŒYŠÕæ8“`ÌR­6§&½Ú2‰ù¹`R«Í!“˜ŸK&1?Ljµ9dósÉ$æç‚I­6‡Lb~.™Äü\0©ÕæIÌÏ%“˜ŸK&1?טäjsÈ$æç’IÏÏ•«Í!“Xm™ÄjsÀ¤V›C&±Ú2‰Õæ€I­6‡Lz1K¹Ú0©ÕæI/f)W›&µÚ2éÅ,åjsÀ¤V›C&½˜¥\m˜ÔjsȤ³”«Í!“`ÌR­6‡L‚1KµÚœ©èÕæIÌÏ“Zm™Äü\2‰ù¹`R«Í!“˜ŸK&1?Ljµ9dósÉ$æç‚I­6‡Lb~.™Äü\2‰ù¹Æ$W›C&1?—Lz~®\m™ÄjsÈ$V›&µÚ2‰ÕæI¬6Ljµ9dÒ‹YÊÕæ€I­6‡Lz1K¹Ú0©ÕæI/f)W›&µÚ2éÅ,åjsÀ¤V›C&½˜¥\m™c–jµ9dŒYŠÕæÔØäjsœIËÏ%“XmŽ3iù¹Î¤åç’I¬6Ç™´ü\gÒòsÉ$V›ãLZ~®3iù¹d«Íq&-?×™´ü\gÒòsÁ¤V›ãLZ~®3éù¹jµ9ΤU›ãLZµ9d«Íq&­ÚgÒªÍ!“XmŽ3éÅ,ÕjsÈ$V›ãLz1KµÚ2‰Õæ8“^ÌR­6‡Lbµ9Τ³T«Í!“XmŽ3éÅ,ÕjsœI0f)V›ãL‚1KµÚœ2éÕæIÌÏ“Zm™Äü\2‰ù¹`R«Í!“˜ŸK&1?Ljµ9dósÉ$æç‚I­6‡Lb~.™Äü\2‰ù¹Æ$W›C&1?—Lz~®\m™ÄjsÈ$V›&µÚ2‰ÕæI¬6Ljµ9dÒ‹YÊÕæ€I­6‡Lz1K¹Ú0©ÕæI/f)W›&µÚ2éÅ,åjsÀ¤V›C&½˜¥\m™c–jµ9dŒY>úµ9¯Âÿ~ôÎáÃÃtÊø‡ìåÛwŽ/\žÿ®yõ1/߯3§yžÍ2¼°ÿ³Ù˜çÓŒÿFr>Ùì\Þ9¼~öÕó‹rн§~öµó‹d½¥9}~¿tžNS,±}nkúÆ×5xܤM!ï›þ“óùÔcÈiÿáçá”æ ÞÕO­‡ZÎ~óü"Z¯æî:}ú¼ž¦4µ|öÛçýdkÙû2¾š†²<ÿÌö‚Ý»vmŸâXR·§>Z»}j7–gÖO?w^k;Åšß¼|éâ)÷ÔlË©/ß²yÛXǨó4í§m÷éFðÅUzr•~oé—·‘î}¿X¨¿¸}º5}r?ü‹kÆñÿÓºï_»{îéëfÎvÆúéïœGD.õêvðYüÎáò¹×ÏNšÇmržÜÏÃ5“³ÃnšÇØkê¾Û}“§¯…¾2AÛ¶¾þ\ìægÇ¿—ûŒ/M3½~ŒÓ©×cªÇ7M ½uˆužŽµ—p¼cG‡ÆtŽï¾wÈ5üm¶Ÿ”VÍ?yåð÷퀀endstream endobj 116 0 obj 12078 endobj 120 0 obj <> stream xœí[ë5W›{äöN×{¥¥-WâÚä¤ìù½k‰—Rh#ú‚B‹*®RËþ}ì}yœ'›»&½žJ%øÅµÇ3ãyÚËkBSÆ ujðì,9}”‘ÿ&¯Î QT’³iÿId6…Køwò䄼J JÆÅ,­7/RQJ4µÿzóWòü$ù1)v%¾«€û:ÉSáþ) ~vF¾™X¾ ‘yj4™Åê‡UÅN3ý雀 ¾äl·YzŒîâÛ#§V+£¶B~ŸLN.³˜»Úo–B^æŠüHãÆ.èv²Üšq#ç jcçdZ©á? 8©7„+¢¼BÑjÚÞà›Íì5 À­ˆ×땇 DÖ}„áœwàê6˜}§À9e6ãÝ­B“® ŠZ/W*wãc@î€Oþ`Òl‰2ž‡ÂysÛ3îµÌ»Ë±÷ÛG0ådÐ,ýìr7·Kx,¸…GSÓ€s;²r½™½ðáñ½Vžù-§4Ë«¬ wK|"’C¸’®84ô¼Ù“m3"çꈱ€³ìß8å+0¼ ðNdéˆlØöPÌkËMØ\+—°ke"*Ûõ¶ýð*Œ5š “ij›ß+‡*F· D>Û2özÑrhêÀš9;#eŒµg³T]3û.K`Ä¿sIÜ/שáçv?«ÛV*6b®€ï‡Ä1ìæ®XžÛ@kƒ6 kÌyWþj2×<Ö§w>ŽT÷Wê¦%Œ±TŸ«ia@n?âPaÞÆ«ñ`Ê©ê`[] »§/´n•³™®œ¥ýà/þ ௛•°p‹qc«8f¬­€¶¯Ó‹0•ª±¼ã5ÔúÞœÒn*°ÄË´v¦>Šî‚A4®}"ôÞÓ`Ì}¦ O06«˜U†0 gôÞ ý)žF4Ý‹ÐèG´:´§g”&GÞj ”yÛß8À=€4 oä"Þ™ìøÀƒfåfƒvÁß÷ƒ¹8>r{³¡x¡9Ù/{ÔL¦L¸{âÂß6±…"ê¿÷ÿå¹L0Þ‡Â]笶 ªh)HöÃBëôØ­C ÎaÉ)H|çÞ"¼»¢Aè¢h¾´ECÅߊޫB¡è×ã+Ñõ9k‹çÕ Ç…48çU¶ÇèaA{¯E¥Œ§h|„ÑÖãU€H3«¡"Šè2ŒÜoÂÈ2Í*ãî ®†YU²,ͬàÜØ%Óbf…—¢Sùë´±+X¡^ÌÆVib¹pwUWÃÄ*YVnbòÜ‘KµRt»žšoaÝúý¡A˜+Ö¿œ¢—ÇÁ–ÝšÌ.àhØ´xÌkíÖµxŒtõ3»aßR£‹u-¡îr‚t0êZô;k[¤ÈRµØ=jØq`·›± #?¹»ì¸ÏŒ¾æ ý5juò%w5Ræ©×oü© ×G¿‹ÑÐÑWØ•¼•=ü±l‡±²B¢’%gîµ£õÌ1yËjñ íy¼òU‡G ä³”.Íqhkóî ú€ñ;Íì»Ó‰´xíuF˜DaðÀž §’(äS Ã’«å[;ž˜Næ=4/\\ BX|[Gal O«:,Ñ¡@Xt>ïíj®}|¨Cõ\úó&jêO¿§õç¬ïh…à†¥˜ûÐ!Ò®®le&•6…—Ÿ]ùʵôzÄ B•WUd9¼ðF@;¡­èl¸)ºÑno Kû ‚†%Ø‹(ã´€%”T‚ºÂ Üý8°xï£\-«„­xÔ­Ö*÷zÄ㊖æRËúí?Ôë:<L óo¯DùŠéæ»}¨ä0”!f#겯¸=¯QxÓ¤¥=X*Û/åXÀòŽP‘U‚×u‚¿`$Hwˆœm‡ZXI‘€Š¼“·(¨©õbæsŒr¹›jº^Š&&¿ o(>ü¬Gô¹ÑÿVd|€2µ9Š~ÒéÉ»LíödîÄì÷bOæ5Å0õÇLIòûÑÃŽ$ÿzÇKø`.s‘æ‹çðA®¬å;ŒdÐ>:»™Oçh ߎx¼¾Xv^V4ÒýÏ/]Ô?/ö#:Y_r«$°Æ¬Â— ©ø²h$K‘2š_õ"&Dxðô¸¡…}Ý¿ÝHý1ùFÅ«endstream endobj 121 0 obj 1981 endobj 125 0 obj <> stream xœåTÛjA}ïÇýë±;0m]úú*¨à[’‘¨!AÙ•è ÿ¬îÝ ³Aw!8 3‡éºtŸSU+@O ØÖ\-Íó“ ‹Ÿfe˜+D °ì(5øÝ„|¯áWsv?LTk¤±nw!BD„„úº½6ó#slzV8y½j»2ÅK{ú)¾Z‹QÏU!_ŒóžHóÊ0 œS»iŸqiÎ-¼rúÂÈ)ZøäЇÀ! m# s$ ßÜ@kå¼k4S\cŒ¹XHÍAS$ŠÝA|Œ”ª…ë¶!©& sõ)¥È6’`Rü¹e ’‹:œ»}*±²…S‰|¦z1¾1/Ge… -ee þ+ÁcQV¨}ž*+«®^+ïw¬‰­ÑZI ÏÜxó þ”Œ{a u‘v“íëf‰|ÚÈýN)/A”òE£<ˆ¨Ž—NÏÃ] KÇY‚¿Råx¤=±$»ÅAô½óôÖdÛ~Ÿ+­QtY8kd3FVŠoZ+R*ë½3™)äXsî]¼•fêxÚš[ H´Y¿L6Þº!hZnÍýA£§PÊNÈiœ 'Éjý¾íì³@§P®ÜæÅÄfê:s"ìQhßU«ÃPÇÒÿDäž*2VÜTäz-–ºQmšìØü&Ã0endstream endobj 126 0 obj 500 endobj 130 0 obj <> stream xœíXKOGVrœ£ÉÁ䲃´“î®ê×-Š”DŠr±YÀ€lŒÍà çÿK©êyU;6`YJŒd5CuuÕW¯¯ûZéÆX¥ù§_¼½ª~~Ôù?ÕuemRN£ºÊ+ψɺ]¾«ö¶ÔßUÖ”Rr†t}<T ÓZyMÿ}<­Î¶ªgU>U=ÿ£[ìuàùƒ\¿½R¿®È®¤06É«ÕY>ˆÎAÔÒ¨¥VFÛÆ¢«›Õêª:\¨X/uã-„…Ú«im"&³PÛõƒÇ…:«—¶1Æ&X¨sÞQ›äêDlØ«—¦‰d]¨ÓÚ™Ô8°/WV¿­È£ø‡|ià‹n¬›ˆìÊ}ÂBvCéôŽxÒîUd:;²ø¦^]Œ˜‘)$L=tÃï_|lä|žP,O½_Ö}Ú;£±sâô‚6ÑǺ„@>Q<^ñ/} ‘±NéóeP|ÂÄŒ„ÑÐBqO 7›‹}V¨§˜ŸÅÂ….-ıq0ÚF÷yqÆUç¬ãjÌH¥·P¼à,”2Ox) ç*ÍQBˆåÞíšDL²‰ |ü¼CŽH? u@Ú&ëJ…RÉëÜ?¼´Þ烴5):’:Ì-ÇúÄö¼&¥ÚG ÖÎûqô'÷˜)ɘÿ˜w'Ô iúIü\‡Ÿ¥ö?c»´ÿ–³žo7XÛ¸<ýW' î¹°&­c„fŠŒìø•\tä"B;’À‚#Ä@•–¡‚Æxj+ µËòµ3›—}¨œ+>¯„Jñ¹Ð~«cűm÷‹:E–áA˜}ú=‹׉S¨ 8yÉÕ'D2í²õ¦2B«—¾Aå­ ÁÒ‰û““ò¨²hsÍQÄ0¦PŒÓïHÜzJ6p˜§Mî ¤æ{¡T-mØËŽE¡y?IviPñ†À!oc§3÷_“œ€‰ÝþEûÏÑë·Ð½_퇾aË(꺇Ÿòº°µËäžA¾ØÉ ÈGŸé0ÇUËNS\)ŸK3ûZ¹[å ¢á7ãËu§°hè´lóWˆÌœÄṋjº|zøÍä|ä8Ý>ç .Xð‘ or#Ö†ê¼ýº!ü7óú=à !¸2ú² v×Õ¹œ’Óaù´Z²âù¢Nö ¶ÜÓ¬ˆ\Ų>ög}î@Ò!š’ JÃû^'ÇÓ0÷•2¢>ÚÁ²‹H–‡¶¬óöÊ æ’_l¼Î%#Dyì8ºœðßnkBú@|þŒ3ÝÖH‰ŽI7™`Ý-zȱP $Žøvœ3fÖj Íž ëØ;z ï½£7ùÒ°aúÞyWÐwñQ°Ñò©aÍ;7¾¶K@~¼§ß‰cî­½CÉPÍ\Ãæ3a@D8šÌ•Ûešõ„l™>˜òUZ!S¼× ú¬ú;Äwendstream endobj 131 0 obj 1584 endobj 135 0 obj <> stream xœíXKkG&9ÎÑ? ôqW°®®~’›!B.vÖ^K‘eK+G²rþ?¤ªgf»z´ã¬ÃÆ DOOw½¾z|£e4Xeøw\¼¿ï¾ÕÍÝCgmVÞ8u_V—;'ë~ù¡Ûœ¨ß»")çìd}ºÙ‰pÞ ýùtÝmOºç]Ѫ^<tö¡Kù§lÈõû{õtMvAT˜uj½-šH‘³A­@­èBÒIŨ#½¾ïß,×·ÝkÒŠŒÑ˜cv™6ÏGеu*m'zÿއù¯ãÏêr¹ )d\¨wË•Õ&EÌ µUç˕щ.[z¼Xm 'ü‚ß ½I9,ÔGzÀC÷7½¬Ä÷_ ¹Oxí2úÀrI£#€%É7ïHíæ¸PWB4ð5›dSôÁm,Ö×ý‹lª&÷¶²èøAÊzÛ¯½#Õ¿ôŽgK¢~e‡LtZón¤ÔFÅféÁë`ý›õOÇJ—Ï‚X² öéò?ˆÿˆ0¢W¹ܨ+ö¸Í:(;”ù·\æ õÚÇ"r}Ÿ²Ù›»G.¥”€Ây·\y X¢¶aIM ø°ÚÐY$4£g Ž€ÎÇ Ksr%\H1òÞïn¢£3/— M ‘ŽœõWs‚í&Šú–M 8¢›æ*­-Ä09S¢nåK’ÊC3›šN›ÀWµ%Y¼ðÕz¤}d!Þx†î)ŸqÖÖû'”GÀ’P2?0¦-8«1Ô+GXŽ÷x¼%#ÁFa䣰ÊÐ_°aS`ÌÖ3è°ÉôÕVý®‘»"H×ì'&ë\Ù¥wµ“!Á>œ–²ÂYH+[àu'Oµ¯¶%‡ÐDhábåÚ¯wMIø¡$¬ñ}pI޾‚A¹‰Ì6~À–ñÖåÞUJ1Šbê'-–$´Ú%n.—\&Álè葻ݚCb¶cg 6ø‡nâsêq¬‡„t©´¤uŒœƒe¥œ xC %Û÷ɹ–wÉäH&'¶SÚ|%äÌz:ëIl0dÞݨÄaÌ5tˆ£sÉH¬âã"yÔÁ(Û˜?|A›­N6çqšCµ2nK>QÇ›4qõ5»³÷¶Mñ¡m&ns½ªÇw|3µИ]+Jv Ùìª0ÙU£ÝèSH´OHdFUÒÛ Œá£ml,•‚ûcŸ!¨!$œ–ùèâüi3Qh,æ\ÒñÉ‘¦*ZÎ$×Ôúçn}rΓžÒ \N;' lc¸ò$DÚ¡‰Dï8¨íSÛÀ¦3ù茑«íßàüˆŽ¼þ¤ð±¡ý/ DÁ>bTLÈ 'ð±?ÒB#X×ZC0%w•Ù·-ë VVhÞ„¡JÞÖ¼ØNQ­@@œ ù¯À¾Ü$ýþ¯ó¨,)ãû¢!Ò𨛦]H†,[ý0†öŒÜo™Ð®ýµlUÒn9Wä¼iIÑ`m&%M§–ãàœõ¡I“u¶Ÿë=+h W…ÕÒ†ØrʾºcÑá¶sË?3°åp(ß©1æéUùy ƒ¿Ýâ¸üZ±9”ûS*欘wg‚ÉÔ.iìÀÎ!OHˆÅ§€¡Þ S?+:à[lŽÎï¥ÍÌ–_au-?¸¤žÛÂEéE›ÔM² >1xL]{/|È£=‰K‘ËRðœ}^Ðèji+®­XéÁoо֛9ŠÓôÃ1*yq@£ÙG<¥q­ý¬qE!ËþMY1Fù))¶ŠD¬ôUÊnœ¬jf!ô‘t«öšæVcÜà3¸AÄõ3Q”T^Pèým]ÜÜû I¥çºÓœì¹óUÑãQü¼ûml¤vendstream endobj 136 0 obj 1386 endobj 140 0 obj <> stream xœíYëo5 X¤"RJ¡a¾ÝUºÅ_âýR Ç…6*m!iÚB‘Z>ðï3ãõîŽ÷îÒ»–"!H¤ÈñÚóüÍøç݇J·”æß~ð˃æýïƒ:ý£yØ$å4ªyäyø[ƒa2î†w›ÃËê÷&KJ)9C²"Ði­¼¦?Nš;—›ïš¬U}ÿuÐÚ‡Ml-ÿä 9þåú|Iv%…±M^-ïdE¤MP £Z - çòŠÍÑL}0_èÖÇdfêÖ\·à=Z?SwiŒèq¦ç ÓFk­9ã;´†F×1™™ZÍÐjM>ÎÔÍ'ç\ˆY™m çz]A[ÇëY—ÕÞör’N3uî\l]ÂË+ÍWKŠˆQüKéCDÈ ˆmü+ò$!ïmۇÃ<„Ö#‡cöÜ|yŒ<ù–BÂÔ'`øÿ©ÕrÄUð-L´>YœíÑXÜ£lŸp¶C ?,g2éÀ%ÊöU'㻼hmNÞjnZ Üdë/Çb˜©Ûãšjo¿Iè N*AÆd,‘AQ#ááW–©Á¤èhÑ{ÙDÇàdŽ–ýÄ« zGJO;kÐÆ‚N‹Î¤ZéjMH^R»M«A“YK!D®îÝvÞ³&g\ëÁUút89#&FÛþOß³Kß¾ – ›N™1×g,™Öxe ”Ê~ž+›SÜmh]°|ª.97¸Múä´ïÚžëƒãjGù`Îý6åíýùÂSˆ=T³%"˜³ÉíܳxêlÅT‹¿5wɶ!G¡s‡´áF Ç •¡#EôªìQ¿ÇÄVí§>%üq|l¼KCët$[®ÑÐoøü K(–-ÚÈ›9OÞ28x:yvÜ¡À6[l,+'ì| ‘“G9n‘VûP‹ü™6øÀPú‰×{DÓÇ|Mâ=2Õ¢]VO)P©_ѹÄEÆn m#"{Û†s·¡p¬ádRòÑêѧ ®D ±ž—àøÁ1:“Çä}t.M r\£‰Ñ—bÄZÐ6ãˆ0¦4+.õËþ¬«¦Ì/eÂ`l´œO²ÒiÐÚÖnHó¥Ì£¬ -&†ãµ±‡¹^]àŽ"£žå!D=¶îØÐ’oÏ-QMrr—ÂòÔZ`¯Âê‘×^j¸ÇÝɰѦuÄ@vH{äRDgs^"¥+„\!”"K¼ ûºŒè¦ŠD ȺÅ#+¦R4¬X‰rõs¤!0}<²e‹zå£ÊŨmØäròõŠm—£@!»j4j¬]ArTÓŽ@ ,Ÿ&ÎsØ¡ûÝìõqxaNM4$ºó½:N¾1L^'/nš|n¾2<S>ï'/l’ôV7iÑÎ.¸&ªÂ—ùmJ"ú¯». »žï&³Ý$¥g/­‰¢òp뢺ý®·æ^ÓÅ'•+µOÊDÐ>œÑÒ½ÙdŠqžË覬–ß4ËË9°üâ@Ãì\‘YÞôá Ó‡çÄà ò!X‚¿KÊcwðÑÞqéù©œÏzxaÛÃ…It;Iaæ¦ÔÑ>ë:GÁM‘U…‚ÐÕÃ*æSÀ%*Æ¡Þ8ûaQÎŽ¶ã‹‰‹å³ÚNv•MûXöá8¼8»í“½¾ÆíëE·i/>ßÑ „¥}Íé6ícÎÒœÜ>RµÐ]<[pê5±ã†î hhîOÒz¥áo·¦k‘Ô­C§ ò§UÛ5Èn:BÚ0Dÿ7M[jºÓi«Ð{MÓvº:ÙbéÉÃWeº Ò-ŠtfÐeÏljºä(â]®Lã0Í}Ãt5]ÑÁÔî> stream xœíY]ÅUc’ÙÈÙ(20y¸ƒt‡î®ê¯—(Š„,ñ^lãÚµMŒÉÎ?§jzzz®w×kûX)^É:·oWwuÕ©¯Ý§½­ëüÌàè¤ûäËØ?úo÷´s.÷Þp¢(|ÒqÜÁü¡»ûqÿS§'圽ÅYÏÕ#ØÓƒÿž=è~Ü}Ñé­ý—Ÿ€½O»4’üÓ…ôÿ<„^6öúÇz naKýÖö[ÓÛGúÇÀýáI·ùÝpø¸ûôÙºŒ”cæ,÷­>¿öµI Ãèvn}•çå¼Î.Ï»·é¿̘?›þëÁŽÆeç7ýíakÇlæ?DìmnW¿x´‰bÜôeÑ;Ÿ±|·ÙÜ ÞÌ92öücÆhóýÃ[û²ì9/VÃZC“e—»~½¿ ¯’ñPJéÓ´G־ȶ6™´{ïy¡Kc*^ü×°5pÍÉŸé²oÕOÉY¿öǽÁÑ$‡Íß &$B¦Ùôw6´n}[Eb¶kÝš3ß¶x#žÌ›þûæ˜ïÔC9%¬ÿ[Ö!êqíÃÖãò®jª¡®E/¢Ù_±î‚¥„s>Ä‘.óúÝ·Ù™SÎÆ¬ë7Äb6øìæ—®&‹Éä*ƒ=M1ÇbƒV±v}}™¨]/«š6 yKÎe¢Õòas`³ü@t‰‘)æõ¥ƒ»aèvfÜ–Xr h÷"N>š'+Þ{Ð~hý±ú¢1ý]¡Gˆyßb9z’:rØ,·[Z«žåúç-™œ[Ê”÷k"Þ¶n ™y窖 «Àx"²I‘üúâãÁ[!©­Âvv[ä²ùoÆ\Jco¼›·êÅþ#†öd²ÕÝfü&oÏi^\tO¢5Ãa¿ÅšV Ú6a¿öK÷WÑÈ!ÒËT´­4’ɱXÁŽ(\q'@ψÿ.ëwsÄrÎ… ×R5^¯p½zMÝcåbÑ«µv››ZÑtW~÷ÓdÁdD‰ç=¡õj·ÊsÍn};« ΂OΫJÌo¬*-†j¾:j.¼™wsÿB®—.:Ë1û(:h’Ð1CÝÞÂ%=Y›Ë¯¦PîîRÿ9ÂýVâ‡>ôÏ$„3ºýЉŸ5íµI#Î]¾A¶@ò^½«)ñïú`GNÈv[>$òNúÅF&gKjKDÒ–I³lâ\6ã;ÀÌNÛµ’:ƒMöÍš;[a‰m ¥ç×â(c4¥ªC ‚7A< PJ‰-ÈÍÂNãå¡[Kž ‹Ð€yé¹§Û²Ñvlׯ’Ï·.´†qiü¿mñRcbª–e—Q§=ö“‚_29Ð}â<¡£:ü8†²ÃW)?Ÿ%{™i¯bt*H¥8O{iÆØ+7O;\•róYGÄèÃéî!ñÿ0ÔJ;}Ï=FK=²ÅÚÏxé­Ž¨É+S댄˜‰Åg,¹¤´½fÆRf’›0æ¢"5¡Òì8ÍS;=á,E Rx¤Œæ’‰-> stream xœí[e×qßej$‘Ç@DQŠ!±!•n*s¸î—À1dʲՌ'ÉñbSrx1‡iyüd aLÂæÍ †?è $Ÿ 0“÷<úc¤ê_ëì½O³‡MZgØšLJÚÝk¯½~kÕªª]µÎéw&·õarúïN¸qkóøÓuzí7ïlBèSviº©¨øÖ&Õ3²‰¯ož{lz{ƒžzïÙK_ï¾6w‘²sSqòÇ»¯nn>¶yjƒ§NOÿî¤í;›¶ú.¬å·¦'Ne\}JmÛËtz’ç$_§k~ºæ&ïÂ6¤©Iƒ0ÞÚ¼p4ýàXSbtéhzæøšÛ¶˜CŒGÓ«Çׂü%D4='Šoµ‹ü¯p½Ö,òCr½çÜj;šþ½Þ\l¥M¯Ø Iox?ˆÉ»¬=]óÒkìáhzY…RR,GÓ³"§RIÚ«´I½´Õ(Ê2 tº7ÔõÓ~ 7„Ö³tú<:®Äy¨Y‡:nN%ÊÓnê ÞÇŒF1†­‹þ¥Ó'7ß=•ð“þ++`ÂV@˜§V¶­ýÓ|œ¸K[ ¸¾e,Á`8=Æe²bv2þ7õ/2æÒ™ŸLú—kiò—_<¾·¥º xœ[ß¶ï²W·1|:ÿ˜Eá†z!ìr¥ŸjMQ„£_9>}s™-X¯=õݤÍ?Ä“ëÖµ©–m8óàœ¦]Èè]²NW§oˆŽ{Q½¤êŽe«²c}Çêø­óØ¿¯›%”ÐÚ¾¼nó#¹^}L¹êNö✪®ëªûWtQ{©bßÏò¤‰«¥&íD.çæâ°ѹ®Æ`¹úêqö}›ÏØÄ_l…>zÚ°@ÞÅm`›µOjD“}[ÏÒnŽZئÉû0”ø>UbVkÂ6ט¥£ÓWtþž“‘÷‹™m‘CÈÉ_qS˾h¨ 1Gsµ.ëB؛䗯ñé.ù½ÙY­±l‚¼-©j$ac :¬×Ö«ómÓB’U/«¥ÿÑrçËÇ‚¹­ù®×%¸þÓð:qÏëì̵¦.Yh'V,ê:¬láM ìJ,Ì™yÑŸ„ .ç|ŽExñëb³¿1ÇsáSr6ÍkßñËÄ~—Ö:z¯†ñÞ8¾–Ä ä qœØÀZûexq[ˆèúòO8²Ãoûªáì¥lûÚ¶µ|*ûÞ÷öý3ªÁ­eU÷›ÐÚèªß×ìWVÊÿ‚Zë蚺…ÓÕu˜t¹œSìû==7o¢ÕÆYmŠõ^YÇUKд¥ÞиB^[XÆöúûáq®r§Ÿ–Ûú­ Êÿ/“z¨XU?Äó7Xý¡…e9íÇ‚w }VMV±É#þE3Ì{ÁÔy­:yÖB¬Ñ^È+«4LÒ^й÷—ݳZ½c¨¹Š«^Yõ¹Šˆž??˜ã•WvWWËô‹E£KÐùÚ:Z\‡ˆ7uœ­TÍÙðS(j¹Å儤‘ÿºõb×½XtïMáÜIX‡¢«¸ðCï¸zþ ÞD6ä6_Jî"ðOÛ Æ=oò¬Oõì[÷«g ÙÍà|ÇÇ5ƒK«ñìÞLÁ‚쉮¹®?”•Ï>õ¶o ´jþ½|-qÿu~ »¹ÒüÙ¿ÁÆ/ÕûŒõ^{ŸZ„»ì­ZÛ~gµÒóó ûvq΃ìþù¾;¾é¯Mê*ÇñE\[Î=çt¾•|ý™Ñ_Ì׬œÇ:§p¾ SIk ¹}RÏ~§´ÇÚ㯺Y'&ž¿x6>†»7Uøˆ‘ÿp•oÚk‘½¸þpÓ)õí¥¼þ¤Ô´Fñ)8,CÜóWË þÙ›.ßËzÕ—ª†ë¥ãóóÈ{Ö•ªVSZŒžîÛµùü£ýFs`ßž­ƒòU.ymæÖ‘ýH`7Ž>õ4ÊX·µã¡žÍƒk£¼Œ—3ê¸Ç~þl®Ó"ÏçŒùÓZryÓÍûoºñ¡svp8Û3ŸïbïàÏMÂØtŸ}'}åøâ·žµ£9ÿæ¹;8šµsY¿ï®Ö­?Fç{Yzy“ón+·¶³¯cÕMö¶’D )M§¿·9}ì…ýÃ*8Z¿5®åU“7P³N²Ïû™ða­—d=ùž©;,’kY¬€wÓ­!çR¦äü‘²/ÒMJ>›ü–ÈÉ©„q$íKÛªÜrœ¬e¯ÚRïWI~Ž~U–¾ð4´Àp×<®: ½¼Tˆ¦‰ª²r £ŸG'£÷¹íÆ\ûŽC¥1"Èè-ñ¼y7.ÅE³#£”€'ËJIs]ª7s/Ìòë¢$‡œÏV¹jÙMFö1ÚdÈ~*Ö ¤1ÇdX Ŷ» i_c2r Î&#çXl2 É€<&ÃZ`¸k×…*‡QŒ)c–É*AFßh‰çÍ£¸qá(.šå½0™³fhÚî¬YPÉÞ#õ” I:Ò@GB1I’]7YrÌ£E–hÈ}i[‘e˜uBËTéõ~Hòsí²ô¥O³®N˸>R³æÑÉèC»1׸ãPiŒHeë[[ÚóæQܸpÍŽjÖ=0™Ð¬NÛ3Ÿðå[âèuT½ Z£lyâé‡52ñµ†zôeÍ¢û$QÂCÇÆ£ÏJ¶QÔûèʱÆT2ø£Ï{$ÂÑç—»¿ DŒ²޾zì%r•tôkÇ¥yøòN³Û#ôŽ>¹I^°tß™\zž¢ïÈö–) Tº±‘“ßÚÈ-q´¸pÜIû’¶*§Ïb²¼ N¸KÞ)ð˜`’´Õg@–¶údk¡£±» •:¼è½6dUÂÙ ­æ_g?F¹«Ã:›Ütx1'y®’É«IòÈê¼ÉŠ¢m«ÍCwAê°ÅìÖ -SðJ)÷C’Ÿk¿¥/}šµÐØ]ó¸0㳚íÒýnt2ú„Gèˆäõ~ŒÒQ‚‘Ö¾µåxÞn7.ÅE³£zqL¦êÂ!§í0IŒÝŸ4ÇEKóÅžü‹ã¾•Ènßè,&ëÅ·Íþ„® væëkòö_äiù²Õqúêž¶É^È×OZÝ3¤Õƒ¾~œ[Y±;o§¤ï5Cdö†È«¢ÛÍ\™5òÐkµÆ]´¯”P†Ô¸Ó•v òNÐ#À]ó¸.ÜNÅÐ 4#‚4F}£¥=o7ŠŽâ¢ÙÙm§_òÉœ·Ó¦í Û)û¦ÿÁŽzñÝE•w;E=õZ^öÒ²+þÙ¼¾´×AÕSù9éN[ö—Û ›a½¿¾ŠVò}/øˆBW×#ÄÒokXÛ Wd‡ÊÏZiIvèN²Ú4±úu|Œ@¶eîrYäJ¦mëÜ# ´ª†DY?,9È¢‡\5êry %V“dý²ºÝØMŽšÛÀ]&i_ÒV墟¢zk'k„ w•( /H76xä·6x2dŒw )tÛ÷Úu›„„¨tÌ}ñÚJžî½J­›¤c–-YÆáº71 î2IûºaXñÑø ËVÃ]%¨7Ó¾ ɘõuÌ¡™ŒÑà®!Å<¦ù²M³¾‡z™û[CvE[µñ6-K>¿o‡.–g¼oG'n²Œ#Ù]&i_Ê×õãIið‰ì5‚Ñ»Š—X}AÒ1Ë3 ˘õÉÖBGƒ»LÒ¾Æ4ß[CÆ4K¤¤É¨†Ê©W´RI9¤>$³¾pª,}‹}2YÆQì.HèKǬrOƒ¯è/ë„»Š“ð}A’~ø2TCŸl-t4vÆØÓðú÷Ú?âµEìMRëÈ^r¯CcJ¨‹Æ¤9©z#Ãkã.HÚ—¶UYý±µLbí~•Ôæi¿*ëfìC¶à®y\g-£“Ñ—\vcnnŒÒdô–xÞ<ŠŽâ¢Ù3ùåŸLl¹NÛaâ¬Óf¡ëG°/zs µ|ô›‹Æ`—õæb“-íg%(i§šÔ´É³ô¦-¢¥VmiÑË» i_;%èn(lò¡†@J`-0Ü5ë⥣ة†Y•#‚4F}kK{Þ<ŠŽâ¢Ù™wÔ/÷d.;ê0ÓvØU—ŽÜ3o.Îo‹.OÕ<`è÷ŸûF"@Ëˉ–”ƒlíxôàªñ#s‹Oò"ãkˆ»¼¤ÉšaTpË:ÊkRÜå%CUyI=Çm2Ü/î2Éò’,X^ÒdÍ0â.dÑ×ÈKâ#/‰'CÆhp—Is*õ^òGÄ$¾kj̲&k.ÍwÍ¥i~M‚õ¼Ëþ…°ËÀéðüex#g’eà #‡–È»áþ‘C¿#û‡§AÆp×2®² Ëètô}ÈÁ‰ªÛˆ ©<úîy÷¼Ý(n\8Š‹fGõâ˜LÕ…CNÛa,hÈÒaÉÛa=ÿùñµ.¦z¿ƒ~øÆŸÄDààš^½ye‡Iʹå=3üàœ‰}d¹¸gO?*Ô°Ù´¼žÉš%³]ÜnNʼ¶#¯7V¬ŒµÅ]&YnÍdÍ­YKͨÙý–[³~-¯gOSÙF w-ãºp£ŒlßXÇ2VÜFiŒ²õÝóîy»QܸpÍÎn£ü’Oæ¼Q4m‡Û(âzç“ÃYGŸÂÑCk]_._™ËžëÝð¹ã‚ž6E Ícg;2G «XuãÃóÏYï;‡úœ°ŸýU† ¹t?bÍŠŽ¸¢èýÊ*WzçXbW´ö(¦Èv(G–)u¼¤%Ÿ!ã룼çé#dÄõML–ÑŽ]¨ô\톹úqH4D\ÇŸ2äªé_=f7¥Ö­}Öâ~‹&˳Z±{“–Б³‘JKùVàwAÛ{ëSÇS;Æãôà‘ljÈ8‡ ½F£KØœ*îYµ¹GÈMu²z“5¸HÝäQPYºN2é&k=Ëê³]ƒŠœG¯ç Fg' v²À@ÖùOÅž¥ób3¹ê—d²°Änýìû Yç*&k£¾ ëGç-të'éù‚jãIZ°ÈÖVcŒmWiTY•Îw“=N8 '`¬½ÎáHi6ݵÞc<­i{g5mÕ1ýYÆã Ø›Žße»§¾úW£›4ª²¦©œ·ö¢.X@•=äQ3Ümœk¹ªc69¬d-8´n²_µ×6¶î¨Ï«R@ö‹ìuüÎXpžÈ[{7†¬¼ÑÚˆê ×`¬ÊÛŒ]ÇoóÙTg\ó†ÃúÑú¾ƨú飭µÖL½Í[×ñøjëˆ@cèm@=8-kWk=îÕ½ªõ™Ô6wÓI=ƒ­µ®Ë¬W匾õ¡«~¥Ÿa¥·q¥Ïq¥çiчݾГ|9×Yÿç}äu¥yéû'dÝ›ÙÚ4œ¡{V}X{Y |¦“¶Çc[ö~î‹M0›†Ó_µ›¬/Í[{ÃfÇÊ\T…½“mvÑiѰ5k£óÐÙ(·ãmNíFÖS{›¬MW9³™zl±˜ì›¥^acÛÊöªŒù”™o‹ÝÆ‘Çn¶Úì¹Ùd³óf“›_®ë¼íÚÛõ°j¯Ï}]w@Ÿ`ªnmv'ªíݵ˜Ý›ó=îC¾DuÅöÛΗØ8’[àKš=;À¯˜ï/©6>cs§OVEÂ<ª_ɶNð%yø’ªókþck¯6±G[3õ%–ð–uUŸä­OC[ùݺêÚ´ºøVÝjÃÖ«ÿh–‡ÿhfá?Z0=V=h~ø½×…Y¿ëØWx)­}޵ؾRŸQÇ>œÿQw{Iž[Ç.ÕÇîö[þ@u´´e?—jýà]¢Œ=¯{oØzÕõb6¶£D?Û”2ü ÊåãYjƒŠÙhø<¸tÞò°}‡ŽÍVªÏÈÃàho2[ igfZYühÓCuÙlAÓuÌ6øŒl¾\§[d»×Ãî³éÃYYC‘ý¬Ý³¬Ï2»9šÞ'Çh1‘2%‹;ânÓ‰¤ó’Ì®%‘“é\Òñ$³SIß'R³öºÙìZ„ý2;ö0Æ-®A?1»Ù–EØ&gÔ}8öj„½°1|Ãâå€8Èì&Î~ænm"l«]÷xÿ°cÊ^Ìî`ý†-öº—v²2ۇЛbãד "£ø×bq7|ê° ¾Š]@?Î|l½í[øÈ‚ø( þ2_T…ÅF˜¬ï—¾?˜MÖØ óU¡#ÃZ‡nçbѦ5¼Ã¡H­®Xì‚]‡hv¸`w|ƒÙ‚€¤o‹ÖFÇlû?4}Vý`ŸÛØ`Ç;ì~¨»‚8ä°‹ÝBUßa;D.»½*²Æ°‰"÷]LÔ½Ï×ÇŒöx?.Ö§ÚîžÂ2ÌÀnëÎÆæoÚ˜U?Ífø•6æG÷šÅï"φ†wk7æ爋ͭÚhÄ›¡uØ_+Qéþª6ž®ï=54[¯´{oðÕú„í.ÝÖ±"¶è¦ˆ-L¯ oæ·\PÌvf#A— “zbïèïÙC÷Ðí5oÙ§°Òç¾Òójú?WVûÂö‘s³ÿ ð‹¶/üŸùuœÆ/„ºÚƒ {Óì€ÃžµÃƒðñö.#ö¸É{ß䂸ÆÚk\h–Rì‰^·1'x"™ýQ»dÏJ+¤aß4ïX© I”;©í²]j¶R²=®©6±‡Ål¦æ ªÙ[}V´~²®o´ù×ðJì­Ù^ýU´¸Çì¶ùøŒ÷H[Ó¬{mgÏñN9ì|*s^Á®{·´wã^·\ßù õ1¡êêipËvØî(²öF°ó15.>¦æÅÇŒØÁ>ÒÓú˜ÓÒâcZY|L«‹±÷ó1Ý/>¦‡ÅÇô±ð½cÒlÌǘ¯†·2û˜<òêc$ôœ}Lùõ1²†³É®Ì>&»:û˜&{›wõ1"Ï>&û0û‘g“}˜}Löãä`Ñöqö1ÙÇÙÇdoñ¬ú˜<òEêc²³‘¹ž}Löyö1"Ï>&ëF>&û:û˜ì›ù‰ª\}ö1㤕ۃŸ}Lqö19¤ÙLjûž}Œèþìcrh³ÉÑÍ>&ÇaO¥‹ãìcDÏf“c™}Œ„ò³}Ÿ}Œ˜‚ÙÇd‹Eàcdß`œª²9ÕÙÇädŒêc²E\ð1"Ï>f¾®óœúìc$±>õXA*aƒÍƒúÏìcdœ³É–{„‘}5ûá}LŽqö1ÙòHð1Ùl|L}ö1Ùr’ð1²q]}Œ¬Ëìc²å¯àcpœrø˜üìc²ï³}˜}Löö­>FäÙLj.Í>Ftlö1³î¡Ÿ4û˜ìÓZŸg“G^NUSäÙÇìd|XÌÇÙÇìö…úœY>Ç™†™÷ ®»7û¨>&{‹ùÔÇd×g#{|ö1²÷g“]™}ŒØŠÙLj=™}Lvaö1bscï æcìÃ|L/‹éiñ1Ýö8|L·8>ÆòùæcZ[|L+‹iiñ1ã] v»¹ÅÇÔ¶ø˜Z³³ó©Ìøíz\|L ‹Ù]ßù‹ÙÇdÍPî|Ì8œ£>fW»98¿aC_1ÒØ‡þÖöÛ¸ÞMnË»Þ÷wrصAíj÷Ö4®§¹½É¯ëq†bo#·6»Š=M¯ghÎÍñ-Ë'Oß‘÷°†¨©áýL­EoÔgd´™+È;“›ôC ú+Fä¶lAØ[›'N6Ÿ\ŸþäÝ?}uóøs“/›Ç¿‡?Ÿøþw&¿yü·_ð÷Òæñßyü;ßùùë÷ã7ÿ·Ó5ùÉwþÃ[zëí?–Æ¿ù››“ßž¾ù×ÿÓý—o]ùËéä>ù㽫ï¿ñ7ßþñÕû’?~–>¸þgß{ðêïûÞƒOþýIº}z[þvûäé7~úßW7œ|óoï»r[þvû'\/Lß¾}åé·¾urõïOž>‘¿<ú™eóÝ“é©óxÄë1ñTç¸x´èËÄ#–“ЧsÙƒ¸ìA+\ö u.{  *žÌe4™NÄ#A>•= *{µŠÍÄ£/°T<‰ËøÆepšš‰'qÙ|‡Ot\ö &.{ Éi&ý(W>1'®|bÎ\ùÄœ¹ò‰9såsæÊ'æÂ“Ol8`Æc¯‡Ç^ƒ‡¨þc<<öÚxxìµñðØkðÕŒ‡§þc<<õðÕŒ‡§þc<<õåaªÿOýÇxxê?à!ªÿOýÇxxê?à!ªÿOýÇxxê?à!ªÿOýÇxxê?à!ªÿW>‘©þ¢úñpå™ê?ÆÃ•O¤ªÿà‹>ˆìµ}q ×ú0ÕRçªÿ€‡È^ƒ‡È^+Sý‘ªþ“2Wý—£‡v-?{|-mckÍ]ÑŸûV»GŸ[ÉŸÿp¯«¼øîîÇ/¾½ô%WeŸm}ö/>¹ùî©L‹Ÿ¼æÞ‹ë6;1´ éã:Ý2¹DyykÓmÞÚ¼¾yî±éí} ò2&?·֘¿øó°ÿ=úÆOß?Uõ99úùÿz£<ú†(Æc'"Ÿ\}è?_OÿwèÔý=+ 󞪅üÿÉkéƒ?Û©ÒÉó'ß¼ý$éäÊ_}p½}æ?ÝÎ60¨\ ¡ZçƒêÁBåHÕø EvžÏPd—ø EvÏPdïù Eö‰ÐPøJh(‚#4!ŠP Et„†"FBC ¡¡HŽÐP¤Hh(R!4©Š E.„†"wBCQ“™¹&3s!LfæJ˜ÌÌ•0™™+c2³1&3c2³1&3;c2³3&3;a2³8Âdfq„ÉÌâ“™Å&3‹'LfO˜Ì,0™Ya2³Âdf dÉL=@Gw4ΠȲIE–MÛÑ8ƒ"Ë&Y6I¡èŽÆY6ɠȲI€b;gPdÙ$ƒ"Ë&ŠíhœA‘e“ Š,›(¶£qE–M2(²l ØŽÆY6ɠȲIE˜M¢;gPdGã Šìh ØŽÆa2“îh ØŽÆa2“îh ØŽÆc2“íh ØŽÆc2“íhœBÑ3(Âd&ÝÑ8@±3(Âd&ÝÑ8@±3(Âd&ÝÑ8ƒbKf¦Dx4PlÙ$@±e“Šîh Ø²I€bË& ßÑ8@±e“Å–MR(º£q€bË&Š-›¤PtGãÅ–M[6I¡èŽÆŠ-›(¶l’BÑ[6 PlÙ$@f“øŽÆŠíh ØŽÆ)ÝÑ8@&3ùŽÆ)ÝÑ8@&3ùŽÆ)ÝÑ8@1&3éŽÆ)ÝÑ8@1&3éŽÆ ßÑ8@&3ùŽÆ)ÝÑ8@&3ùŽÆ)ÝÑ8@&3ùŽÆŠ-™Y áÑ8@±e“Å–MR(º£q€bË&Š-›$P|GãÅ–M[6I¡èŽÆŠ-›(¶l’BÑ[6 PlÙ$…¢;(¶l Ø²I Ew4PlÙ$@±e“E˜Mâ;(¶£q€b;§PtGãE˜Ìä;§PtGãE˜Ìä;§PtGãŘ̤;§PtGãŘ̤;'P|GãE˜Ìä;§PtGãE˜Ìä;§PtGãE˜Ìä;(²dfrïhœA‘e“ Š,›(¶£qE–M2(²l’BÑ3(²l’A‘e“Åv4ΠȲIE–MÛÑ8ƒ"Ë&Y6 PlGã Š,›dPdÙ$@±3(²l’A‘e“ Š0›Dw4ΠȎÆÙÑ8@±3(Âd&ÝÑ8@±3(Âd&ÝÑ8@±3(Æd&ÛÑ8@±3(Æd&ÛÑ8…¢;gP„ÉLº£q€b;gP„ÉLº£q€b;gP„ÉLº£qÅ–ÌLŽðh Ø²I€bË&)ÝÑ8@±e“Å–M(¾£q€bË&Š-›¤PtGãÅ–M[6I¡èŽÆŠ-›(¶l’BÑ[6 PlÙ$…¢;(¶l Ø²I€"Ì&ñÛÑ8@±S(º£q€"LfòS(º£q€"LfòS(º£q€bLfÒS(º£q€bLfÒ(¾£q€"LfòS(º£q€"LfòS(º£q€"Lfò[2³£q€bË&Š-›¤PtGãÅ–M[6I øŽÆŠ-›(¶l’BÑ[6 PlÙ$…¢;(¶l Ø²I Ew4PlÙ$@±e“Šîh Ø²I€bË&Š0›Äw4PlGãÅv4N¡èŽÆŠ0™Éw4N¡èŽÆŠ0™Éw4N¡èŽÆŠ1™Iw4N¡èŽÆŠ1™Iw4N øŽÆŠ0™Éw4N¡èŽÆŠ0™Éw4N¡èŽÆŠ0™Éw4P÷~2ó©›ôßw_Û¼³iÛ¨ÿàÂZ¾qkzâtóøÓò¦åÓÖ¥éô¦Þ¶í½'„dºæ¦ä½„øe›äÇ·6/=x|-ms-5=tܶÍçpôð±Û†]mGû°Í¡æ£/hÃØäíàèÅ·ù‘¥íçw÷¯š^‘k±¤à¾¸4üÚúžYüú¹zw×éÞýóƒÒÖ—Ù!¯:_uùÕ㸭­¦~ôkÇ¥™ÿÒé“›ïžÊ,ûÉË„åÞ‹l˜ìkš¢\™n™œšïÓ[›PÏÈhóÖæõÍsMoïi¡ÜôÇÖÂ_ÿ«mÿ»òÐO>¸þèË·¯< Jõü?ýÙ}¦Q·rûúÕÿúTí·¾u=ý‘ÐèT4ðöß\•VWÿ6}0+ðÓrùäçzõ/§÷~úþ›Ÿ™îûð¦2œrèdú%ã:~É8‡N _.N=têü’q4¿dœC§Ë/§:Q~É8‡N‘_2Ρ“ã—‹Ó¿dœC'Ä/çЩðKÅIîÐIðKÆ9túû’qø¾\è”÷%ã:Ù}É8‡Ns_.N8t‚û’qÚ¾dœC'µ/':}É8‡Nd_2ΡSØ—ŒC•6L‰*m˜UÚ0%ª´aÊTiÔ©Ò†)S¥ S¡J¦B•6L…*m˜*UÚ0Uª´aª\iÃF“6,.1âÃSˆ7šˆÚph"jàðâ ‡&¢6šˆ8<…xቨ ‡&¢O!Þph"jቨ‡¨o84…xá)ć§o84…xá)ć§o84…xá)ć§o84…xá)ÄUÚ¨o8TiC¢B¼ÜþÈqÎeë³éôÉÍwOu·Í9UjýW&Ò‡Þ¦Ôå­þÖNNqòÑw‘B)e“nl|ŽÉä·D.n´ÈÒ›Ýe’f6‹sm!Ç<á®âr™Ð$i«Ï€,mõÉÖBGcw™¤IÈÍÍÇî¹!¿¾yæ“*n[kíÐY_ûVŸŸf•ýÒqئ–´KÖ¼÷’»(ßîêP”\Tõ ò°»èZØÊk<úâÜTy÷óó;øìÒ@Tm×âÁ½ŸU6ŸdŠSkNW*%µ?6cò²%rˆ&˺Õî g=û$±1ä*²„ý*Ë»YÒƒåz)XÑT¥ÿ=ä¦ß–ã†,mrµ6]´!'Ü›´Ï}f¯Ç•[ƒ,^UŽ*GŒ3'•½iŽ-Žö°6U I2YB“$Ʋ¼í§èоˆ"Èë2ú—Ý*rKõ¹b™M®–^S9"Õ†1—¤_DióVtÞB¶~tƒö™m®äñÂn÷zåµu,^ç¡£ï0Ö¢ã5¶¦ªÑÆŸñÛǺ«.‰µ5-ºŽ¶F:†4v|P9Z]£”íº+ª'¦·º¦Ù›Ê;OÊiè›W}3V}­¿èjt‹›Ž%Ÿ>gýâæ}±ÓÝ;z²~›Í¿Œ]÷‘í‹XmÍ×[XÚ·±¿Âêzܵ]þ¦”MÔBwgì³L׃µ²µÏhU±6Æ?~¬Ê77Á©6êo©¬OIJ!rÞ=QdµÕ¸;8Õ–íY(zmeGgç­}í"c†ƒ“Èc NlböxrÐÿȂທ*þí}PV%èe‡c<*r²{³ôc;.ŽÈ=AÖçÆÑ^6mŽm´2‘cAŸAltŽÆ¥›Jì"ƯÊ,L29Ae¿H? »C䦲µ¯ÚÞæ!4mŸ­ ='hBP_"Z Æè´}Áõèµ½ÍI Ú?,‰È2þdcÛ“•=Áòõ_‹¬}¶°Èµ›œTÆ8cDÿÖ^ywr@oã©ËØ\_9¬X´=v½ðj{Xƒ ;§dóYµ}ó¦ã Ö&ëXñJÙ¬–èOSýɦW"{XÑ7•³éòz[_/›_ôÓô¶«>סÃYõy¥Û°<ÐyÑÛ/j]ç}¡qŒ7WëÑl]œZûfó†=e–Adµ¾/ûn'ë~ c†]û×uD°+Ýv-^w¬UÔë y]×[xdàÚ*êÜ¥6ô:ša4ž”`ABB ã#{ï9Û8ýYÙ{³HÏ=6½½÷Òâ5F“©l‡}iùæ_ÿÅŸÿƒýïѯ¾ÿÆ£x¹òÀÕÛ?ÿàúÓã/Ó{?yÿä_žàµåÛ{?øoÏË Ê÷íúÉú…æçO~rûOÐÑ•äÿû3ß¾òá±Á”“ì%6¦ jOÇ$fŽ©ðÙˆèøl„ÆtL…ÏFÄÎg#ä‘©ðÙy¥bª™/†Y &²V™ØbX0‘Ű`"‹a•‰-†Y &²Ld1lÍ|1,˜ÈbX0QŰÁ¹ÊÃ&ªv0QŰÆÄÃ&ªv0QŰÆÄÃ&ªv0QŰƒ‰*†5&®v0QŰƒ‰,†/†Y &²V™ØbX0‘Ű`"‹a•‰-†Y &²Ld1¬2±Å°`"‹aÁDÃVω,†Y «Ll1,˜ÈbX0‘ŰÊĉ,†Y &²V™ØbX0‘Ű`âŠaí#›\1¬1qŰÆÄ‰,†5&®Ö˜¸bX0‘ŰÆÄÃW kL\1,˜ÈbXcâŠa‰,†tŸéLd1l¤ûL—1±Å°‘î3]ƒ‰,†tŸéLd1l¤ûL×`"‹a#ÝgºY )>Óu¸oÐ ÙmSšJÐ/µ¯#–ïýÒ"~þ¼/«}à8õ®._úᯋµMÉ{|Í+äl_¤˜ÏÊ_svÞׇéÁÉïÞlÿüƒ“Gí[‹¯¾÷Í÷ßø»ûÒãž–xóá+ŒO»ž>xÙVFn¸}ýê{W±6§ï¿ñÓÿqõþ‡¾ýã«÷?ªßyü7"Ý–ÿÿ,}ð{ç´Ðxîâ7l_ Ï]üŠíËà wñ;¶/…ç.þn¾Kṋ¿œï2xâ]üí|—Âs=ߥðÜÅßÏw<é.þ‚¾Kṋ¿¡ïRxîâ¯è» ž|Gß§Ì“\£ŠG‡'5žxϦx´p}žÉxˆâÑÂõy&ã!ŠG ×ç™ÀîÏ3Q> stream xœí\ëÛÆ‡ï!Ë<áÞg»¶ƒÐEÎ'9Ëå.¹@ò%hšÔq?$9Äâ¤hãÆ…Q°û¡ÿ~wE.w†œ‘¨ÜI9±ßhoóžß,)¿ÓDdqêþzâç×Ñù·Eüò?ÑÛ(ËL¬Ò<~=£´#ÿåE‹®ÈEÏÇo¢ÙNÆ%ì^ï^6[ä*McÚÞý3úåqôM4;5þöËš°sßFe"ÝŸÙ¤~~aù2q^&Fǿ̲ç䢈§"ž¦±H³$ËãL뤰3^G?Œãb2M“¬ÐZ¨qüÊ}PJg™Çß¹¥T™´þ6û`¤ãø“©HDiÇ?¹q‘ëÔØ_<›¤‰eîèCK¥TQŽc5™Ê¤Úmôw;žç2Õ–þÐÏ&J¨DgêÇ‹'ÑVz»¿VúЏé­ Vø<)²k/¼N2ÿ7*ã¿ZñŸD–±Te…íNABéØògÊ:UV*ã\è» ÁÅŠÓ&6@k²^ky‘”y\˜¤VÚJgVP5Þô& ·½ è oN„L¬ôÙxFoúù›‰ ‡$5%ð «äÔŠ+1óîp/)Ä|±…´<–•àÆáû€ÉC@CÕ<gí›YŸ3r¼;Y¢Š\#ÅÜcÓ•zùHXdPQj÷ÃJ6Þ™\¼Š¦"-’"uÁb}=¾x±nS×Êl;Õ@‰2M‹²®½Ôp?Œ!™Jìp¸Ð±I-°òá”L‹? Ó—¶èüFqoÀðëÝP¨ã¡˜7V {,ÊwxÇ+{ƒQÙnÈäD±–&Ò&7žÃæóågͨÈl¯VÎ÷ ¨Þԃ͙ ]¤eÇ`ÍœÑDcýÙ¬T×B+×nÎD€uóÞ5 ?c§gK…ŸÕg£C‡HjT?ÀD.’¥nÀÌÞCîI¬›L(Âñ}!~ãð“©q×'Tø2ðêÑ:£®æO‘hï^e8ä IAÐy_À}_ÅìFëP¶PfvRÅ% Ùktà‰_yFB&Xžaø#SfPõ=›ƒ«*Èù1 ?¬f mc}>†&àÃc´_ûŒ~Â̆L›•· sh–¯˜ƒ>i¶LÀ(dñ¢ýl„ħÖÐÂXßžÖ€êÅ캂öÚÐ;¶ U÷‚K ³Áý ô–õy°ÝE \¯Åy-[W x†Ùæ!K*@XH}Jr9â¦Ó½Ä :Ÿ ?*Z‚ÏEo2{ ©3‘ü•ó¥(>¼# ?*ké«  7}Ö,¼C…›ôj«Æ·½Ñ¬¼ÙP»à÷C@Ÿ0ô! !··›™!'ÕEC‘'Bº'³p»I-lGo ¢øÍÊ<1Ò„Vz,ÝÜzñÔYR–Ká¡}äXd›À]…®‚›SEk˹WA-× r ™(}ANÅß%@Ìî5°Ù\»~…ÊÝN_±AV6×ú3C(˜~è/™dÚM›}T6â ÌrÔ¤"˜ öQ"¤SkHb(‡°™˜Â!±O‚Ù*·F3©ÙêQ“­Vé½6IÊ÷Ù{kþ¯Ü{Ë%¼÷+Æc¹ì´–tàŒwXX_)çÅžFT}²¯‡¯宿Þ__«ù¿r_û| _{Àøô;üôÅï8êœ^yÉWdy9£§hGÍÊ~¹‘Ê“­þK&…ÕþïýW3þÞ“wï­§(´ÈC‘_ÀQÜtZ™–ïº}Ôúztn×hØwyêr]W Éq×9!:°Ku]ºÛvÍ}ãÆ(ËÙÛ6:×à•›õÝ^+çyõ°;´kó"D‘ÐN‡ÑmFùn2³ÌJÛ¹ù¥púÃÖ&ï¼í¶‚çZ9€ê„þ;düw€TA‚o²¡qJ U=QªÅ+MýÆï-[HlK¦e®s½Cy‡ ²Ò„!yì­Å˜ÊBÿü옑z>¼JºCz>L¢Š‘wŸŒ tiúí@ûÌAаe¤˜%oÚ,ý—0¡»CÎC–8 “ùŒ*¬›”õ=t ƒ ¾!8ƒ6ÁÁF6j »¼k¦/óžÊ)£ÕòpÃc3í±,žv§H!´²7È•õ½VkzÛ~%i þÀ@?ahÏBy8 4 óaAùóo—÷Láž)g»…y/²ÞÔõnÖ¡ŠC˜øV]™jr™ày–Ó…™Œ‰ü+Š·¯)UÊñÓ^Ы´`ªHNT?wÌùWà›vA97!èRãníœIGdBGœpì†G‚²FëhGA$ô~ОNÇáÌòÜCª ‚þê=…®¸ËæÖߺ è¼Â3d Úò?ÈP·vÏ-“¬i§TUã—cûRѤñ+;²tj…düpÈœ3"e€+t¾i¤ºœáÜ‚þ+ºaZD¾{ â2xcQbŸçéštuœà¨2'ÁQ\ޏéXÓ~)y9À§¬3"BÝQÛµt¾Ïì½€)òw18ø2†Jµ ó5J[¤¥¸¾¥ße •ðU Ü‘¸háýš`fŽkþ<Ï´[ËÞª@u„V !{|ñ·Y”øÌ¡{\b#­±ƒÆýÊЬr§@‘WŒtsY¸/%Bå÷i­³¡ð> stream xœíTÁjA½÷1?`gÓvUuUw_*xK "ÁHÌFŒ+lü°ª³‘ݸ*¸F܆åMÏëz5ïÕÌ RD‚äë\,Ãã“W_Â*5”aÙ‘:ür¹‡oá‡pzŸC¯ÔZ´Z7WßJdI 4ÙßÍeX†ãÐUáäÙwjdÿõM|±„£Ùújkl ó¢ ™NÆ”€Šú3ÖˆÆX†7<§+%RàݘbΔ5pæ7  p=NSkT¶I†›ˆ”:À‘s¨ðÞaen´MÛų•<µ}Å–ÚçãD«°UyÑ)œ•ÿªŸå¤ì|A‰Jr6?Of³Á—Ùs þ€=9¦jöt—þy{~oz¨E›Å]ö0rdl%J·g€GãüqŸ,~&F6uÍ{bôd–eüËžo®¹­ƒ×¬É8÷𔫇´1 Ûrÿ=Þ$¤?¤$i¥ô¤Éº©˜×‘Ú.2mݺñzäXµøM©ÍB;Z(|ײnupé„Ì>F×®CZŇmcãàIkË 2Ûãž/å/(Çò?›ý²y ÷FI×Ùü…/‚}á¢|'v¾•®‚0endstream endobj 161 0 obj 458 endobj 165 0 obj <> stream xœíYÝo7Wûx<¶2­n‘nkÇ_­T¤JP5oÐ#iBi „„¯T ýÿ¥Îxwog6·”'8 ù|ãñ|ü<ó³sflëÀXþ ƒ—§‹'sòïâlPL°hNë(òðýÓdÜ _/öî™US)%8Òõád£ƒµ&ZúïëÅñ½Å£EÝÕ<þµìÙ"·žÿÕ 9~yj~Y“]Å`nK4ëãºíƒ.™•3+kœ…Ðx°mÎf}ºxº4¹YÙ6"‚OK³×ÐØe,ni~oV¾…œ".Íq³‚Ö9(~iNxÏÖ•¸4GbÁ^³rm&{`i^5Á•6xx¶ÞY’¡ g}‡„1Aâ— ]$C­ot:©ˤ„>)@ç%QRôÇók>žœÅn@’çv´>âŒRÀRðlf».d|Éàå,4·è»éÁÇ4Ä»8R\þêÂßXœj*×N)¿c!þ„Ë+æ C‡ë±O!¶dƒ‚V9‘‰$S‹Äœirmõlpñ§Õˆ!Ó—¿Ø‡Œ‹w]*Ú„Ê7ŒÞ±_È|›F“Ñ“Ìp«[šìÐdBö(1ÝÅ 0`×b†ñóª«ÅoHÜyZZd ¶Eª»n§glC,ˆîzÞ]c€B6Ü8a€\¹Øu†Î9ÅÈ”ä7QOÔ)t xÁ? Ùå%e]¸o1B‰Ù¹¨[h»cÇ“C#k#¢ûÓ€‰Â•Xðœq+©FªW+'-Œàg‹÷Ú“#1þƒä©#·ßªÎ öún‚SôX‘œâK¯-‰WG;<öüé´c¦i6Ò÷³hóùª[ö>å´GDÐe\–hI5ÔöªÅÌŒYî3iPÔ4\D¦ 2é³µo -"~¦éâ $Ý ²$@Ý:wš˜è‡>Öì?¥ÙKÖ.GvŒŒ¦à|òÔü{þRl&f¬­ÝU|hŒW‡g}ÖfmgŽs¬ð­ RF,] k¤v¹«dv³9Ҡ؆\9}"õ ˆÍî“(âæ’Ê&l#Èà]>„+Žàa½Wa.i¢rsSêÑ¢ÛF_ªt…!Zä}Þ5¯Þíh«h-sÍ· ‘)Òä/Ï5õ1ÞÄD`LRôjžO\øå½f_ñø|"Šj­DÐÁvŽ·Ã!Í@»þÈ -ZçÔQ½Ï±²¡ ‹ƒ-T|_Ë7zVbꎹ³|'極ó$tëÉ’PÛrɪPøú-F­uoæv¢Hlì+ÔhIfjKŒÈe_ŸÒl¢>¦H-QŸŠ>Xä~ u—°p°"fVyM4šêu›oœF#b믟FwÎ)}0òÅ¿4ù98OœøÜ¼¨åÓgç†âŸ-æy#È•™{ùLIÑqå%8yÓ9ؾØ~(Òõ=gŸÝð¡ÂZ,¼ 0‘?n0æ6Ýiî‘!Ió—”]QÊ®Ž"c&.@‘ç¸Õ´ïm}CòÛxlO›6eRf÷©âÓFÊ…ÝM˜Øp ³ÖO8ŽäGú­glN¡‰@·?›.O^ÕÞ³¤m–°?¬ãâä-²çëÁFýø|{ÈúnU£–¤s+=™Þ24y½`ó—üsôVH¥M}‘7¨£óÕWÍ ­Ý¹þIpîB Šj*a©EvþB;Ñ·|Bj ”]¾<¥”Jr«{bþ®ÿ¹_fn¦w:¢Í4U¾‰ª´J¼ v)Ró K$*¨6š‹þÌcé„¡êÚó0‘H’´ó6‰CÁT68§‡ÖµI÷K®¯D¸Ç—›²$“dö·Í¦²>ZüY¤Tendstream endobj 166 0 obj 1652 endobj 170 0 obj <> stream xœíXKo7 F{œ£@;¹yìTõ¼ôP (Ú[âµ]Lj&ûpý(ôÿ¥¤™)ÏøQl]¨043EñûH‘úÜŠNªVÄß0øt×|÷Öµ»?›ÏR¡5B·widãð¶Ñ®çáUsúºý£IšBF¢®/»Q…6B´Và¿/›fûºyÓ¤UÛ·?õ”ýÜøâ_zAÇŸîÚVh—t-„.ØvµM+áBZÙv)Û%NÐS­sÃïwÍáW‹Õuóã —’-ZÓApA‡¸"{ÞÃÂÐIÛ:Û©jÝ¿³Åðè¥ÐýÏÛÍb):å­¶ò°Ýч_óƒ3VUçñÁ£^Ûã…è„ãdþøÅ{Øž.–²“Þ }Èc+q¼mâ“`l¥xXß+Ïçô‹pI3à·^.@š£‘>" ²#œ¤1ß_àØ §…­ êZ,úHœp³0Q•ð~õ˾p»q0ü§Á òÉa§\åÌSÙÿR©3‹”ª¼¯càEÌÆ9(a\Ò¹Z tެ>›Rºî2~q=ôq!;#uðy³AYå}w$8Ü늀ÕýñsTÓ‹÷*Áãû÷‹¨1 ~þ>O5bÌ)Mr=tFÖy¥0Ú•µE+ŒIÎF,ê±|LWú€+IåâJgîØåà¥%ØôjýWö¦ « ÷äcÓ‰šº/ƒœõõà(B·}Šäõ2 %@»è±%€F/d®Zï½ ÌtæAbª ïy˜Î]Æw ùû³ÅÒt Æ(7ÁHÓ¡?t±Cþ=ÑÅ;ÆVÎ]êÙ5órÞ™×Üh®k›< ÂáÖÞÍx”¸nŽ_ÇQ\Ig+ÏUò­ó*bZV"Ú)«‘ø7)³€wZ³á¦—9gážQ^ª‘Q2ê;£Êòz?˜e…¬ÃÜ`̸ ´²ãUÞ$9­bŠÞW€1w³ào*`‹ç)Žq3ÂY§yžaÜgÅ4 ¸úÑ?4@{K½°¾š@‰F©P’åÖmŠ2Z×y¯àOå).Ó(®Z7$†ÙF"‰sœ•gÊ–²&_DÁ:“Nîœô¾xÁçÄ É” i.¥¹‚‚³É)YÝ‹¶iüh¢ â>…g.o¯È˜¦õôs9ùЍ§ËÒ\MÀÍÛB†x('‘ Ð9ö•ÎÙ‰Éq£'_?ÖßËèNÃþ:ù·<$^U‰¸|¡P²Òsɽ;B:m2ÇRæœ24KЖõè‘A õMDÁsòõ²XÂ2ðxt•È$QBr}o‡µ<ŽÉ:Wuʆ~7:¦ýßNÙ ë}…1ÄE=ÖÝ•ų̂«ŠRRqçQ1‘»þdaŒŽ$‹ï½7Ûlgÿf‚‰Uÿ tÛy‹¬Ûf Vß9ëyv… XYµw+"uÏ\¢îîh?Ç:5ÖÏmèÖµm—iRÀlªåoó’Â¥dˆcáƒRŬ¾'*&:½üa› S"²÷¥ûîž´ñþ—'ã²×\æõ¬|¢¢H¹n®H¡ùš¬@¦­ÀlARίÑÍÎ…(Ϊérä¹Íh• mÄl“RÄiíB¥\˦jpk«ƒ°mD8oß'š¬É)GÓÚuEŠèï <Ï™û«¯žq¢NT*‰A—Ñp_ÕmC*#¼ð5Դ⚬Béµ ­6)’´V*ÄÄIß_HŒÊùõÂTVß{Lö^Ýø€)ï.”Tžb7Õäe´)³j)mý>ëé‡.D†ªèŠdR+ñ “±¬º×½ aΛ—Gj`ÒjOw¶4÷Ìu³ìænú"„vNó—%c~Y2Q-PFú{åë5倧‚ë¸C…¡<ÌÔå“ýá?ŸX/\ñÐÕÉH Š?—Š;iSSøÁ7;ßÏcMïÅ›æ“:ô¦’ÁL‹M».ŠÊÐŒˆ`ªÒ›Í=›…‘¡3 ÇRäMóî§ãendstream endobj 171 0 obj 1501 endobj 175 0 obj <> stream xœíY[OÜFVa] qÍBu¥Ú%Z¯gìÛ!…REJ!å²-M[µQ“ª*­ ýû›õÎ7Þsv!ÊC :>œ™9—ï\ƾQ(•ˆŠßŠxsô/Rñîïà&P*:JÄuI™‚ü#HÒ}Gþ\í‰?ƒr§<ϵ´{ݾm‘è(&²n ÞîçAyª¸x1$¬ìM…qñS2~s-ŽV¯\$Y˜1x[dÏId*zRô"!#ªD(cÂÔJ\ßwDÚíE¡J‘º#~/´6JÅqY SõŸ7Þ„J‹‚LœZó_V±HæÚ;F’Ú«·ˆ•‰„Êt^@èÒypºãL.,ºÑm•×´5K¤y8ôÙG¥Ë´Iò¼ó9Ð-ëû`TÒy ìY _ß:™6ðW{ØóŽÝ¨í2z˜q2«þ©ôªVjeã²JëþÄ×kDo;‘;v–' Uoô–c¯{‰Ö}ÔôÔµÕ Œò¬@…LÂD N‚ÁžƒmGì}Ÿ®D†l­²Î¡c?égŽ}Èlø%í;Xù…##áˆfDŽ€>p"/'ÛS°#Çö­Ö=¹ö¿'?'™ÜBÏй5ãíâÃ_eþ½r‡a%Y¯É3¹I±» ìM ûS«Ú-±îƒ¯æ¦²J¡?f½ G%hs¼ÀÕÏßö-ÒðõªØKthîU±TšQÈÑ+t\ôI©Óf‰º5{ÎWsLšž.5Ï”áhÓš/1çÀ.-ˆ˜qìÚÎ:¨ïᮊýµGŽ$†þO"mˆŒ×…¶(?3Žß£=ƒ¡öäût¡:ö©Wk® êsº|z¡6Ëc»Œ*gÓ)ŸŸTäósÂDQ†•Ÿ(HG¢“Nû„†Õ:É(ZUHYaÌ6cù1^Ò™ kžN„&-=Ãø`™­W¯˜„s|»tŽÎѪagPâ¤=ý¹9¦WÂè°ÈÆ>~<‰So™£]½A³q¥¦Á±LwFÄÌFÍë#§Ì8ýº sm}ømzé"xkbñ/#%ödD-Ó2°7̲0¤f¡¾Jß5M9ÕKšª•H5v]¢ÂŠe¬E½K[ô)Љœq=TxZžN½6„fïÐåêzs4îÑùk­wÚÝ Ä!ÝšìÀʘšaÞÀ¼ˆìÇl *¶ö™?®R+sÇ~¯MJ«_LT¥ÄÔ™coÒÁx2SÌÊ-Ï\8Íz½“Éãm:™$Ýñ4äš¡¯%Ÿ'U‚!Ó·V@¦tœ­=nˆoÓ9ìzïψ Ù"Ô÷7¡jPƒ¥°¹Ócù ur‰öÄ‚G’›,LÊã*,çt±iø4Ý{HëVi¶h‘]ÂÑ–}1=Îê7w¦8áxàÓT &²ó¡c_bçê8™sà?uì½Kƒö ^ û´ŠUW,áÆÜä˜,gêÐ<af(ß 7ñÜR»PàhÒy N÷µåj“5Úc•#‹w\Œ ¸›† .OËûaÃë.3ÕÀä…ï¼2@P8qعÏkÿmÌ´KÇxé*Ñi&æ NhA‡þæu\ ß>RÆ=ì½[¬Æ GU Wh'mÐfBÐôC@•ºUZ„Áì,Á‰sZe“¸Ìe“C Þô Sö=è;ú˜ö‘ÿ’2 §ãê^(•7¾µh×5k 6¨–ɼ€zD§ ax·eÞ¨oÂlƈìÇ'yç[[•²E’<îÞoÜâîšm:+ïö#ºhµ<õ)­j¨ÂŽÎè ±Ä0˜b’˜¯Gž=T+šÓ2ì›Óþ?v|–§²€@½k!^Ê/­7¢ødE¢«ÈŸ<“âk|¬óüîK¿LSqü—•<þc ‚endstream endobj 176 0 obj 1509 endobj 182 0 obj <> stream xœuRÉNA ½×‘/0Ê¥{’.ÊKm×H7`Ä(¶ HÉDø)v±L5]Rë•ûùùµí-ÁÎ+¸Ý¸ƒ“ ÷Onëˆ*Ä °i(üã$¿ÃÏðÁ­ð×5¥ZkDÕz¼“¤ ¯Ç_n½pÇÁÎÉá PîÖÏö4#=¾ÝÀ×¥úªÀäUm¹n…´Ž`‚ a •| TcãÎ8§à©TDàjœÈcàÑ'Ä®ÇàCL™â>'å”ZÒk3bÈd’ì%®3ÁïÊHSÖðžeJMEÕj b«Ä¾!àÎ(‘1æV#yÂXyžyªaó²3.¬á³ÎbOÿ=Š®Xg‚½ñmíïv©«Î"Xõ?sJõÿöšr¹> stream xœ+T0Ð34R0A#9—K?È\!½˜«ËÈÈRÁÔÀD!Ì21s¸LÌÑØfW¸–BØ$KKKSC YEép#LL Ì €DQ*WšW ÑÆ㲪Pj¢®‘‘)4P021‡¨{ÂÐÂRÁ%d†B¸a…–… ì'74endstream endobj 188 0 obj 133 endobj 4 0 obj <> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 21 0 obj <> /Contents 22 0 R >> endobj 26 0 obj <> /Contents 27 0 R >> endobj 31 0 obj <> /Contents 32 0 R >> endobj 36 0 obj <> /Contents 37 0 R >> endobj 41 0 obj <> /Contents 42 0 R >> endobj 46 0 obj <> /Contents 47 0 R >> endobj 51 0 obj <> /Contents 52 0 R >> endobj 56 0 obj <> /Contents 57 0 R >> endobj 61 0 obj <> /Contents 62 0 R >> endobj 66 0 obj <> /Contents 67 0 R >> endobj 77 0 obj <> /Contents 78 0 R >> endobj 82 0 obj <> /Contents 83 0 R >> endobj 87 0 obj <> /Contents 88 0 R >> endobj 92 0 obj <> /Contents 93 0 R >> endobj 99 0 obj <> /Contents 100 0 R >> endobj 104 0 obj <> /Contents 105 0 R >> endobj 109 0 obj <> /Contents 110 0 R >> endobj 114 0 obj <> /Contents 115 0 R >> endobj 119 0 obj <> /Contents 120 0 R >> endobj 124 0 obj <> /Contents 125 0 R >> endobj 129 0 obj <> /Contents 130 0 R >> endobj 134 0 obj <> /Contents 135 0 R >> endobj 139 0 obj <> /Contents 140 0 R >> endobj 144 0 obj <> /Contents 145 0 R >> endobj 149 0 obj <> /Contents 150 0 R >> endobj 154 0 obj <> /Contents 155 0 R >> endobj 159 0 obj <> /Contents 160 0 R >> endobj 164 0 obj <> /Contents 165 0 R >> endobj 169 0 obj <> /Contents 170 0 R >> endobj 174 0 obj <> /Contents 175 0 R >> endobj 181 0 obj <> /Contents 182 0 R >> endobj 186 0 obj <> /Contents 187 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 21 0 R 26 0 R 31 0 R 36 0 R 41 0 R 46 0 R 51 0 R 56 0 R 61 0 R 66 0 R 77 0 R 82 0 R 87 0 R 92 0 R 99 0 R 104 0 R 109 0 R 114 0 R 119 0 R 124 0 R 129 0 R 134 0 R 139 0 R 144 0 R 149 0 R 154 0 R 159 0 R 164 0 R 169 0 R 174 0 R 181 0 R 186 0 R ] /Count 34 /Rotate 90>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 8 0 obj << /Registry(Adobe) /Ordering(WinCharSetFFFF) /Supplement 0 >> endobj 12 0 obj <> endobj 13 0 obj <> endobj 19 0 obj <> endobj 20 0 obj <> endobj 24 0 obj <> endobj 25 0 obj <> endobj 29 0 obj <> endobj 30 0 obj <> endobj 34 0 obj <> endobj 35 0 obj <> endobj 39 0 obj <> endobj 40 0 obj <> endobj 44 0 obj <> endobj 45 0 obj <> endobj 49 0 obj <> endobj 50 0 obj <> endobj 54 0 obj <> endobj 55 0 obj <> endobj 59 0 obj <> endobj 60 0 obj <> endobj 64 0 obj <> endobj 65 0 obj <> endobj 75 0 obj <> endobj 76 0 obj <> endobj 80 0 obj <> endobj 81 0 obj <> endobj 85 0 obj <> endobj 86 0 obj <> endobj 90 0 obj <> endobj 91 0 obj <> endobj 97 0 obj <> endobj 98 0 obj <> endobj 102 0 obj <> endobj 103 0 obj <> endobj 107 0 obj <> endobj 108 0 obj <> endobj 112 0 obj <> endobj 113 0 obj <> endobj 117 0 obj <> endobj 118 0 obj <> endobj 122 0 obj <> endobj 123 0 obj <> endobj 127 0 obj <> endobj 128 0 obj <> endobj 132 0 obj <> endobj 133 0 obj <> endobj 137 0 obj <> endobj 138 0 obj <> endobj 142 0 obj <> endobj 143 0 obj <> endobj 147 0 obj <> endobj 148 0 obj <> endobj 152 0 obj <> endobj 153 0 obj <> endobj 157 0 obj <> endobj 158 0 obj <> endobj 162 0 obj <> endobj 163 0 obj <> endobj 167 0 obj <> endobj 168 0 obj <> endobj 172 0 obj <> endobj 173 0 obj <> endobj 178 0 obj <> endobj 179 0 obj <> endobj 177 0 obj <>/Length 18858>>stream xœíÝ]ˆ&×}çñ!N.bÆi<Ö-á•1±=ÝZüÂn< b@w°d2VXBÀDÂÌö’‹†…ŒÁ¬e }aÖi¢a0,Á¯ìÅ;Ãn÷ŠM²KÓÛ–½ÁD;2ö4b¬!7ÉÆ[™Z5çåþ§^Ï9õý\4ÏSOÕ9§êyº~uêõžŸýìg(È=¤;…!Ý( é@iHwJCºPÒ€Òt 4¤;¥!Ý( é@iHw$dccãæÍ›Ê·Áá)P¶mÄY¸ççí?{õ‘Q oÊ7J³‡ŒËY¾Pil{¦n¿^윎U©=°®±­wx4¿ÉÕÅN´”ÒùyÄ"Ý‘Ò}DS¬•š2’Vœ¼˜t}N'mÕ¸é,Á®Nžj¶­LÒA7äš×öyªÔ,Ø6Ò}¬Œ%¯t±Q鮜Št"Ý‘–6toß¶c:SßA˜¤ù¨;š0‰¯µÎÂÛ!Îö;ktÎæ-çê¯}Ý~Ô¶1\(Ó™îF B]ÎöU ™'×¢œ5¡óêl›s?³¯ ÎIF™Óà·é[>šJ+k1 5©¢Ð÷­ùŠ•7ï”í4—s´¨¤îH‹îÕÝigŒY¹öä “Tþ\×pVgïlÖ"ϦsŽ ¾æÉ=lc•í‹%_ÆëÊZúêrn7)|ɵ(gMÓcvÎÂsêl‰]‹Pouw,É…W®ÅëÜC®™£¨*|KRDÀ™¸Qí”!vökZ•Òiñíï‘îö瘾òÈ#ó·€”‘t 4¤;¥!Ý( é@iHwJCºPÒ€Òt 4e¦û=÷ÜÓ¾Np›æÍÖ°™«Q÷{ôÑÏW ËÁž£í ÎH s `Ye¦{•ö ŽtïgàŒ,»Ú\ï6À9PYé@@º6a U¤°ZOvî\8BÕ=Z•æŒH é>Ú„)T‘ÂÊ7Ù¹K6c–ìŒHé>Ú„)T‘ÂÊ7Ù¹[jጾ#t´¢to‡g6µãg<9§m>Õœ'å<;Ì.\Ó~_EšÆ+‹ªËÊ©w •4=`¡|á'!n ï1½÷½ûj‘NvÊosê¯ÀœV—î`ò +>yäàûD*MºGµsxQÊeåµ±2ÖñæÞ_Äè“D5[9~ìOHØ"QNâk6}w kL÷ØxÖOkï·šBb߯‡Ú#õ8Ö騙Ò/šI4s5›Sü`‚ßæÀ€­.Ýûõ)£ú1³¥»f®•EÅ6)ª ©¥»r€f™hæbà=vö̼ù A¤;éÞ§IQmH*Ý[ɦ»ÑN»Á¥»éäˆtÏ8ݧÞ3_jº+»ïc¥{pœ©0So>Héžbº;cI÷~åÈ3%OÕk.”Ç|c.•îì™òEº÷OwåäBRA'ªÊ‰Þ Á0d9÷(Y_~Ôl6Fœ û{jdÿ¸Å0ä7`qe¦»2 åÝɃ¹Ò%w£#8wk*R6>XÔÀ5»°üƒí+ïßʾWFS‹¬÷¯¥—d%sÔö–ܶÊóåHM™é A@îHwà.D;€îÀ?a‡3€’t 4¤;¥!Ý( é@iHwJCºPšÌÒý—~é—~îç~îçþ盫“ÿñÿqéFVäcûØÒMPÉ)Ý€é@iHwJCºPÒ€Òt 4¤;¥!Ý( é@iHw@ÆüñæÅ³Ï>Û}Û¨´±±qóæÍÛ¶ Ò·:ÑÛï¾¶ß êh¯ÿ’î$AHwç[íé@"ät÷ ´±g€TèÓ]ŽyÒ€TÐw·‘‘î6Ò7ÒÝFºò6üœùé@*F¹Þ½"ÝHDÔ½ê8gäŠt 4¤;¥!Ý( éët³’N[Ké`½¢.—Z°L'ßK};s×<ÍŽd_HËám|jWÝiÉ›Îê„9’Ë/ì1q¤;€õÊ7ÝÛ`³#³–òkß¾ºÚ·•?;•élO³:yŽ”óXÆÒÀŠÌ½Ê*†´Ä—è¾ávø9ï..ßf\(ÜW¬¯Fg™Æ–G–øjñé·ÇÛ<Œ=âpe_¹Gº·3Rõêú+'Ìé !B°Ÿ¢yô§ó­P”æ‘$ÊÇ–DÅy¿'–Ʀ{uw¤ ?7~ñtê‚Ów€™D¥»=Nð9"3§»²%¼4Lwa þ,ú¨b•Cä(,—<¢!T>Ò@B†ôÝ5£Í™îíŠoDåh¿ãÜó§ûÀãî±çÌ“î0eXúÆŽš+ }X¸æ¸»¾%BE­'¯9_ »£•½Þàp͘¾”}ÏóMwÍ5âC®wwÖÕ¾­x¾{òHw QÞnôòG¤¬bHKbï¹6äÞpÁ·<ÜY޾>òEíBã‡_¦Ÿ Ò@BHwÓ]( ¯ÔéÞ{7Mº;Ë'ÝÀ­½ lu÷£\œwg«ÊÔÒtƒç»ÇO˜ÒÀ’Jwß®o»S§»²%ÆhöÈNšŒ ¦»0P}T±Ê!r” –KѪNé`é¤{ýá¥ûD»âQ9Úï8÷üé>ð¸{ì9ó¤;èSÙx=éq÷Ç­3ûzwJwáœeK„ŠZ=N^s¾vG+{½Ááš1});úžyΪ€8útl^øöØJh˱úúåUä9ó¾·šYP¶D˜»Æ žïî/ÜW¬qž]>gÕJ6V経Np¦HwJCºPÒ€ÒtK¸¨Ý7° ¤; X¤;Å*8ÈHw@ùHwÜì;úï%Üþ5&t–Ö-°-Íw‹ÀöîôÕÛ7˜³Ÿc+ßv×~nŽê«ÒïI¾Çï.¬yJ^åÊu9Èco¹Ÿ)Ò¦y’žóþŸË'èñÈçƒdxF`¥‚y,¿5¦i?]ºWw:÷ö§¤;`¥FOw_„OÚww~JºÖK~¶¯ï­ï#åpÁð‡ÙsÜï)o=Ò½[Z%>Ò^>g^ù:xÄsæ@ÒHwJCºPÒ€Òt0­q¯/*ãj¥Ù̶¸ù^|•ò#©HwS#ÝDº¯é¬ˆþ>!#R®jÇ cIù{™¢Òb~`¤;°"¤;b¥ü½îÒXã¡Ý'zu‡ã+o+ÖèÞöËy;°ªsG°îÀêîzcVw?ÒÛWˆ1¾<\صë¬Åù¼QgQö|9ŸhœMùÉåQO7w6Õ¹ü5ÃuënÞ®Yhš/7ø5ç:8¿™"ÝÑßÔ~D·pSqƒ½nê›Jø¨²Öã¾ñ5Ã}5g-•'bS¦Y ¾Ìî1Ž3ø£…æk ûº}ûc8×ôÝäGù`®`ºË”«KýZ5v]ìKwß$Á!ÁÍyL¹=ÁÙåõ a»—Üð¢ŒUÍAžëàNÒ@fì.x÷S!Ý+q7¾!ØÙjÉ]yç˜Â Ý7¾1ÐÓn‰\˸鮙Í\Ò½ò<=]³ätïñcÓÝ®‚t±¨'x6/œA®ß3_)Æ~ûQÔÞÍê{xºWLRîxX¤ïîl§0GÁ­™à›î=~'±}w¡º|‘îÀл߫˜Gp?î¾q÷QûSߘö[;<œ5Vº¨jñŒX_EòlÎßwwe¤G!Îi5?†süârDºëÒ ònÝ÷Ôm{äjŒsæ]¨ ë41g 7Åsæ}5VÖÆ„=ÜXû;ßvÕÎò+ÏÖFp6G|-ä\°ÎLµç7*/}; |ß‹°”„¯I³ÇÂ׆Ü3žtp—`ǽ`ú­²aH bw’U f@º0ùζ+Û¸»g}âÙí1ŒËègŠt 4¤;¥!Ý( é@iHwJCºPÒ€Òt 4¤;¥!Ý( é@iHwJCºPšÓÝx a—ðpÃÙž@¬hHKf˜‹±€=¤êù ¼çç}ýìÕG„©„O‡è¶GY…¯1Ó5rá«YÏÃÂ×3§˜BéÞeÿ{î­3Ý»ôq8QpÅ*k!ݳ³ž9ÅH÷Ú0îøcM›r]½kœ¢‘˦»³LME³5f ¦{š™šf«2Ò}6Œ;þXÓ¦\Wï K÷!ýoÒ}xÕ J³UHÙJÓ½=6ß~ÚÝáÜŽ,¬_œã8 1Æì6ÉYŽsá»;Ž1¿ÎÉ…Æ i>íVá[’±õvG0fÜh†ðÝ9[Þ-\®Qó¥·|½çæ…oŸyóÚžÖÇ.$Xµ¯%ÎÆØU´eúFp6Ì8ÁÙ*ùßDø…óc„É+×/ø£ò5Ø×0g“ìÒ‚?-}™š_2`[cºWžôr¦£ý¿$Œã[ãøþíåáFÉý1¶R{¸1GvÞ EËš!òƒKÛIÿ¥wڜʑUÈÃ}#Ø›BÛ‚#Èå‚ÿ&Â÷hŒ`—lOnWáÛ˜ŽúŸò«œJóï¦,“¾;b­1Ý…ÿ·ÿBÁÿ@Í¿½=~°åšÙÑë+Ù÷‘ØQõ ÕuWĽ7äY‹m[+˜pvoXîÐË…*›êk†¯ Ê Êöÿ&±ÛXQ?¼*ô£Šjª0¡þß¼_™¤;b‘îîU@Cî=Ø£›îöGQéî›ÜžÁqÓ]_¯¯:ß&—rfcÓ]Ù¶VTºWwzÃQ{_±¾v™Ý†§»]2Ý•ÿ&ó¤»~;~Ät¯<ÿnöä¤;FGº{;»Ê³õÝ53(ÏNº†÷Ý•Å: Ѥ»œ Êt×´­Ûw÷M"w‚£vÎËÉÍŽí»kYÅü›”îö8úœ&Ý1é.ýÏ×þŠ#yÊvîE±1ò ê·ŒN‰]ÝënåmXÇk5éµAPõ:îî|­Ob¹B-•uœ|Ü]Žÿ¨vùeÚߣ¯äÊóÃq›O±ÎöÇþ»Ée’îˆBº;2µýTøOnGÐünxø9ë Ž lŒP¾³Ì`cŒ!íJY_Tp^ì5«la Tw/|_]ʶ5ú3_)r1xμ=¦=r·ç&…1•2Ååêäsæåå÷ho¿uþåI|UT®Ÿ®f*£ýQÿnvö$Q½¬Yáé¤)öü8ˆBº³:»0Ò€Òt 4¤;¥!Ý( é@iHwJCºPÒ€Òt 4+J÷wþá¿]º ÀªýÝ¿ùK7X ÒÀLHw`6¤;€™îÀlHw=}èïüòéÏ\ùÛïþñ_é|÷/üâûßuow`ƒtfCºè£NñOÜ{ÿWãw_ø›ãfÈæ{Þûîæu=ð7_øccÒ˜ é@ë©SgN|_ýâ̯l½yófý÷{oüä•?~õÖëo½þçwÇtfCºÐúÏg~§î Wo§xýâ·?ò«õßG¿óµÿýÿ.89é̆tÐ_“÷üOÿ^32é̆tÐ_îg~eëôן¢ï$…t/ÐëŒ!_þ‹«ö ÌŨÆòÕ—탾iú÷ªÿæûíÓN¼¡ÙjÙø£Ýî@Ò˜ é^ dÓ½îÒ½ÿ]÷Ž^ì?ùi{~›Þ並H\ûËj8ïõ–‹fw½= ¤;0Ò}IÆ^ÙìÄv‘›3ȦkϤìýÌ£¸ù…=¡ÆÏ\Nÿäÿæ–´í)uíýh9î,ˆt_éž‘)Ò½9>-“È~ñµÖ ê{oüÄyQ"é̆t_éž‘¥Ò½º³—>å+ø›¾ûõ·^óþ^>tfCº/‰tÏÈén_N&H¿o«ƒ¿›÷¤;0Ò}I¤{F¦H÷Ø’x'¾Qo²<úþæ lß‚žtfCº/iméÞ¬÷§kÏÔF?‰½ÇæN½‘ñ'ßÿË”;ñõLýöG~µõúEý¶ÛTÒ˜ 龤:í8qrôbíÀ¨û|Íó:Çõü^I¼™¸Þ›wéßô¦¹ù?WÄK!Ý ”ìõî0 Ùy³àMošÓèþêõ97/šO¿øÉOWwß=”@º³!Ý DºçÂw+½E:ñÍ…îq™æ˜K{¥{Ww4Ò˜ é^ Ò=­lôæïÄ7§úwc»9Ö^Ýé¬7Ç€N|ß™_Ù2ž Kº³!Ý DºgáCïxçµÏi¬Òæ¼smÓrùGå|î;é̆tOÈX§”Û‡rëUÿðbkãî®CâsüøX¥MaÒM"å­lôæìÄßüž¼=a÷ï+Ò˜鞨{›,âôן1ÝG·qMtcùÖD_÷<ûi~ø[Pÿ–Oóå!ÝÙî !Ý“2uºOwoŸ?cß©ÆÐì½7ú÷¤;0Ò=!¤{RòM÷Ƥø¦ñ¾ßCó¨ßúÇlà §ëÄÛÅ5ê.û•ÏìtOêìž @º³!ÝBº'¥€toLщwž4W½=S/üÍñ•¿ýî£øhýºû›!ÝÙî q¦{ÓÝÇ:gÞ.g†tëôþŒù:݇ßÊFoô›Þ8Oš«î>ÛÎÞ{Oº³!ÝâL÷?Ú-g”ëÝ™!Ý{Ìï(ì£à%¥{5öõrΓ檷ûîuŸþú[¯ýS¿WÏ é,‚tOé^­&Ýǽ•Þˆøægfìœ7¾S®ˆ–Bº'„t¯V“îKs0b¾ý…ßÞ¤È~„ é̆tOé^­&Ýç?ƒ²îµw×6Šú|äþSú“îÀlH÷„8×ø~ã+oþÃßG•c\’TÝI÷oþðå¨B>qïý¤{éÞ\qžÈÃàIw`6¤{B¸"®ZMºO}+›êNûÿäûYoÕ¥ë Ò˜ éžÒ½"ÝÇ0ÿ3a•Hw`6¤{BH÷j5é>Ý­lì;̤ƒtfCº'„t¯V“îÓ]ìNº¨H÷¤îé>ØÔ7á‚tfCº'„t¯Ö‘îSßÊ&Ùî;é̆tOˆ3Ýë5ul9_üä§~aóT¨B8qÒ>í‹tÅÔénß 6¤;0Ò=!ÜͦZGºÏ°“æÑo|eŠ¿Dº³!ÝBºWË=#nó=ï5ZÖéžf÷tfCº'„tOÊté>ílª±¿¬QîÀlH÷„îIÉ=Ý쾓îÀlH÷„îI™.Ý…[Ù4·…ÿêˇÍÃчÔRõèw¾–T÷tfCº'Ĩu0Ç–c÷ ënÜ+7~UÈ©“ﳈt…óbwã¶ð£ÜÌ®ÇVݤHw`6¤{B¸Þ=)³¥{“ëF rÕ\jÝwÒ˜ éžµ¥{`Ÿ¸÷þáåØ×÷×}ÖWop=ØD÷„¹ù…½æEs_-åußIw`6¤{BÖ–îc±wt'{³¶êÎ6Í•ÏìÔõçôŠ|Iú(û6’º1-é̆tOéîôÔ©3õ_¡•îudþþÇý‹×¾³àfJðÊÚ ë¾“îÀlH÷„î¶v™Ô}ÐÏ\vvvõéÞ^Š–àÕbN…ußIw`6¤{Bê${ôÕ¯ïÕÕëwßåUQ÷†›¹Ëkd›óì³J—îuQÆ5féôhe£ôŽw~ùôg|çøòIyV|ÿ×t6nœFé¾/þXXÒ˜ éž9öŒp’ï3/wÜK/ì–¨ÄÛ®éÏ™—/0Ky/ý(žY¼ûNº³!ÝSWçúç>øqaÍn÷;ƒO‘‘~þN¼¼íR…î¨u½»ðòÍã$œGeÙî;é̆tOW“ëö ð]Î]ÊšgÄåΓñÁ\¯7K½W]°\güW_>LmGý(Ý÷e÷OîÀlH÷5¾o¾ç½r_Íw´XùXyOx[…æž©=4ó¼d_³3¹Çh5ç©¥p"BWÏ•!ÝÙîË«×Úïþ…_|ÿ»î}àÄÉS'ß õ†p€\ÿ|wá,zCóÜñWnüøÕ[7®¿õú›ÿð÷ý¢ŽÕz6åÁfúÝg^9ïÍŒ_ùÛïþÕë?Zü¢ÁÜ»ï¤;0Ò}Iý®tòݲ­¥O÷F¿›™ÇÞG¿%QEžÄÞû)2šã]‹Ÿv7Öi—꾓îÀlH÷%õHwÍ9í±é^éöÒÛ6þh7j|åe]±!:ðqúyOá†0Y?W†tfCº/)ª+¦? Ü#ÝO:£Üg^õº…m0™ú]7Ê`5óžÂp³~® é̆t_’ò<©ØË´z§{Ó¤OÜ{¿¦/Û#!äÆõ>M}¬ç»Ë)¤sCþ!Ý÷æ°Î7ø2{沑îK’Ó½÷ùêCÒ½U÷¹ÿ”ÐíqkûHÄ(a3Vº·œûê?èÞê}ºÆâ—îÀlH÷…u“©{^ú3´GI÷VóÍÉüõÛ¶äéÞnÍ´'¢6£§{ÃØ¾Iá {+ªûžÎ-zHw`6¤ûÂê~Xý·ÎòúïX;KÇMwC{ý^ýºG`!¦}Þšò.7í]i—ºKL{ÎyR·©)é̆tON{²\Ò½í…ïŸ4-×ÜñÔéÞ–ßFl7ƒ»Á\o|´›tv{ºßH³G¤9{±êüBŒ%ÖÝKŸÔ9ƒ¤;0Ò=9môH÷îò~ëôG †lX4Ú0¶ûÝtlg°e_?Õ·Lºé[Ý9%pÜ;¾9¿Gû‚ûÀ|·Ýgæ¶s×=Ñ¡Ùúi‹m7ÚbÓy,}Eº3"Ý“c¯»õ“IJ«X6ÝùÝeg÷°´s‰ù®0t.´ïÖîÜcojÈéÞžjg,Ø›_Øë´÷ÙÛÝóö«ÎAŠæ Ñ7øò';ÂD§Ü“îÀlH÷ä$’îuÆô(°pžîfaßC·³Þ„YwG´ÑUíü6Î$è.çöˆxc”'¨:¿G»“]öè>Z¿hÛÐwß!6Ýmˆö@F3a½Ýóûÿua#c,¤;0Ò=9mwMŸîõ$ÜÿÿÏ_kŽLWâóv{ Þûn6CÔíoÓ¥y†©+ÝÎz›åÝÝìÝð탽0ãîMÊ 7ä-Ý ‹ºŠ¿zýGŸûàÇÛöçùrß½²Î:´Ks¦{[ îòØHw`6¤{r|+t¥à¡÷nêØÙ0sºws½²²Y˜ªî¬ÝëzÆëîow`w;À^γêš3ðÛ­Ÿ¶UÍ5~±s7üŠ8ù¸{åIw{Æ›_E“ôí¬žöÝ¿ð‹õ‹z7#4#wG÷|{Ò˜ éžœî ½GºwìûÎHoÌ–îξrïu²÷á Ι·›×´M¸Ùœ“}ƒï{ñÛoœ3_Ý}¨ÞÀöÙ ÝðnK¨»ìï×½Õ;í7çá·#´}úqï–Hº³!Ý“30ÝåÉíË´Œ&M÷æjõNœ4:Çunu/ å°wð(¾æŠ¸fׂqT^æ½?¼á<Ý—îÂõî•+Ý»={cƒÀïæ|…vÏ|·ïΞy k¤û¦¾W]*ƪ?xFz5Aº·‰^ç·“­º.{—x¿¾rÙp¾q‚§5³ðÅO~:ê>¯ö©Ýœ¶S9ö^uÎrº'vã¹Ý3ßœß4¬ž¶)¹M÷îÖ×ґîÀlH÷LîÁ}¼B2›î¾9mº¿í®ïn]Í3i†Ü/½¢Â´=îfÓî®×ÜÎyŹpV„ðXãòÝ“ü»énlt˱÷"4K†sæR‘î ïD[uÎi—oFëë×ú½7Áù½7~"„å¸éÞ •æn¬u¨·ÏSo?µëêöÖw"»÷q«B›½ïUW×Rÿ 5p>ëO¸UQð¡yím}ëO+âŒíò•WÄÕS½zë†~ &é̆tOш·œ³ïÓ.¯¯ÇM÷æ&*u`´‰Þ%¤{£¹ÒOíF§_¿ç|¢{µ:¿áœGaÏ|ó¢-ÄyU…|0¢YÔíÆb»]h¤»q`~ô%Cº³!ÝS4üÙç½·æ¼".˜îJÆeu•®»?[ºwË" Oé¹ó"~ù°‹qVQ¸î¾ñ"ÝÙî)žî½EÓͤºÃÝ£êJ±×º1<Ý×­i®˜¯¦OwûøòÃ]|_º±¡æ¼ˆ¿{ß—ÿâj·Øf¯IlºÓwrGº§hÈcâ½o‰3üŒ?ýIpCÒ½yÖ­}=º~qÍöŒ¸êísº­ÕŸ3oœ$è|Œ¬ïÒ»êî{ÕÓÝxR-w³òEº§hÈcâZ¾Cï2ßíÕôæIwã|½÷À™:Ý+ÿ¦RÔõîÝ{ ú ¶É¢Ò½ûQì¥ ¤;0Ò=EMð ¼q[sgÖº+öÅk߉êµçf÷¦ Ë!éÞì nNÂï—Í3¤»q%[#ö^u•k“Ë($˜îÍ®{YÕ_Aý÷«/>rÿ©z„vø+7~<ÅscIw`6¤{¢‚'·—AyuYÖµ·O«#Sx²j½)ÐÞÖnRsù@ÀvH d×ßz}Ü»ÉAº³!ÝÌ„tfCº˜ é̆t0Ò˜ é`&¤;0ÒÀLHw`6¤;€™îÀlHw3!ÝÙ¬(ÝX Ò€Òt 4¤;¥!Ý( é@iHwJCºPÒ€Òt 4¤;¥!Ý( é@iHwJCºPÒ€Òt 4™¥ûÆÆ†ðéÍ›7gk ÉÊ2ÝIq¤û¬å0Ò}Öò˜é>kùÌ Àt7μëŽlŸ”×~êûÈ®±;¤}ÝÜx«o£È2ÝœaìLb£´!CŒPwNÛÊ2Ý}YèüT˜dÄt÷E»1,Ì Àtw²»ÚÆG¾ò•{æûµ˜L¡Àt×:V߽ߞvb0‘òÓ]Ø+Þcˆ±+^™îQm` ¢ÒÝAègÛ‡ÌåËçÐiŽôsV`f¥¥{uwW®(58³¹û‘ój7!ÝcÛ@´Æ•Yº€ Ò€Òt 4¤;¥!Ý( é@iHwJCºPÒ€Òt 4¤;¥!Ý( é@iHwJCºPÒ€Òt 4¤û2666nÞ¼™rá“¶p ó4¸®¥yaÔÕ×|jŒ ·<»/@ H÷e¡ÁBÆGò[cH“ýÎ’…@@º/#ýì\ª…úzço¡>Ý++¿í Û¤;€Ñ­=Ý777?õ©OÍY/é>¼ÞÄÓ½º;¿ƒŸú¶Òÿ©HЪÓýÉ'Ÿ|î¹çnݺuâĉsçÎííí'iW¸ÕÛ=*gïÊyt¶;ÐÞsëߨ¢[»]©sÂ`k}eÚ»Ž… {, »Rç¡ëî@»=ƘúÛí”Û,V¯âÓÝîÙ“îF´Þt?888þ|íÍÛ:à÷÷÷ƒ=x_˜ùvÃ:×ïF Ó6/4t¶Ð×Zg™¾–;'ŒZQK@0;ªÁš¥¬Ñ7•³IÆGÎÍÒÀXÖ›îuOýâÅ‹·oßnÞÖËáÂ… »»»òTQ)ÞýHEúœoåbTŽœ!‹Bž}‚:ßö«TدmrÕ¾b•‹‚Ö›î½û»]KRé®ižoQ(gMn=¹Ðò\ÒÝù)é`,ëM÷êîãîgÏž½|ùrp’úîκœCé»YšÑïO<݃ _“²RðYuºWwΙ?::ÚÚÚÚÞÞÖŒ›—¾5xå?¬ÜëA®Ô÷ÚÙ˜`(ßtˆ]ª•¢ïÞ£1¾¥§Ÿ$8¦>ûIw£X{ºÇÒ'GóÂ7PØÿ,¬Ö§ë»Û ¶“U!¸(„YsNÒÎB;­sTþS+ÝÖ€Í9¹rß„Æø±û$œ•€é¾v„”‡t_;ÒÊCºPÒ€Òt 4¤;¥!Ý( é.Š€Òî™=‰‡8ÅfÁ¸e²á`H÷Ìî –¹ Ý3“Tø-˜î㎅!Ývxxx||¼¹¹|´|ƒtŸb4( 龤ëׯŸ>}úÚµk>øàÓO?œD~@jóˆ9¡4cˆó9oÆÂ]í í™jÇÑ<›®;­¦…Î…ã_ζ€,î‹988xæ™g®^½Ú¼}ì±Çžxâ‰`¾÷ãÕ{‡½æYì±Z*­\OtU6C.°ê„}Ô6 O[Ò}1{{{õßÝÝ]ç[;Ÿä>÷ˆ]yag.jjôM+×+Œ&Ï…ÜMóäª ¤ûbê¾û¥K—®\¹Ò¼Õ÷Ý«»÷`{ªÁt&™oyåÚùïKw_Ã|•*ëuN%·Ðn°o|c r. )¤û’vvv^{íµ‡z襗^ºï¾ûö÷÷ƒ“Ø*w%£ºÑÎñ{ï™ïwâ›æˆ€¯Ê¢öÐS#Ò}a‡‡‡GGG[[[ÛÛÛšñûÝ­-wâåèËå õú–ƒ³…vƒc[«_zÒ=3š]ÍQÇ׃Ut ¿s_Ã4å;‡w?5fßÙ ÿ9óÊÖjæÒAº—®'¬ é^2{‡6` HwJCºPÒ€Òt 4¤;¥!ÝW„+Ý`%H÷u Þ¥®ûÖwÇ:ç±÷¿ ~|âËÔ­²Ë—«ž§aΧڴåi’l¢å ©N?•³œ¨yeówºoÐ#ÝW'x“yå[cˆpçœàMuœ#ôxœÌ¸­Š­zΆiªˆm’¾¢—ƒ²F}SƒåŒ5²¾œ±¾ é¾:út¯¬@ ö°{§»}'y_ºÏÜ*aàR Z8¤IúZ‚ÅÎP©R é^±@€X¤ûÂ777ƒOv‘oU ¤à§¾õšfG´>Ýgh•f-µ¸„Fi’² {øè•öžJßþá#Ç–3p±H÷%íìì\¿~ýôéÓ×®]{ðÁŸ~úiÍTÖÏ|#øF+ÝíUütw¸T«úí=ž¡ar†4I?šq†Tª™Êù_`ïñm…È“;GÐÏÈ ˆEº/æààà™gž¹zõjóö±Ç{â‰'‚=ø¨]|QkŸ`ŠÓÄI÷)ZåœÄ(©Å%7²w“”U(ÇRip*}fË?-ß먑}±H÷ÅìííÕwwwo4kóIÓ]S—>¡•å,Ò*Í"Zjq \Cß½ÇGò·fL®,HºÓwÇ H÷ÅÔ}÷K—.]¹r¥y«é»Û[ý•ÿTó*´–q–µVr~:0Ý+k%»H«¦H÷Q&7rH“”UØÃG¯TžJø/p.êî]ôÁÉ•åOôŒ…t_ÒÎÎÎk¯½öÐC½ôÒK÷Ýwßþþ¾<¾~¥ß¼ˆê¸;?ê·×t˜„zƒé¾H«4UÏÓ0¡…Cš¤¯%X줕öûë7é4¿ŠØ_ÎðÄ"Ývxxxtt´µµµ½½­ß„ÂòäQ…ë×YÃst£sUØR­rÖUù»ÏÙ0M±MÒW4ä­²Ò¨€ŒÝ N.ÌBtë[¢îùéî9tv»#Ë`¡äʵÏß?vïk@°›»H«ìòí ^°aöTã6É®KYì(•ÊSùÚ£ßÜ N.ÏïtÿGÀXH÷¢$Þ-Hª1­4[UÍÞ°E–C² _c›±B¤{iä~ÂÒ\'¦ÙªŠhO’qhHé@iHwJCºPÒ€Òt 4¤;¥!ÝG0Êe»±…oŸ‰¥ ù"¢nç7Q€téŽ.ÒÀâH÷T _}³BOé`qkO÷ÃÃÃãããÍÍMùÁê3È7Ýí§bTë¾['é`q«N÷'Ÿ|ò¹çž»uëÖ‰'Î;···œD~´Tûh¯ªóô¶ÊõP¬ ëìÎçSiž2é¬Ý×¼êíÙsai7``fè[(·Á×xa Q³»µÎ{ÅÁFúžAg?/.ª+´Þt?888þ|íÍÛ:à÷÷÷å¼ð4I_8ÙGÄ0Ÿ ëh¹v{¸³1ζ9 ‘£¤l¡Poð+ð lß ³o4UØðø £ Ô”`Ö›îuOýâÅ‹·oßnÞÖËáÂ… »»»Â$Á$Ðd’œOúýíÁ³ê”gÛÉù=zºkZèk†~¯µ0¾f.„åÐû+V6²Ç·µÕ`%Ö›î=úî•nϼ1PxmOÛ-ß®ÈhIït7j˜î=Ú,·°Gºûàû¾‚í ìîÎ2{¤{T9Öi½é^Ý}ÜýìÙ³—/_Žš\Î×±úîýj× ¶­;p”´˜­ï®©=v ýÑÀ¾»0²~x°ë´êt¯îœ3tt´µµµ½½9¸ÂU®ú+õqwû­\»\²¾1vEc¥»¦…v#•ÛFr›£ö`k*êýûÚ³Ü=X€uZ{ºÇòíéí»o„Î#³k±[âÜ,K6Ê·SaHç8HßB£‘ÎáÆG¾¯F9ÐÙZç´½¿â`{œ!m-À ‘îsËqµ;nº/ #Ýç–W»7¯y€|‘t 4¤;¥!Ý( é@iHwJCºPÒ½åÍÎÊã»\ðÓ»Ÿ}"Ü~ÎA¾JžkèÀFºG[íã:äg«TÖ-Q…¥\†Ê»¦sCup"ÝãÏYY¤=s’Ó]ˆçàòR%Ý ÖÚÓýðððøøxss3ød÷ÆJ‚Ü)ö3QùüÔ·}°æo|VîÝ统;wnooO3•Ü•”ŸµÚ>ì«;¼ßcЄ”b_öTS¤»ó‘k¤;(­7ÝΟ?_G{ó¶øýý}e¾ e§ñvÈpyä¨|Í6æËÉ“öÝ‚s* ¬7ÝëžúÅ‹o߾ݼ­—Ã… vwwcË ÆáP –£(ï×LÕ`å´Q}wÍ@@c½éÞ¯ï•R½ÓÝh÷q…=ó˦{pÿ²Uö§¤;(­7Ý«»»Ÿ={öòåËÁIæIwM\ -™9Ý£÷ê÷Ë~»pÒl«N÷êÎ9óGGG[[[ÛÛÛÊIäSçŒ.õðã—=Ž»ËYØã¸»1³Î1å¢ôÙOº€ÆÚÓ½çrã#;„œ“D ×4ZÒÌB焚i}ón4ÒY²Q”~S€té@iHwJCºPÒ€Òt 4¤;¥!Ý( é@iH÷,)o–§>n«ä[Õv9o,ïûÈ.ľU_[ú§)øP€yfg”Z4ßûè•åõ{ú!Ýó£¿Ñ}¿[ÍÕ0ù#ß=ùí·Îôs—ݪ¼°tú:2š5 q¤{fz<¤N>CÛz4ÛÒo®å&¥c¢§¦@þâìH÷ ›Œ…t_ØáááñññææfðÑò•buŸcºk2,¶Ã'—œÒ]žVå«°ÙÁXH÷%íìì\¿~ýôéÓ×®]{ðÁŸ~úiyü¨ç·V½Ò]xâ\“—ÆGšÃÞ¾õéÞV¤L;}º÷xÂÞÆÝÏöÆ©bžï×-³ûÂY”sùËsZ‰?gÛäføÚ/ÔØ(Ì»0ËöšäûB•?Œ¨ïZÇ×ßRšdŒì[þ¾J±*¤ûbžy晫W¯6o{ì±'žxBîÁG¥»±Òq—˦²ãS(ÖÙra·¼0³Bèj&—Çq6IX¤•bñ:·3”oEiÚ5¿Á…l†P»ð½ûÞúêj†ø¾y+VÿCu¶Dó];ljý‡’Ûã9ê-Öƒt_ÌÞÞ^ýwww×ùÖ)ª3êL_{¸²º¨`ZÕê ʶùš§,_¹!¢yí«EŸîÂð`pgM^J±‘é+ÄöÁ˜ ŸÜÙò¨ï:v['ªIòüÆnäa%H÷ÅÔ}÷K—.]¹r¥y;¤ï>ÖðÊZ˜î±×Y¾u·¯y=ê*ŠMwß‚•'‘ÓÝn|t7ÚÖ»CÜcM(<Ø»(a9/˜îQÿPBcì¤;d¤û’vvv^{íµ‡z襗^ºï¾ûö÷÷ƒ“ôë£(W%ÂhÓ¥»óÓ¨°¾Ùš&M‘îÊ SΗfÛE3ËÊ¥*/ÞæE¿ï=ؤ±~ÀK¥{ì?”¦1ú¦båH÷…mmmmoo+'Q®2”ñã+yC<.hÌFäqwãS£.»¨s5¿•g¥Ü¯¯f|$/(¹K-dFlh9›\ÊÈ Ö¨A_WÔRÿCu–?JºË?ò! *jÛëAºgiõ+ÕøH9ÜW²½®Èß.V³>•[èœkMä†Ùeú£_³Û“ËítF‘P”]¬oßüv›!üêíe,ðŰÐc^*ÅrVþ°-¾µç«Wþr5óÛýHøU7ÚPÒS +ÍÚµ$eK5»Ç–(…tÆ´øÚy¶húî‰[ðËòíÈq1"M¤;¥!Ý( é@iHwJCºPÒ€Òtω}'¯ÊóÐhå éªÐM¯f3Ê•¾‹\.U)4˜éž“QÒ]sÃêùCˆt€‘î9Q¦{l!ò-¾'RLÎ3#JBº/ìðððøøxssS~²{KxD¿˜!Ý*fF”„t_ÒÎÎÎõë×OŸ>}íÚµ|ðé§ŸN¢I÷àCÆœ*‡ËÅÊV3îLÞSÓæàÍ凞ùæÎ~¾–Qˆ0/ÊŽ9'~5ö„Â#ÅŒ'­9‹µ ~rùlšY0'Ò}1Ï<óÌÕ«W›·=öØO<ìÁǦ{%>£½ŠŒ|ç„ÎÞ¿ï!˜¾*‚mÖ„¨ó´ƒàã:œ5êçÅ·Ø š¯FžÐW©¯=š×ÊÉ‹tæ=¢î‹ÙÛÛ«ÿîîî:ßú+Öà¾ú`Î £ #8›$4C(D9<*݃sat[cç¥w¥QÑ»¸ä1c'Wþr¤ƒt_LÝw¿téÒ•+Wš·Q}÷!é.¯Ð5«ìÄÓ½Òí1v.´~é^Y»è}ÒOw»ja‘j–3€EîKÚÙÙyíµ×zè¡—^zé¾ûîÛßßN2iºÇv@…b…f…Ã+Ï~¡Ly¦FLwa6}™?Ý«Ð~xå,øF£+$…t_ØáááÑÑÑÖÖÖöö¶r’ີ_ºë×ÎñÇÝÇí» îœÐ´!j^¢6†¤{Zæ±-N^‰‹´ß–€yîù˜î•b/«ñ‘³.gOÚ7Ð×{®\§›Ûìk›<ƒÂø¾·šy‰ ¹}÷`ç[nywy·Š1ZÔ@) Ýmþ^Z0S‹7p~Y€ÀÚH0húî¾ÌW¾µ!Ý( é@iHwJCºPÒ€Òt 4¤{ù¦å±SM$x?v@ÙH÷8é§;÷ îqúe'é˜ÓÚÓýðððøøxss3ø`õéHߪÓýÉ'Ÿ|î¹çnݺuâĉsçÎííí'Ñ<ªË¾¡·ðP5Má•çPºïùiÝ15-4>²Ûà,-ø\²àìÆ²Þt?888þ|íÍÛ:à÷÷÷ƒ=øàc¶}ObuN[¸ýºyÑã!寄¾\×<±TùèñàìÆ²Þt¯{ê/^¼}ûvó¶^.\ØÝÝ•§ &VÔsÖ5… Ïî”ô5¡f›@3¾Ðl_<Œn½éÞ»ï>ºÛc.’î¾GˆÆ¦»ovcYoºWww?{öìåË—ƒ“(÷67/äüS.tmçLwyÏü¾;`t«N÷êÎ9óGGG[[[ÛÛÛšñ£úîÁ©ìѪ¾°…ë÷·G¥»|Al³É{ÝÚÓ=–&Ý»ã÷Ø3ï;WÎ(ÓWŽ]—³´!{æ®yð„;åìÆBºià)côb£ ÝGæ;õL9-éŽt 4¤;¥!Ý( é@iHwJCºPÒããÒ>–€ẻt“ÔÊy:ߘ³5[sK¹=©-䨯8o¨|¦Nð>ƒÂðÞêH%õÍe#Ý㤳zÒ<§µý´šñ†¯šl ^" ¹ŠÿÆ{?@øHÿ¼€êH£Hç?(éžÍêOß>hn\š»ÜW«I÷àóôOÒó}Ê*z8:Ò˜ÇÚÓýðððøøxss3ød÷ÙŒ˜îrÇq Êjlä,hÈ“4ÑÛ}|ˆ_é>OVîÝ统;wnoo/8‰‘—vß4øT4ßn[»®¨=º½Ó=ªÁÂøšfC¢ÒÝXVÆrS6Ró-_«ÑæÝ7û§úÝø‹§»°L¢þ„*Œh½é~pppþüù:Ú›·uÀïïï{ðÎŒ©<ëJçÊÚ™ò Zh†rˆr°¦ÁQ}S¹m±éÞ¼6là·`äL\åvŒæ#¹©Ý–Jwß2q¾þô‹À@ëM÷º§~ñâÅÛ·o7oëåpáÂ…ÝÝ]y*ßúWÓé â_h†oHïÜ 6,6Ý++6ô‹%¶‘úoAߟÖÔ"÷–rºWžoM.6X€q­7݇÷Ý}д‡ØM—î¾Jí¢” î½Êž(Ý£9<Ý+×²Šª(¨˜t¯üËŠhæ±Þt¯î>î~öìÙË—/'QFBw`ìÞÚQÒ½G™ÁO‘îÎJûõÝc‡÷H÷E-´’ÒÝ79éÌcÕé^Ý9gþèèhkkk{{[3¾ Ê•uT'5ØÍ$±Q4ÊŒµO÷TæÓFäq÷¨Å¬:êõ(…h~'Ž»íÀlÖžî±4Ò4F°‡ +ÓJÜ{o”µvö¢ipìŒø>íÑ]ö ì×ø`·Í6»[rÆp{‰ e:[«™‹¨ …á¾qœgö óE´s"Ý1kíF_hs~ ìiÒGºsË:Ú+ÒÈé@iHwJCºPÒ€Òt 4¤;Æ%U,ˈtOZuœ|߱؛»MMsŸóØá-¨ß÷u/¼ÑïC7ÅíF‘Ô7 d‡tOZì N¾ÍHìÙg`W]yâ$xËÞŒø}Ísù)nJ?"ètOZÔÚ­÷íÜ—Jwg´W¤»§(ߨáÓ8:Òèmíé~xxx||¼¹¹|²û"ÆMweáK¥»o`lä,hà÷%ºÂtŸ§  H«N÷îóÝÏ;···œ$xÜZs0»Ûi®:OвGÀ¥ÏEa^bÓ=jö…ñ{ÌETºËKXÙHßwj”Ü-ê zÁé.,ßï¹vþ  HëM÷ƒƒƒóçÏ×ÑÞ¼­~_îÁkölkvwÓÚNöhró„ñ};´åÝQ³/ŒlÛ(éÞ¼6¬ÇLuK¶? ~ûš%Pé6†ÒLwß2q¾µ‹UþNÈÖ›îuOýâÅ‹·oßnÞÖËáÂ… »»»Â$CzÌúØŠ+ÝåO£†G…n¿Z¢¶¢œ…Ä6RÙá­&“Jí»ËÛµöGý~¥dëM÷}÷ʵ ±;DþhñtWF‘ÜyÛz¯²'J÷¨FO÷ʵ¬äŠ”ÑkÉ=Ý+ÿ²"ÚÞÖ›îÕÝÇÝÏž={ùòå¨É£ú)ô݇§»]xì€à§ò\Ø•öë»Çï‘î±­9Ý}““î@o«N÷êÎ9óGGG[[[ÛÛÛÁ‘£ÒÑîÆ ë¬±rW^3¬%vråp¹çLŦûÀF*ói#ò¸»²%ò¢è÷zº…eâ|ko4('dkO÷XÂ.D{ø†xæ°¦ä+¾Ð eúö …+'×Ì”]E¿=r!Cìï¶£iµ­ûµúZâüQùš>zöµã87ÎŒì'ÚHwÌŠµv£/´9¿ö´‹ ݤeíé,„t 4¤;¥!Ý( é@iHwJCºPÒ€Òt 4¤;¥!Ý( é@iHwJCºPÒ€Òt 4¤;¥!Ý( é@iHwJCºPÒ€Òt 4¤;¥!Ý( é@iHwJCºPšüÒýÙgÿôñÇÏ~ë[Ïö³ð÷á‡?úƒ\ÿð‡ßÏß4ÿ¾ùæ[Kÿä!yãŸ.þ#á¯ïï‹/~7…Õì(›èYú÷!¿t²üÒýÛß~þܹG–n`E²‹žüÒÈòK÷ì6 ¹Ë.zòKw@jž|òɿťK—–m ù¥{vPP¶:ÚÛPï¾NVFf=ù¥û?=yò——nàŸØI™~À÷havÑ“_º¿øâ_?üðÇ–nàŸ(Ó½Ýu_½½÷ÞÒNÛ|Ô­}-*Ø W¶Ä]ôä—îÙm@@Á4én éF²1Â\Ø-Çw(Á×­Ï.zòK÷ì6  `=ÒÝÒ cgîÊ©,§»=¼Ç‰ÙEO~éžÝ,t7(ÓݘܗôÙEO~éžÝlô=ó£ôÝ…ªít—ßÈ.zòK÷ì6  lÁÝó§{ï=ów_LvPP¼àÝl¢Î™÷½~*XÝúrK ÙEO~éžÝ wÙEO~éžÝ wÙEO~éžÝ wÙEO~éžÝ wÙEO~éžÝ wÙEO~éžÝ wÙEO~éžÝ wÙEO~éþƒüŸøŸ-Ý ÀŠd=¤;ÙEé@@vÑCº]ôî“xê©§–n€A¾ô¥/-݄Ŝ9sæ…^XºiÉ"zºH÷IÔéþgögK·@OøÕ‹yð_#|ÿýÏÙ3³oÿ—ÿ^¯¾HwCÑÓEºO¢Iwþ=€LÕáMº/Ý´d=]¤û$Hw k¤;«/CÑÓEºO‚t²Fº³ú2d=]¤û$Hw k¤;«/CÑÓµÒtßØØ¸yóæ(íq"ݬ‘ ¤ûäæL÷Þ¤;µRÓýÔ?ÿ—¯ýyÒ݉tŸ}wS#ÝY}H÷É _Ä#Fû<ðꫯÚÃIw k£¤{¥Í‹n Ú›Äm†7»¯5#DkLh#ÝH÷ÉqÞíë6Å»ÛáÆ˜íÛöS{´n™ÒÈÚðtïö’Û×¾•+¶£FkéCº;‘î““q“ÄÎx6^;S£Uê~?édmÜtw”³yøÁ>¤»é>¹7~zòä/ #Ø}÷n$ám ¬:¡.&#ݬe—îÝZH÷)£'5ù¥û‹/þõÃÌù‘¯Ÿ­OwáEEº«‘]º÷¨Ë‡tw¢'Mù¥»°%wÇ«»wËÇ4F«Hw`5f>îΞùôÑwŸ\°ïÞržR7înyΙŠ4ÿ9óÂëàúbí“ím¤»}÷Ée±EºY+õzw ÒÝ)‹èéÊ/ݳ؀"ݬ‘ YDOW~éžÅédtgõeÈ"zºòK÷,6 Hw k¤;«/CÑÓ•_ºg±U§û¹ÇþÕҭПœîõÿøl-™énË"zºòK÷ì6 æÌ™3K7ar¤»!»èÉ/ݳۀä.»èÉ/ݳۀä.»èÉ/ݳۀä.»èÉ/ݳۀä.»èÉ/ݳۀä.»èÉ/Ý¿ýíçÏ{déVV$»èÉ/Ý€,¿tÏn »ì¢'¿t²üÒ=» (@žüÒÈòK÷gŸýÓÇ?û­o=ÿÙÏ>RÀ߇þè~pýÃ~?Óüûæ›o-ý“‡ä7~ºø„¿¾¿/¾øÝV³£üm¢géß{„üÒÈHwJCºPÒ€Òt 4¤;¥!Ý( é@iHwJCºPÒ€Òt 4¤;¥!Ý( é@iHwJCºPÒ€Òt 4¤;¥!Ý( é@iHwJCºPÒ€Òt 4¤;¥!Ý( é@iHwJCºPÒ€Òt 4¤;¥!Ý( é@iHwJCºPÒ€Òt 4¤;¥!Ý( é@iHwJóÿ]V3 endstream endobj 180 0 obj <> endobj 184 0 obj <> endobj 185 0 obj <> endobj 191 0 obj <> endobj 192 0 obj <> endobj 190 0 obj <>/Length 18360>>stream xœíÝ{°_eaèýÕ‰‰!R6‚Á L5ÇÀ Eá5…é%L:Dœ££ÃØv(¶§ƒbÏ´Uœ÷”ŠS{Æáœv”ц–1žöÄ*ÁF¹M¨Ä€ÀF0.Ù1&ÛÄî43ï:üf6é¾üö³î—çóùát“½òû­Ûw­g=ëWN¿ñ£ Ü÷Ñ›^„ÌÎø«çÕóáÔö×I|ÝP.nVI×¶¬>dHÕºõ Ãp# m¸ôºóæ7½ qÛ·çâ›?9qð@Ó Ò°_À¹uñð™ô1k>'èè÷ž8y¢}lMµÀÓutõëÜç ³¹æœ®þµóš^Šè|þž[ÖmÛÔôR4LgÖÑCæ@/[±þ³ë×Ýí¨‹‘ž‘•´tdñºÕ×6½ÑÛ·ç¯ýQÓKÑ0œAw”} ÅFά O§·în8x6^!“Î~ì0pýW®\rjÓK?¼íK·o é¥h’ÒõdÒëJlððoÅ€p]ß^:½±àᬜЈÓß°ôÆ‹>ÑôRDgtlÇÚõŸiz)š$€‡éúqRS‡ÆÞpÒ—•ÄÉ•êÁfÒõmDÏÉZ X·úÚ¥#‹›^Šèüþ·ÿj󎇚^ŠÆà™õà@8C6~È·¶ÀŒlí!€CXc¡~+—œzýW6½ѹ}ûxÛ—š^ŠÆàÿ 7¿Æ‘pÒ»5'iǧJwõi‹èǶ €Yu¡~/û‹‘…‹š^Šè¬]ÿ™Ñ±M/E3p¯Žv‡jÑ/žN¬Hл­ O+¿g5†š­]~þÕg­iz)¢³atóŸÞ~SÓKÑŒx¸gG¸)ZrÀ‹*€+ëßÊß³^ge•†Ú,˜7Ã¥×¹ \³‰ƒ.¾ù“cûö4½ ˆ.€ûwH›®=¹Øx ÷ëXÛ>pšÕ˾+¹ÎÁº µùÝÿô[>õÿmz)¢³nÛ¦ÏßsKÓKÑ€þp/`³iÛ-ÎN¬uô]¿×ð^®Ò8«:Ôcdᢠ—^·`Þü¦$.{'ö]øÕÿ2qð@Ó R·p¿WC´ðHm'ÖCú¨÷ku×^œ[ïWû¤¿ßÝrõYkÖ.?¿é¥ˆÎçï¹eݶMM/EÝ:À1™B´óèsX?“vA„ˆd5î÷Š*€ ²@Õ–Ž,^·úÚ¦—":cûö\|ó'c» Ü™ŽäØ“C›W8±êήå_\äb[o{¿6 àâlPµë/¸rå’S›^Šè|rÓßÜöø–¦—¢V- àØ-¥hùñIX·shÿ×ÚK®«‘¬i¸,¶¨Îò£ßôåßü㦗":£c;Ö®ÿLÓKQ+Üaí?& àCYÃ3éÊ×ÚÑ®œñ¬f¸D¶¨ÎºÕ×.YÜôRDçoûÒíÛhz)ê#€;©+!<…•<\‡¾ÖˆpÍŒmÀ¥³Õ@V.9õú ®lz)¢“ÖoÚÀM/E}p÷tè$€§³žêÖ×Úu±­–®]¸ 6¨Â××üé’#^ßôRDgíúÏŒŽíhz)j"€»¤sÇ<kûœºøµvZ$ëd´ë•®H$Î@œ_1õ[»üü«ÏZÓôRDç¶Ç·|rÓß4½5ÀÑÅ ?\G¿ÖîŠa…Œy¥À•²ù@‰Ì›¿áÒëF.jzAâ2qðÀÅ7rlßž¦¤¸º{ÔÀs²ÚϦÓ_kõ{U´: àªõ{ J¢ÿ~©ÙGO¿ø£§_ÔôRDgݶMŸ¿ç–¦—¢¸í:}ÈÀ¬üÓõàkí–¯„Ö¥D×Åv¥Y¸hÃ¥×-˜7¿é‰ËÄÁ~õ¿ìØ×ô‚TN·W68œõŠ~|­ÒË5ÐZ4Iצ—›Râû¥vWŸµfíòó›^Šè|þž[ÖmÛÔôRTN·T?Ž48+[Á¤>}­]ѳÕÏ*t(\3[ô†ÃGþ÷¥×5½ÑÛ·çâ›?9qð@Ó R-Ü:}:Ìàl=ûZ;¡7ëž•g:ÜÛqýW®\rjÓK?½ý¦ £››^Šj àéßFçfsèå×Úr=Xë¬6³À ²eA>KG¯[}mÓKѱkצ饨–n…¾ZpAÑn=þNÛ¬Óë›uf8Ü8ÛäðåßüãåG¿©é¥ˆÎÞö¥Û·?ÐôRT¨EœtüðO¿*¸¶ jÓÅ•ÍÚB·A·¯Ä7K£V.9õú ®lz)¢³yÇC¿ÿí¿jz)*$€›ÔûƒŠ.QT[G$ßi un5³ªÀía+ƒLÖ­¾véÈ⦗":k×ftlGÓKQÜŒH'¸\6ªÖ¡uÌJ’‰nÛZ»üü«ÏZÓôRDçöíüám_jz)ª"€ëÕDWÁfBu:±vY=rÀíd‹ƒ9-˜7Ã¥×,\Ôô‚Dç¯ýÑØ¾=M/E%p}"<„àJõx{‰ö;mƒ6¯WVŒÜp›Ùè`87±nÛ¦ÏßsKÓKQ \¹˜¸¶ÊÕÎ5Ê*Qn¹vnw‰¯•vY¸hÃ¥×-˜7¿é‰ËÄÁßüÉ^ÞÀrØÀµ±íP–¶­KV†Rà®°ÂŒ®>kÍÚåç7½Ñéëûp%0pÍlAמµÈjP"Ü--Ù }§´ÇÈÂE/û‹¦—""F7ßôÀÆí»w6½ •Àer¨˜B7ŦDnmXy¬¥À]ÔøÆè;¥U®¿àÊ•KNmz)znïľ ÞsÓÖ[{9òy’.‡ƒÄŒpã:ºMùN›ÕÔjã{¯Žî®wã¾SZeéÈâu«¯mz)z+-Þ´{ÓúM¸ée©œ.ıa8ܶ,2©…ñWM÷€ n¼è§¿aiÓKÑ7£c;nÞöÝ £››^úà< à¶±‰¢ÎõÄw]ܵmž¾PZhå’S¯¿àʦ—¢?îvô¦­·nÞñPÓ R7œƒAV¸µÚ¼­ùB'–úÇwÚ3¾P¢µnõµKG7½·atóÍÛ¾;:¶£éiF»8iåy¹c@n¸ýlq̨ºÃ÷Û½ÔW6UbsñÒ³ÿëÊÿÜôRtÕÄÁ_ÿÑÿà¦g÷Ž5½,MÀ³²ë/NwˆMCU±>øf$€ûÍK<Ì›¿áÒëF.jzA:flßž›Ü´þGwÆ0ÇÕœð`w_.ÜQ6CJ\|¡m €#QÖíÛ¤ÍÖ.?ÿê³Ö4½±}÷Λظññ-4½,m{ÛÅWJ÷€yGãTð{÷%¶ŽM˜;|ÁÂúo æÍozAÚîþgGo~pÓíÛhzAZ'ƶ[¯î[h$ò}Ѿ»Ö²åF˶L/]}ÖšµËÏoz)Úë¶Ç·Ü¼í»Û^x¢éi©(Ø~¼)¸ß*ú~}¡-þýúÊÚO“ب鑑…‹6\z›ÀSL<°áÑ{nÚzkäs\Í©WlÝ68N†ÞõÜߣoªC0‡¾>ø*é„ë/¸rå’S›^жÛ·gýÃw®øŽôš^–è^Û/wˆfŠUÂÚ3~Y¾ŽÀÌÈfNw-Y¼nõµM/EóžÝ;vÓÖ[7»wìïÜôõÝiŽ«^À@ýïK¯{Ãá#á??:¶ã+l¼íñ-Õ-À@­]~þÕg­ ùÉÍ;ºië­æ¸ŠzhÁ¼ù.½ndá¢!?³atóMlܾ{gmKE³0ÐO=ýâž~Ñô¿wb߆Gï¹ië­æ¸Šúidᢠ—^·`ÞüÉ“oÚ½iý¦ Üà‚Ñ ôÖÕg­Y»üüä¥9®nÞöÝ £››^"š$€€ÞZrÄë¯9çƒ7m½u󎇚^š'€ˆ‚ €(`¢ €ˆ‚ €(`¢ €æª—¼fþ«æü±_øå¾ÿõ– 7 À0·}è/Ž|õ¢9l×þ=|õjX€Ü0Ã` 7p¨O{ÙàŽZøÚ#^uØàŸ[tÔà[ðêóæ§ÿ0>±åW®jd :]~Úª·}âlüïM/Hå0Ð8HZ¿¿µìÜÀ>ã|¬Ò…@_þ?^~Ì›B~ÒJ›Õ—~ýÎ<î­é?Üõä{ßÀ˜~\“·=ÿÄGþéÿ«ayh„"€é"\…toðþ7ŸqØ‚WOþ›¯ýð;ýžüIÓ€DÀt‘.×êeç­þµw/9~Ê¿Ÿ8xà/7ß²þá;Yª`úAà@—Ÿ¶êÊ3#ð‡?w×ßõøT˜À%úì{¯¸ð-gÎöÿÝ9¾ë¢u×Ô¹ÍÑM8³ÕËÎ;ç„å'?ç†Ó¥ÝøØØÓw?µmÎ= Y½_·ÓãÝ;]úÆ#ŽÉú¾‚–ì·o¸ðãùV¤'w?ÿÈ‹O}ê»SÅR‘‰æe1WP/ à@¡7þxË”ãVH<Ï9–®ßô7<§tG½ê¤9¢kº4`¾õØ÷[ruŠ–pï:~ÙœÏ Í)ý+|sôž¦®D¤§U'µ¸àßbâà{Ÿ~¸å×SzOó²È+¨p¸S‡oçÿGÇžÞôÄýÃÏ–pý¬ðqÀ±Y½ì¼µËßWJúªÜÚþì{¯xω§OßCM¿]µ~ü-˜$€y™“žÀáBO:›/|Ë™S~,Óõ]\33`EKGå† ?~îßQÝŸ2ÅCBæe¬yÝ^½ì¼œöC…Cìßõå|»†›¨éßbõ¯½{éÈñUüáéñèÆû6¸\?ÌËTPÏàLB>®ÛéÐÓŽô|ÏŽ‡2ÍØ!€k&o¢%€#QÑßéZU/!§Óu®ÛW­¸dÍÛV–{ËtŠñ‰ý_Ýz[¥·âÓué÷ÞõÛÓçè*ÑÄÁ·Ÿîswý]é÷áë_—_‹¢"€y™ êœCÈ)òôw!€ë4:öôÚõÞôRÐ Ü{·ø õ×oòÒ}à/ÞûÇBþÅkX·¯ZqÉeïx¥¿bº*>ÿ9_sXº‰ƒþró--Qß{˜—© žÀ9„¼Ë=‡Àur‰=f¸ßÖ­þtE3…hpv½ÀI“ê×íÕËλæÜV÷ç1}pVõßÄØ9¾ë¢u×Ôÿ{#$€yY³”†Ç_{Ì ¯=æßp‹¦8Ÿõkþløì)ãûW~åªÚ–‡áæü¾&ÿ9Ücu>t:›¦æ…ßV½nnb)ë­BáªàÝHõh>€;$üF×lJ£÷ä‘Å‹_{ô”q&z¬8Ÿ![úàýFSÞ„Dƒ2Ýp6‘À}8…a Ê}@&D¦Z«tÝÌYѨ”‡Ã/(TÁ…Úzà 2TT³I?Æ·yÜq‹Žr"b˜b)pnÓŸ§ÚµÏ–gu]¶mÂWò¤‰sÓ¤§¿«NZñÔÏŸoóu™t·ø“™^!–•î«Lû)vŽïúé/vÿx×3ƒÿ3=?YôÊ×äîŸrâ†Èô¨juëvƒƒŸUü/Øìíß¶½bº—p¸ ðý£¿8·CŸÿIÓ÷Ží[+=/'·C_Â<\ý'¦W­¸¤Ò yéõ¢¥gMnãm¾nþ¬S¥û"ÜKùÒkÎá<éQà´cOÊ1¥VÚ²>ªZݺûDz„}|׳Ží8t÷•žo¿íè—qLŽ»ú?ÿÜ·Ç'ö§+ÕÞÛÿÊWÌ_0ïéš“{H‚›À5ÀàâÏVMP \DzÊž&Ó]Oý°µQA¦“¿é/p®Nº`ï:~Yº¯«îTxúÖÝæD0ÕÉ1_Qºo_·í_B6–]WÛTó9æ[®hÝÎw "äârŽ/·ÈçŸã/2¸’r÷SÛ¦¯NéŸvÎ ËÇ‚¬KâIઠà 2mŠbFcu\ý*…¦ßÂ_yRÛ¼e“é[Ýïò+Uÿ]î@˜êdx)ëf’£Áj8¤æ{}@EëvŽËáK’ž{쌋3}ŹŸÎúsàß"Ççã>PÕp6áGñ*ÞÊÝá;—^>­W3Le:ÿ»ëÉVýˆìl]ZâgúW~ÿ›ÏÞüí¼ož(˜L²Þ²Ëw‘(kÀT=Þ$÷xãŠÖíðGQrì3LÎý]¦÷Hgú[´ó2JÌp6á\çp» ?g­á„µ÷0=>õK¥#JÒóï÷½éôS^ÿæ!çÅŸÎM÷œï^rJ`C¶ðêaøi¥&“Lo?*ò˜@¦4ªhºü´Ug¼áäá{›áªX°¬/ Ê=D9ü+È·ÏÏt1%Çß"ëe‹v^Íì œ..ð3líX¾ÀôUnÿN™ƒjˆ‚Oçf”¥…ïá3šŠ¬[ýé¥#Çþp‘]A¦}N‘m𪗬:é]Óÿ}‘I•&U±ngÚAy*$ÓüÌ9.;f*ù|Ã<3Ý*¯íaò8 àlpq§ .NÓWá§=ý›õQ±"§2™^s2ж‰ô0ÉôpÁµ+S½äþ]ùî Tźéc)xn~V“c—~1%÷þ<Ó@è^ÊìœMø®VÏ&|ÿÕÂ|ÝþQ§ûÙª¦—œ‘i YE»â¬£þ’ãÙV/;ïg¯Ézó§UoEÀT$|Õ*>¬,ÓÝæÜÁt+€3Ý•-^tᣔsÜD ?Ã/rvZÏeæ$€³ ß<<Â:›ðGŸaA¹çÉ ƒSýj>ß"ÇÙÜçÄ9ÞwRÛÄ×!+¥êeÀýÀÅÏ(2¥iîKoÝ àL·4K9© q5S×¥‚ã3]Fq¨:8›ðvMø<$€«&€ë—iÄc¥g9nÌy³E›ßG:\ø }ÕCþòpzÂúÊyó¿õØ÷‰¶P¦eš$)’®ç®ø¡Âo¢dz¤¶SÓL£‡ &­ŽÎF—"pˆÇ€ ÀUÀ5Ë´J×p-Ç©j‘§s³¾ï4iÇùS§øÐ…Oר}n´ñÏ“I™FF”²»®¡·»Àá;¥·îÀo!ÓÇð½àg˜é2JÛfsèœMøµ.è:|7U<€ &mœMxQT}%»ÓÂïä4~êÖi¸j¸6™fœªùí9f¨*rZ“ãžs³¡Ã‡ü´*€C:d|bÿž{Ìd °R†„àCeÚõ•¸ ?· ÿ¥áfñ'™ÃØT8ÕÀÙdz¯ŒW¤ Ñ’IAûMWM×#kòÕÿèDŽm­ÈkвÞsnv tøÒV=óxsôžðéÇ{ïÓËàFÔ9 V¦Þ˽w(€3-j)3` „" ÿû†ïË÷ypš)p6ác$b[kW/;ï­Gþóïó§DéA+ïBµÝÎñ]•ž¦ ઠàdzÍFÒÜsYg¨ŸØÿÅ{¿QÛ[‘Mž(U¿»8<€_ùŠYo³ËàF„_^)Þ~™öE¹} à—á¿7üj> )¶”¨“Φ=³Y¶M¥»ì¾ªúv‡®š®ZÖÒkðÙ“L/((¥áWc'52º©SäpzøÎ:Ûö¤4ƒ}qÇ7Gïivâ±x„°/> ,¼[Šü®¬·RÇ/z]#Ù·?€ÃOÅë àð•6¶”¨“Î&|«‹mì®Î¡êóQ\5\©¬õÛøŒÇYoV'Åv™ž‹Nú|Â?“š9Þ#•Û`µ ®Z¦iáŠ<‘iöš\Ôö ŸÜ¿7i(€Ã'‚6:68›V]Ìnœƒîº¨¶ñšåxåOÞy›5J“Æåøˆêþüv wïë à\µL[A‘)…2O«Ì?….þ{/?mUà®»øQ&|Gd"Øêàlðlp¸ë¢ÚÆë”žÔþÞ»~;Ó 4É»þgzÀ!ÓëgKщ.qnÂLcmJœ|«,ÞÜ8³ð7ª“cœCÕ‰pÕ¢ÚÆk“£~[õÈIŽa‘é2ý'µ„®s’Þ95À2¸"Yë4ëúŸuTõìÓ5À™®÷5ò¤ro)2=ÚSÿºœ™ž‘Ρê5$ü ‘+]’¾ri¶t9ê7iß5þ¯êÍ}{$ýÄÂ_Ø3PÛ°ºLeRÃË«8m¤LŸg&2¸ ™¶¸ñ‰ý_Ýz[àWuTÿíߤ¹N²œ—8? ÷ü«“iþ¶-|ŸàÌj~ëzWàÚÀQ]¬¡µÒ^úÐ)d­ßš· ‘#ã‹Lv’c÷[ÏyU¦Ú5ì…Â_ƒt÷SÛV´"Ó­õLÒ¯ûŽí[Ú–%Çe K·¬__º"ÓÛò¹ÖKàÍ—ÿuàçSâ¯Î4”£©ãÍ&üKœ›UIgÖª]í!€sÀ0)Ç»m“Ëñ×)r‚˜õžs=÷©Â—ªžÉNÃxðE\~ÚªsNX^݃$2¸D9¶¸ôóè…íw?µmÊø‘4§ßyìÒ3;9ë€ù¦žØl0€™Ð8ü<O‘£üswiÖ{åî]Û9þ9©8€.?mÕª“VT—ÁéÇõ­Ç¾/ƒçTéøçCÕ6~aˆÆ8Ç žð3äô,ñcg\œ{„Eƒ§Žùê·Š$å6çŒ×/š à$ïRgþ͵<ØÚ#üfE›;-üìvâà³ÿöw«^žDÓ&EŽyý¸˜˜ãd¨È¹uÖ¼ÄgLÚ9þ9©%€Ò >ÿM§—>òv’ ¢¶°&5>8¥ñN²Œ€›4çsÔ¥ÜÃoðT<ëìIÙ“?Ošñp~2ÏìyqÆŸ_ôÊ×{ø‘³}¡3žÄ àt#ú×çFg[þã½nÈ+èò]EÀ™…_ÛÀÂӱ͇ïýk;›À´AÁóÑzÞ[ð³¥<ö™é”´¬;±Y¬sçS[T=wpÊØÚcb#šš³Ùé ÚÀ¹gvHkê¹½»žúùó/îûùä¿LƒgñkÎñŽ€éš:Ï7f»¢á3ž ι2 éé.ÓÝÝ[:aÊíß5ý:]¾…IÿüµËß7ÛGšã8"€3 ßÑà@áûÍÖ>ÜÂñω¦ DìSý̹U–8ãQàÑ*í¨»žúaY;¥L§}5l¯9€|òQ‹+zgRz¦qÇö­28Éõʱ5ØÀmà$û“ÀõhäT<_ýV7;L¾æLf_µŠìºs/L2û:–c´ÎL—.üŽAk?Òð]mãŸL£®Zqɪ“Þ•û|(ÝRnyèöÆ_°Y…ÙáUŒk>/=ç¸û©m%þƬ·k~~²‘žT髃ep¾úM«õ'?{îÙ½cûürðoÎÕ9nÑQ9ö]M5pK8ÇtÇ5¨ÿ¼1_ýVúr"Í9Û_'÷yc‘…™í“c|¨Î,|GSgêt]øP½¦Z¦)Oë™ÿy@Óˆâ¯IOYn¼oC¦W­ÂôCxutÎxº0xô.MßÒ?á¬g~5ïyš àJ38 °<÷XÏM„È‘^snté¶sΠ˳Ž`odº¾–pRãÜcáê à|³^%Å^ç¢Hsζq垣±ÈÂ$³L”ãÔZg–i?«+…Ÿ6Õ6~8\¦ÉfÊ}ÝÈp˜ú…Ø)¯†›ü”j˜ÍèÐý@z.¸å™G+ºéšõöoý©Ð†¨ôÕÁsÎ-Ô3iu|âì5áׯ'öÿã#wžH¤kõ‡N¹ ¶¹ëòiO'ÞWT‘Ú(¹ë7©þ̶`sÎx*÷¤<fÆÝxŽ[çqßGo üI](ü¤¹Î;¨!2Ýþ­y(ަNéIØûß|F¾Ãÿ¤þ=ô;›/ýúÜ÷ì£õ\KO_žÛ»«ê “²žøÖ?©a{x@—"ÓŠ·s|×—ðí¬·Ú²®Û¥¿Çu¸Vp’ý=ä9¤gSN=å"õ[Þ°ŠæÌ}6^pafÜpMpéÂï«Wú˜D™nÿÖ|r/€©G)ï>MOhþáÁïy³KGe½ýÛÈ„m àôðwî ï¨è¦YšÁ¾¸£¬iÕZ(Ó´ÏEžÑÍÔÀ5¯Bm à"5"=•zÛÑKÚÀ9† Lªç´°†æ<20åg~¼ë™ém_E{¸&¸ ýTß^®ÿ™pLÕJIß$¦»U}•uØFÞi×Î(kSšM‰³‹·JøŠWð1ˬQWçMà¶pòÒÇõ‘Ó>PÊ{Œ¦cø÷^õ_¹ÈÔ_µÝ)Øœ3N¦8å?/øàÀ…™mÐ¥Y kžjm~omÛ„¿1¼ÎÇh‡Ëô¸cýƒ·0Õ)>ÓÕÀøÄþ‡úä`ÞÜQY_¾š~é+¿rUuË3›6ð€ Î$S{47Ó¯«óˆßÂ(}N¬ÉgeøÒÈìDý&µ ®'€g\£òPpá7àpá;Êö<%~û7©ý¡ DSÁ{M³NŽ:£Ñ±§7=qÿ”¡³2¸s²ÞþmjÞþ(q›Q S¯Õ#|XrYCîÛùÊÃÖpRø}x“¦¼ ü쫺óð®ÔoR8€g¼í7僭!€gûÀó}˜8ðƒ½¾oä5ÓeºýÛÈ2 `ÊUâyùøÄþï<~ß#/>5Û¨BÜYoÿ6ø‚À®ð€ žSøp¼²ffÎô¬{m/­hs|ö½Wü?'¼=ßS²éã{?y`Ê××øHÌÕoRËkªàÙVòÜã‰p¸"{´6¼`9ÓäÏICö0e)wdæäЩ9|صÏÛ·Ú‹¶VÖ=aÒèžnð¤J_ÜÝ N›ê·œò“åž3„¬mtûx ]Îw»ôØÃ \™wŽïzlìé黋L»*No²^õ;T#;À"<ÛazÊ[0€Ócý3{^œmNüÕcgÛèrŸ] à<¸þ×ÁuZøÛøcÀëV:üéǦnY `ŠKÏ2Ï<îä²ÞÔ’n ë¶ýË`[øF$ƒ[+|î†f/_v4€*Íà.nbá;rÁákÕöÒŠ®ð¤«V\ròÈâ׾갣þßíqr«L×ÃôÓÚýËñ»ŸÚ6ÛSc™ú³ôÓ›ÎÕoR €gê8}›*Àùd!€ó?ä·d_Óá[E³W²îþšÊuLn«—wÎ ËK<áNÏl¾õؽ“ǪLOLþ ;Gï·ð[p“šÁ¡ÓdœGƒÏß÷^øg[ÿcÀY§íi|¼Ÿfº*†:O7|×WÅɺ ®MŽsÁfgm˜Ôã¨úÕÁ3ëhD³ltƒêýÕú5–ãhÕžúMJjÎáw³+}ðàzýl…|kµÎ#|_#€³ ¿·Póg›õÁÅ6Œ÷ÀLJ·¬3Þpò²×½±¬áÇÃ…lžULä#ƒ«–ã\0Ýþåæ[ÚPM½à28S78 V=CÕ¢ àš_•ã…I îLQ<€wŽïúò¾=d«¯4€† DÏñ²•æ8ý š]€½ò5géÿÑwT½¹ûùÀÒ.xqª¢Y|Ú|‚ÒZ¹_€Ùªá€HOUÏZü¶âCEšýŠ3 O-ëryø LI7!5þ pà9[Á&ӵт2ÝçOZpéY—ò×À¼¬=¾ë©t|QŽùN[5ÞO÷ÆàNï1‡Yô¤‡–»ŸÚ–žO&D)§›Udðà¦m>Si›¬'‚“Zµ3À‡*þ6²f¿â¬èâ7³Þ¬íøÆ¸ž¹KÃKÁKY|kyý&x&˜—µ§‚Âó(8ÊeˆõÛ¶ñ~¸»·yÓÜ=nÑQ•¾°w¸é¹XgTñÒ`(ß 0“öí ðt¹·¯Æ¿â¬ÏJWÖ)‘jûˆàðËd¹[1ÓÜc‹4Ó[?Z5ÏßlzÀ3~A˜üÚSAá{óІ`åx ±U/=ÀÝ’®öÇ/zÝâ×}ä«oüÞÙÞ|PÈàúå®ßÖ¤žMÖÑm8Þåx4½È CŽÍ¡¶kã~²‘oB²¬“ð›éöo 3Ñ”¢g<ãXL~­ª ÎQ¿Å/-WAwHøà®ª ½gS<ž­:é]åÞO7ÞïýäÞ®lVîúmCÍF˜Áí9Þ域oý̱9Ô¹!4À™5ë-Ó¬#Ï þÿëölsêS϶>dp!€yY«*(|Jégý2PÛ‰3À’ûI˲ìßµuçãs?š àÒg²M^Zà;¶omáV܈Üõ;>±ÿ‹÷~£µg„8DšÁ眰|È ÉÚs¼Ë÷nêt-ýêÖÛgHJ?œö;ç:ßÕx'¯á†/Fް¹ý›iXAÀ麔ïzqßžRÆW÷)€g;©O©ßJçä/…Î!ÝÄÞvô’ÁçÖÂ1Ÿ_ž–ꎟ¿ðôžŸ¦ÿî /?mÕë;ò¨…¯=iäø"Crê¼ý›´#€s¼879äóäŧÒõªàç_ÿ”õÀ}ýp·ä˜}m¸Áè²㜇hmäËàöLêÓˆô´õC§\»~»ò"D÷W{ݬyðó@K8in2‹RnH4~{:<§Ü3/ àœp Ÿí©úAÜé»ÅNŒ÷ÀÝRÊQap³÷'?{®¢ÇY[Ài¯]þ¾ÀS™®Ü½¬HÁ»g-Ÿöy Ücm¨—¦.‹·'€ Ž%É­”O¾ôkÐÅ àá2Íè>…ίÁÕF"|8M ƒŽ=¸våôHwK‘ñWUÜìQ'x`øœí¡R'êVý&¸ïš}™\ƒƒÂÚÀI#ÒËš¿°…d à!ÒçÆû6ä> àüÂ7•ÈŸ.+"ü 5|ȃ“ÅM–#€»%ëyCº÷qßžŸüì¹}n´¶=L‡x Íàßyû{f[æ˜?¼cÖ¹úMpßeúQ®f‰jU'õ>L[Öp¼V=I>IÏhâà{Ÿ~¸à÷.€ó à˜Ï± ?E«ç&én½ÎØ(HwΜ{•É—F4µKé\¤Çæw/9eÊ’G;ø¹à„ÏI7ë7ÀqÈúBÚ‚Òmá¹»Ù AÚÀIáÑ%J|­æ7À‡J·µ±}{yñ©RÎöp~¸á3[D{.;„îœé'èéÿÙ½cÏïzð…ŸÔ?·Êt àC38ÚÁÏW­¸dÍÛVyeTGë7ÀѨídKNÿM§/9☂›ÀÎñ][w>^Ãl#Y n´†üäC/lo$Ýó½¡ýPéþçÏ=VÝÂßpáÇßvô’ŠþðÜ^Ü·§ cì{IçÀަE„WœÏy Lépç”rg£Š1‡õÀ‘ûì{¯8ñW}Ãá#™^T›®ü=Ý’Yº.ý ÞzÔ á÷äo¶ÿ?Ï<á+TJç'Ìê>¶Ç9÷˜Ò à¹jÅ%¿ùÖs2ëϨ%3ý'€™”žZ¿èu¯zł׽æˆó^qèÿkï¿íßóo¿ØýËñ'þ¼îªHºwzÝÂ#ÞpøÈ¢W¾æðWþ‡}Ôàóÿñ®gvŽïrÑŠàüpm<œ¦t¸+Êzî®Ä4Nà"pmÓ̤Ò_³Ù€DáÙÔÚ„¿Ô~çø®‹Ö]Sõòt…¦t¸µJ™êy >ÏHà"pmV/;ïšs?øÃZn’¦t¸…Ò=äÚåï+ëe§Oî~~ݶé_ý&€—àüpnÿðç2ýÚ¿Ó³a{¹ `J'€Û¦¬É®ú4åÕt€D!€ë´nõ§—Žò“ž$€)nϾ÷Š÷œxj)cž“—†=ÿã#w÷ûê¡ À¾žÇ¹`q7\øñsßøŽŸôð¤ðþÒ–ò¶=Bà6øÔ¹—½{É)¥Ìó<0:öôúÝÑËaχÀ$¸ ï§­Íå§­ºòÌßùɉƒÎþÛß­zy:!<€{ö²ª#€›•î W´¢¬Ç}6þx˧¾û7%þ­%€HpA¸N›/ÿëÀÁ~îgxS¥ÀM)ñG“Æ'öuëmñì-0‰.H×iýš? ¼ïÑïy\Â…pâªapýªHß$ÊÌÀ$¸ À£i"€Ë>Ùé“»Ÿ_}ËŸT½<í—)€Ç'öçñû „f8\§ŠÒ7†ù®f$€HpA¸NW­¸ä²w¼?ä'=<)€'¥éRÅÂÄ †äÀõ¸ü´Uç¿éôÀ©ï3‰¹î0‰.(<€M2TŠð1矻ëïz?£éœò0¹ÅpKWíSç^öÎc—–;ÍÕ@ú¥üÃߋùI @"€ À5ûæÚϽþ°#C~2žyM‡À5À“pŸ}ïgwr‰/7:”¤'€—àB¼f¦fáøèØÓk×ÿyÕËÓrá/O¦x’7xÐ÷ä£ÎrŸÕÎñ]_þÁ·ˆI0/À…àš}êÜË~kÙ¹!?9>±åW®ªzyZ.üã¢x’ñÙ÷^ñÖ£N¨b´ó€™í¦À$¸ \?‡ Ÿ6ŒRàIxˆtÃüOÇ-[rÄ1Ýòð6¸é0‰.(<€PËâ¹ëLÂ?.ŠÀ“ðáotË'=Ü|sôžÈ/ÿÍHà‚‡˜: –ÅE‡L<\'¤%õäîç¿õØ÷Ms• ÀÅ àú]~Úª+ÏüÀŽ¡I­^vÞ;]úÖ£N¨bZbXÙpéwhi•ÝýÔ6é[„ ÀÅ àF„Ïlüµ~ç ßÿzÕËÓ]Ÿ:÷²¦¡?b’*€«0d:‰ñ‰ý?xî±4}½×·8 @"€‹ ½æt°\s¾E3=k|vïØó㻜8B‰pE¦ÏÖþäîçïzꇮߕHàâÂç×axdmf|œ5=ç~fÏ‹?ÞõÌ#/>%z¡ ¸:ƒÏvçø®ÇÆžþƒÿ½éÅé! @"€‹;ô€:qðÀøÄþ—þáßú‹ÝƒùìÞ±}~™Ä1<²6ƒWh¦øs{w½ð‹Ÿ=:¶Ã}¨®Îêeç¥ÿëâ]u0‰..-1éÕˆËO[e>¨YºÇ{ÍüWÍùc¿8ðK;FÚ&píÝ9¾ËÁ Ç0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA @0QÀDA…_iz €(`¢ €ˆ‚ €(`¢ €ˆ‚ €(`¢ €ˆ‚ €(`¢ €ˆ‚ €(`¢ €ˆ‚ €(`¢ €ˆ‚ €(`¢ €ˆ‚ €(`¢ €ˆÂÿèâ  endstream endobj 189 0 obj <>/Length 12693>>stream xœíÝqhÖwžàñg.Rx ”@ ´ðü¡„\ †"á™ ÉÐ’%´‡.-BNÝ-:‚’²WGA±3ÐÊî$ƒg˜ÅôXÒf;K<¦'¤¤íÐ-AJ!PôÈr a/ƒs™N«?£Ïïù}¿¿çûzýQf»6ù˜ožïïûnìóý« $à¯BEÀ$A @0IÀ$A @0IÀ$A @0IÀ$A @0IÀ$A @0IÀ$A @0IÀ$A @0IÀ$A @þªûâþÐ3@à `’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €H‚ €$`’ €i ÿµÿbŸå?½û³> Pv˜À1(`7¶P ˜À1ÀpŸ¦0@ 0Ü'€i   ÷ `HÄ@CéT[·--ß =4!L `€`(¡í»j»NÎ\Züî›Ð³@SÀ4ˆ†ÒÙß=¸¿{`emõíßO-̆š‡¦0@ 0”Ή޽ƒµžûÿ{ööç'ÿå}"r!€i   ¥snÏÁžŽÿçzý®7ðz  šƒ¦0@ 0”ÎøÐñZµãsjaöí߯¬­ šƒ¦0@ 0”ÎGûζµ´þøï/~÷ÍÉ™Kóß~]üHÐ0 $€b €¡\Z¶l}ý|Æ/x÷ÖïÞš*lh&˜À1ÀP.ímÕ«#cÙ¿fþÛ¯]’O@Ó@ Ê¥»½vqàð#™K’à `HÄ@C¹ô?÷üXß›üÅ.I‚Ç"€i   å2ÚÕwh×ðæ½K’`ó0 $€b €¡\Öëw½÷ŸrIl†¦0@ 0”ËXßýÏ=ÿÿ K’à‘0 $€b €¡\.în¯=ñ?î’$È €i   årud¬½­ZÏGpI<Œ¦0@ 0”Ëìëç[¶l­óƒ¸$ HÓ@ J¤­¥õ£}góúh³·?ÿùô¯î­,çõ¡ì0 $€b €¡DjÕŽñ¡ã9~@—$Á÷ `HÄ@C‰ôtì8·ç`îvò‹qó²K’@Ó@ Jd°Ös¢wo#>²K’ "€i(  %²¿{p÷@ã>þùßÿöý¹kûø9L `€`(‘£/¾:´}wC?…K’H™¦0@ 0”ȹ={:v4ú³Ü[Y>3ûß]’D‚0 $€b €¡DƇŽ×ªÅ|.—$‘ L `€`(‘믽SmÝVاsI©À4†Ò)æe ”‚'lAöa—$‘L `( lð„-^{[õêÈXOí’$E;oäìU<2°Á&\¼®§Ÿ}ïå·à’$šžâåìU<2°Á&\¼þçžë{#ì .I¢¹ ` ^Î^ų!lÂÅíê;´k8ô.I¢™ ` ^Î^ų!lÂÅ{ó…Wöí|)ôâ’$š’âåìU<2°Á&\¼½{k=¡§ø3—$Ñ|0/g¯âÙ 6áâ]8ÜÝ^ =Ź$‰f"€x9{φ l° ïÊðÉΧž =Ÿ$‰¦!€x9{φ l° oöõó-[¶†žâÁVÖVß½õK’(; ÄËÙ«x6d`ƒM¸`m-­í;zŠG˜ÿöëŸOÿêν¥ÐƒÀÀ@¼œ½ŠgC6Ø„ ÖùÔ3W†O†žâÑ\’D© ` ^Î^ų!lÂën¯]8zŠÍšYœ;9sÉ%I”ŽâåìU<2°Á&\°ÁZω޽¡§x .I¢Œ0/g¯âÙ 6á‚íïÜß=zŠÇ6>?}þ÷¿uIe!€x9{φ l° ì苯mßzŠ'á’$JDñrö*ž Ø`.Øéþ½;COñ„\’DY` ^Î^ų!lÂ{ïå·ºž~6ôuqIñÀ@¼œ½ŠgC6Ø„ vud¬½­zŠz¹$‰È ` ^Î^ų!lÂk¦Ø%IDKñrö*ž Ø`.R{[õêÈXè)òä’$â$€x9{φ l° ©ëégß{ù­ÐSäÏ%IÄFñrö*ž Ø`.RoçÎÓýBOÑ.I"*ˆ—³WñlÈÀ›p‘†¶ï>úâ«¡§h—$ ÄËÙ«x6d`ƒM¸Hû»÷w„ž¢±\’D 0/g¯âÙ 6á"èÝ;Xë =Eù$‰à0/g¯âÙ 6á"]8ÜÝ^ =EAn|õÉÛ¿wIA` ^Î^ų!lÂEº2|²ó©gBOQ—$ŠâåìU<2°Á&\¤ömki =EÑÞŸ»öî­\’D‘0/g¯âÙ 6á´lÙ:ûúùÐS„±øÝ7Ǧµ°t;ô ¤Bñrö*ž Ø`.LçSÏ\>zŠ`\’D‘0/g¯âÙ 6áÂt·×.=E`óß~}䯅¥å»¡¡É ` ^Î^ų!lÂ…¬õœèÝzŠð\’D0/g/R°oçKo¾ðJè)bá’$JñÀ¤àЮáÑ®¾ÐSDÄ%I4Žâ%€HÁXßýÏ=zŠè¸$‰FÀ@¼0)xïå·ºž~6ô1rI¹‹"€iVÅü« ”ÚÕ‘±ö¶jè)"å’$ò%€i  ðHþ8ä#¹$‰¼`Hd«¶n»þÚ;¡§(—$‘ L `€lµjÇøÐñÐS”†K’¨“¦0@¶žŽçö =E™¸$‰z`HdÚ¾û苯†ž¢|\’Ä“À4ȶ¿{p÷@è)Jiñ»oŽÜ¸°þ×ЃP&˜ÀÙNôî¬õ„ž¢¬\’ÄãÀ4ÈvnÏÁžŽ¡§(·[wŽ}øK—$±˜ÀÙÆ‡Ž×ª¡§(=—$±I˜ÀÙ>Úw¶­¥5ôMb=€×3Ø%IdÀ4ÈвeëìëçCOÑT––ïûð—·î,„„H `Hdho«^ =ErI#€i  ¡»½vqàpè)š“K’x L `€ ýÏ=?Ö÷Fè)š–K’ø1L `€ £]}‡v ‡ž¢É¹$‰ïÀ4Ȱ^¿ë zŠæç’$6`Hdë{£ÿ¹çCO‘ —$QÀ4”Èpqàpw{-ô qI˜À®ŽŒµ·UCO‘—$¥LÓ@ Ãìëç[¶l =EŠ\’”,L `€‡ikiýhßÙÐS¤Ë%IiÀ4x˜Zµc|èxè)Rç’¤Ô`H\’T"ÈSÙlÈP[q¹Ì¾~¾eËÖÐS—$•…ràÔE62ÀV\"m-­í;z òç’¤ø ` N]d³!ClÅ%ÒùÔ3W†O†ž‚†˜ÿöë“3—\’- äÀ©‹l6d(€­¸DºÛk‡ž‚FYY[=ÿûߎÏO‡„À@œºÈfC†ØŠKd°Ös¢woè)h,—$ÅI9pê"›  `+.‘ý݃û»BOAÃÝ[Y>9sifq.ô ü™ràÔE62ÀV\"G_|uhûîÐSP—$EE9pê"›  `+.‘Óýz;w†ž‚âܹ·ôóé_¹$)ÈSÙlÈP[q‰¼÷ò[]O?z Šæ’¤` N]d³!ClÅ%rud¬½­z pIRpÈSÙlÈP[q‰ØSæ’¤°0§.²Ù¡¶â²ho«^ =¹$) äÀ©‹l6d(€­¸,ºž~ö½—ß =á¹$) äÀ©‹l6d(€­¸,z;wžî?z bá’¤‚ ` N]d³!ClÅe1´}÷Ñ_ =qIR‘0§.²Ù¡¶â²Øß=¸¿{ ôDÇ%IÅÀ@œºÈfC†ØŠËâDïÞÁZOè)ˆ‘K’ €8u‘͆ °—ÅÅÃÝíµÐS)—$5šràÔE62ÀV\W†Ov>õLè)ˆšK’G9pê"›  `+.‹ömki =±»·²üóé_­—pèAšràÔE62ÀV\ -[¶Î¾~>ô”†K’r'€8u‘͆ °—BçSÏ\>z Êdñ»oNÎ\rIR^0§.²Ù¡¶âRèn¯]8z Êçý¹kçÿÛÐS4 äÀ©‹l6d(€­¸k='z÷†ž‚RrIR.p½x÷ÖTè)b!€ëåa“ÁÄÀ*LâlÅÜ7>t¼Ví=)rIÒ\/› – V`g+æ¾ömki =‰ZY[}ûwãS ³¡ L×ËÃ&ƒ%ˆU ˜ÄÙŠYײeëìëçCOAê\’$€ëåa“ÁÄÀ*LâlŬko«^ =TÖëw½“½$I×ËÃ&ƒ%ˆU ˜ÄÙŠY×Ý^»8p8ôð'S ³oÿn|em5ô EÀõò°É` b`ˆ&q¶bÖõ?÷üXß¡§€?Kó’$\/› – V`g+fÝhWß¡]á§€Jí’$\/› – V`g+fÝzý®7pè)à/Ì,Î]š»žÔp½ž˜ŸöWñÀ@œºÈ&€IœNÙÑ_Ú¾;ô4Ê¿Wþ}ìã_ÿÿý?½ÇUY` N]dÀ$N§ìtÿÞΡ§ Îܼ<>?z 6K9pê"›&q8eï½üV×Óφž‚ZZ¾;8qÌO€ËB9pê"›&q8eWGÆÚÛª¡§ ±ŽMÿêÆWŸ„ž‚MÀ@œºÈ&€IœN™i –nNž =›"€8u‘M“8œ¬ö¶êÕ‘±ÐSP„#7.Ì,Î…ž‚GÀ@œºÈ&€IœNV×ÓϾ÷ò[¡§ ³·??øÏçBOÁ£ ` N]dÀ$N'«·sçéþ¡§  £“§–n‡ž‚GÀ@œºÈ&€IœNÖÐöÝG_|5ôdfqîÈ ¡§à0§.² `'€“µ¿{p÷@è)(ÈÊÚêàı¥å»¡!‹ràÔE6Lâp²Nôî¬õ„ž‚âŒÏOŸ¹y9ôdÀ@œºÈ&€IœNÖÅÃÝíµÐSPœ•µÕŸüÃßÝ[Y=%€8u‘M“8œ¬+Ã';Ÿz&ôêÌÍËãóÓ¡§à¡0§.² `'€“õѾ³m-­¡§ PKËw'Ž­¬­†„À@œºÈ&€IœNSË–­³¯Ÿ=¼ý¯¿žüâãÐSð`ÈSÙ0‰Àijo«^ =,,Ý<z L9pê"›&q8MÝíµ‹‡COAGn\˜Yœ = €ëåa“ÁÄÀ*LâlÅi¬õœèÝz ¸ugágü"ô<€®—‡MK«@ 0‰³§ißΗÞ|á•ÐSÌèä©…¥Û¡§à‡p½?}ææåÐS¤N×ËÃ&ƒ%ˆU  `+ŽGË–­³¯Ÿ=ÑYY[œ8¶´|7ô IÀõò°É` b`ˆ†ØŠãÑÞV½:2z bôî­Þ½5zФ àzyØd°1° Ä@ClÅñèn¯]8z b´´|wpâØÊÚjèAÒ%€ëåa“ÁÄÀ* °Ç£ÿ¹çÇúÞ=‘:sóòøütè)Ò%€ëåa“ÁÄÀ* °Çc´«ïЮáÐS©Åï¾ùëË'BO‘.\/› – V`(€­8ëõ»ÞÀ¡§ ^Gn\˜Yœ =E¢p½?}ææåÐS$G×ËÃ&ƒ%ˆU  `+ŽD­Ú1>t<ô”ÀÊÚêàı¥å»¡I‹®—‡MK«@ 0ÀV‰žŽçö =åðþܵó¿ÿmè)Ò"€ëåa“ÁÄÀ* °Gb°Ös¢woè)(‡¥å»ƒÇVÖVC’\/› – V`(€­8û»÷w„žâIüŸ•û-ÿ!ôÉ9sóòøütè)"€ëåa“ÁÄÀ* °Gâ苯mßzŠÇ3{ûóKŸ^»÷ÿͽ\¼;÷–~:q,ô Àõò°É` b`ˆ†ØŠ#qºÿ@oçÎÐSlÖÔÂìÄü‡—ñ”kø¦q䯅™Å¹ÐS¤B×ËÃ&ƒ%ˆU  `+ŽÄøÐñZµ#ôpoeyêË›—>½öƒ· ^¯ßõ5U²–nNž =E*p½VÖV'ŽýàmÉÈràÔE62ÀVƒà?;]ϧ‰Ï¦'¿øøaïqµIë¿‹õßK^S±IãóÓgn^=E“À@œºÈfC†ØŠcðæ ¯ìÛùRO}ÿ=®®õÉÊÚjý­ÚºmjdÌ ¶´|wpâX.+ÈÃ` N]d³!ClÅ18Ñ»w°ÖSð'½ugaâ³é™Å¹|?lß gn^ŸŸ=E3À@œºÈfC†ØŠcpqàpw{­°Owã«OÞŸ»Þ ·®U;ƇŽ7â#“ÁM9pê"›  `+ŽÁ•á“O=Óèϲ^GS_Þ¼ôéµ'{«Í;Ý ·sgC??v䯅ÜžÏ äÀ©‹l6d(€­8³¯Ÿoè7»´|wòOÌO×ùW›ÔÓ±ãÜžƒ|"¾oaéöèä©ÐS4- äÀ© bVÀÑ&ëÚZZ?Úw¶Aüν¥_Þú ¯÷¸Ú¼ñ¡ãµjG‘Ÿ‘uÿùÜìíÏCOÑœ0g_ˆ™†bt>õ̕ᓹؽÇÕ&õvî<Ý ȧNÙúr¹q!ôÍI9pö…˜ `(Fw{íâÀá?àz]š»>ÿí×9~Ì'pýµwª­ÛÂΠÑÉS z{³Ä ` ξ3 ŬõœèÝ[ÿÇ)ì=®6i´«ïЮáÐS$gjaöäÌ¥ÐS4! äÀÙb&€¡ûv¾ôæ ¯Ôóî¿ÇÕäþeýä5UýZ¶lóCà‚­¬­N‹ê;¡9` ξ3 Å8úâ«CÛw?Ù?{çÞÒ¥O¯M}y3Î `ííê =ErÆç§ÏܼzŠf#€8ûBÌ0ãÉnÍÿöë‰ùo|õI#FÊKµuÛÔÈXCoxâÇVÖV{ßÿ¯qþ;‘òÀ@œ}!fŠñÞËou=ýìæýÌâÜÄgÓ·î,4n¤ÕóómžØ™›—Çç§COÑT0g_ˆ™†b\ko«>ò—­¬­^ÿê“_Þú ’÷¸Ú¤Zµc|èxè)’³´|wpâ˜çH9pö…˜ `(Æ#_k÷V–'æ?Œí=®6ïÉþŒ7u:6ý«Èÿ„|¹` ξ3 ho«^{Øÿ÷ν¥ß|6}å‹Ký£¼®§Ÿ}ïå·BO‘œ…¥Û£“§BOÑ<0g_´þ”)Ëw¾†<,×ëåý¹ëMó¼ñ¡ãµjGè)’s䯅™Å¹ÐS4  äÀÙ—ÝÊ”â›_ß·ñu(Å´”NoçÎÓý¾ÿwÊõW›ôãß&Xÿ^ZoàÐS4  äÀi’ÔüàùK@Wʶd”ÑÐöÝG_|õþÿžZ˜½4w}ñ»oÂŽÔ ›|¯/ò5:yjaévè)šrà(IRø|‰ùU €üˆ|`Êh÷àHמüâã‰Ï¦KúW›4ÚÕwh×pè)’ã‡ÀyÀ@%IGÆÃ%ÚBÊ\Æõ¢¤ºž~vñ»oî­,‡¤áZ¶l«¶n =HZVÖV'Ž5÷¿[)†ràI:ùp‰ðål—q± Þ|á•};_ =ErÆç§ÏܼzŠÒÀ@"IÄ&Ÿ,±½" àÍÿ–c›J¡Úºmjd¬eËÖЃ¤eemõ'ÿðw)ü)ƒ†À@œ IÁã>Vây]¤Àõûjr(‘C»†G»úBO‘œ37/ÏO‡ž¢Ü0'HšÞ“=S"yi¤À¥^&(—Χž¹2|2ôÉYZ¾;8qlem5ô %&€8>ÒÜêy ÄðêH$€Ë¾LP:§ûôvî =ErNÎ\šZ˜ =E‰ ` ÎŽ4±\ž&a_#MÀM°FPFµjÇøÐñÐS$gaéöèä©ÐS”˜rààH³ÊñQðeÒÜÜk%µÀëzŠä¹qafq.ôe%€85Ò¬ò}”„z¥4k7â÷e7ƒÇÒÛ¹ótÿÐS$çÖ…Ÿ}ð‹ÐS”U3pâ‚÷ÿú¢˜îV‚ÿ~+ŽMÐt´±¿W4e7Íê@Ù]>ÙùÔ3¡§HÎèä©…¥Û¡§(%Üœ‚×`‘QºqX þ»®89Aiè–Rð^ÑdÜèߎËhWß¡]á§HÎÌâÜ‘BOQJ¸ ïÀ‚‹ôû'•à¿÷Š“4…â·¯†j¦n²¥&вeëÔÈXµu[èA’ó“üۥ廡§(Ül¢*À Ç”¨¾@¹³]4G¼½ÛÉaóöí|éÍ^ =ErÆç§ÏܼzŠòÀM%¶öK6€+NNPZÅï!Í‘Žþ]ÙÛíä°IÕÖmS#c-[¶†$-+k«ƒÇüøq àæCøiÑPbøRTœœ „î Ý1JÀa·t;9lÒ¡]ã]}¡§HΙ›—Çç§COQ2¸IÄ|¡Bô§“¾ ''(•àûF©²AÃ_”Š6§½­zud,ôÉYZ¾;8qlem5ô e"€›A çƒJd\ØgÏæØeÃŽQ)sF–wòͰ™Ãfœî?ÐÛ¹3ôÉñCàÇ%€K/æÃ®86A4ýv!€ëg3‡GªU;Æ‡Ž‡ž"9 K·G'O…ž¢Lp¹Er2› ‡’È¿>@ RØ(ÊÀ•h¨b3‡M¸8p¸»½zŠä¹qafq.ô¥!€K,’3AðþÌ>‘ÄÿUJd‹(uW¢Y¦ŠÍ¥·sçéþ¡§HÎü·_ÿÍ?ý}è)JC—U)N1pacl†“D%’ÍÁ5H›ÉbUìäð(ãCÇkÕŽÐS$gtòÔÂÒíÐS”ƒ.¥²œð99A$âÙðfij^;9dÚ¾û苯†ž"93‹sGn\=E9àò‰çIynæ Ï­âäˆgO(fCh‚®Ä´j;9<\Ë–­S#cÕÖm¡IÎO'ŽÝ¹·zŠÀeRºg<\)áWh„4·‚æàJªË¥3ÚÕwh×pè)’3>?}ææåÐS”€.¨žú•p%²¯¡“/ÙM i¸’ð"B‰T[·MŒµlÙz´¬¬­N[Z¾zØ àrˆêy_‰ì‡®uþˆê+éäEŠêå_ÀuHy)¡,ííê =ErÞ½õÁ»·¦BO;\å}Ò àGrl‚b$þÚÀf3‡ðCà ––ïN[Y[ =HÔpìb{ÌGX›{ìˆíKZqr‚Fò’¯4]W,+”Áéþ½;CO‘œ37/ÏO‡ž"j8^Mðt3€+Mñµ6Ëý¾æ àŠÅ…èÕªãCÇCO‘œÅï¾ùëË'BO5©æx®GÀ•fù ¼Ì74eW¢\âŠÍ¾çÜžƒ=;BO‘œ#7.Ì,Î…ž"^8F>ÑcŽÌ'>j4Í×ø1/ðïkÖ®D¹Ð›9ü½;O÷=Er–nNž =E¼ptšéYyWšë« lˆð¥öuÝÄ\‰r¹+¡Wâ1>t¼Ví=ErÖx=ƒCO)‘8á•èÄZç!#Î/»“<™8_Ñ•Ð/êæàJ¬ën'‡uƒµž½{CO‘œ™Å¹#7.„ž"R8q>¼+eøùjý'Œ8¿øNNð¸â|-W"x97}W¬>ĪeËÖ©‘±jë¶Ðƒ$ç¯/ŸXüî›ÐSÄHG¡YÛ¸~NN°^ÅÙpXÁ¿8ÖhWß¡]á§HÎøüô™›—CO#Xs?°ËÀ•f_hn^¿”BWâþN¨Äñ%‚ Z¶lÙwvý¯¡IËÊÚêàı¥å»¡‰Ž)æGu‰ª2¯#EÓ/4Ÿ˜_¶•˜^¹‰pÅ·ÄêЮáÑ®¾ÐS$çý¹kçÿÛÐSDGóCº\I™ãy"æE©89Á_ò‚ݼt¸ý7F%¦¯¦ÚºmjdÌ ¶´|wpâØÊÚjèAâ"€ˆüÙ\ºžÌ÷$‘Îê@yEþ:­Ä÷RM*€+¾C Jc}oô?÷|è)’sææåñùéÐSÄE-ò§rc2÷cDäkTqr"m^¡O µ®”áû¤ß ªVí:zŠäܹ·ôÓ‰c¡§ˆ‹.Nüã’–d# .Ä/þf%Ö×f‚\ñ ñ9Ý ·sgè)’s䯅™Å¹ÐSDD!Ùgpy¸’ðªAœ¼$ë‘fWJòmS‰õ«¹ëéØqnÏÁÐS$gaéöèä©ÐSDD7\)ž¾¥nÈÆJ±v''šWbý’ àŠïˆÌøÐñZµ#ôÉù›úûùo¿=E,pyè–=€+‚*Ë °ýk0宸F‚˜ôvî<Ý ôÉ™Yœ;rãBè)b!€³ö¾&àŠÕ„@¼ôr”xßç; bвeëÔÈXµu[èA’3:yjaévè)¢ €sV¢çk¥YÒ±€ƒ‚e…"yÅåNßç[ b0ÚÕwh×pè)’3ùÅÇoÿë¯COœ'OÖhš®”mq+NN”“Zƒà ¾Ç ¸–-[¯ÿ—ÿÖÖÒz´¬¬­N[Z¾zðp>?}ææåÐS„÷ÿvC÷ endstream endobj 10 0 obj <> endobj 199 0 obj <>stream xœ]Á †ï<o°ÁÐe‰á¢£¾+á #8¾½´¨›Ð¤ýÿ´m¶ûÝ>†E6§<Ã9…è3>æg”#ÞBJK`ùTœáî’h¶—®¯„²4àT룻csVÃÀHUÌÉfo(6m »™JXÑÿ}÷U4NŸnÝ*m)[”ºµŒF#£¾"OH£‘‘FB]K¨F{™ž©^†½Œ#´ª^«ŽÐš½ÖЧüÎCÓúßm%> endobj 69 0 obj <> endobj 200 0 obj <> endobj 95 0 obj <> endobj 201 0 obj <> endobj 71 0 obj <> endobj 202 0 obj <> endobj 17 0 obj <> endobj 203 0 obj <> endobj 73 0 obj <> endobj 204 0 obj <> endobj 70 0 obj <> endobj 193 0 obj <>stream xœœ¼ |åù8þ¾ïÌÞ»Ù™Ùsö¾7›ÝÍæÚ\$Ù ·H3„€jQHÁÛ žÈ¡P­'*´žh•PÁÖV­Zï£ê÷ íñ¨ñ*R­d÷ÿ¼³›phûéÿ·“÷˜ç}gæç}î÷ Œ2 UˆAS¦¥ªüÛ:²™g_0Yáüáv„ðá³/^á{ðÁù_àC„T×,Z¶ø‚íB-\£îEHñÞâ%—-*ô+ºöwç.œÎs?Ä.AèѯX{.tW&„Jêáo Ü¿bÉÒ³çÎï9ŠöÌ æ_ºLs¥ú”Ì ïÂù,,ö/…̽léò…óG_£íË~¾pÙS[6x¡ÿ•pývöc„Ø[J³yÊ,¦¿å®‚6hÏ åóä]¸zz1~Óá¸MΧãÉ…ƒÞB _¢;VÿŒ¶" þb0³Qº]‚ÞF3ò_ÔîG_¢j@çæsˆG+QÿÝ "pU=z-DIgÿŽ0*ÃÌcø”„»LG·#zîX–×Âù✀¿Äô¨ùŠü7øûb~ú n"ï°O —Ñ °(wm~]~SþT‚Ž2î¡ßç+óÀU3P/º] #X…îC¯àNÒLöç×À˜fÃV¢'ÑK8Î"¶ è,è}ºíAÏ WÑ{è#Œ±—âUøMü– =—{.?!¿ ¿Ag¢´ ZÝ8Œ[Éfó8óîÐÿåå=pïéèbt)ºm@Ñcè]ôôfˆ–L'3˜Ç‘5£9h`óVÓVô":ˆÕ¸7b ߀K.f™¡ç€&Yd Ž—±ÿK´ pú Ú†žC¯¡×áž_N,â8žçâ_àëñÍøWøAü[üþ;Q÷†¹šý#û÷Ü;ymþîüVx®¹Å`fêÑ0Ÿ¯ ÏàýÊpgð$N fõC¹\u~\~eþùwQE¡o3j‡wžŒfÁ¨/C×¢}èpí+èÏèú'`‰ÁZ,.|8ˆÏÂÓðE0ŠÇñ—xˆXaþêÉ2@ÞbâÌ+ì,ö‰¡9Kn ÷e.Ÿ,ߟÿ}þey~ká9m0ÝhZ.ÏØ.xÎÐaô)úž¡Ä^ëx< Þ÷N¸ÿA|ÈIM®"¿%y¦™ÙȼȊì¹3säîÌíÈ×ä'm1HDTG#PÓ Ô ÷¾°y?zffPÏ;è lÇ\'à™x6îÅçâ¥xîÃWà+«[ñN¼¿ƒ?À_–(‰ð'g“kÈ­d'y޼C3ˆ™ÆÌfú˜+˜[™ÌkÌ',Ç&Ø v2ÛË^Æ^®@ FiU¿|Üvü‚¡Cwý>WžkÏý,·.÷lîÜßòºüþüGH‰*`Œh1Œñðþ7 ›Ñf GaŒE£¿Ãœ¸`°;`Ä^yÞÚ`Ü“aä³p'^ǹø|Àÿ*üÀOáøYü"~ ¿?Ä_ £/‡cpÁ ²Þánòé'ã[ò=aLSÍ´0½ð6«™á}î`>d>b ka+ÙiìJöy£8Gq»b“â9Å ŠÏ”œ²«(#NHø1/“gÙf Ú‚:Ã|FÞ Møäü0qãgáin¦ƒé md"xPùȬڤô+ýÄŒ8U/½¹‹$™Yl„Ñ£ÀoˆÌ!7^ô~ ý@Æ¥]̼B¶f{ Û‚ßE+ᙈð1ÔŠZq ÌÝ›¨f(ÉlcÿLï¨P3ÇC~5û±‚0o€lÆ„ùžƒq±¶F‘›QÎ9<åàÀ¿åïÁ³P={ˆYO&’¶ÝŠŸ…w܇–}ø70/õÀ?Çø¦]…û è|ò+ ËHèyú¾[€s€¹ ‘Eˆe älôé„Y ¤_tzZ‡×¢ÂÐËä—¨/dž9.•||ogÆ£íøöEöEžlV€ô€Bî18ÓÏD€jê‘‚$€þ»Ažxò-¾’,Açá;™Oñƒ¤MA ™åd,¾=÷-ÛÊTÆö‚4iS6¨‘¢Iáfk`Æ?F-@‹RžËT\CëÌ›ÌÑ|gÞŸëQ”ä>D—vƃt[¼4½­xžÊæÉ$6ŸŸ‰#ÛØó6¬Ç~ôz8,· 7áPÞ‡ûò:<(|žrëÐ]ì:özö"ôVþš7 [ÐÝèw M½<žØœ ²ç<Ш ¥áíZÐhJ ­ÍyÚ Rrºõä½ým 5 ð1®[„ÎørÐPW «€ÿW£õ nG¡×É£d3ã'7’?‹Éyè}ô>ó<#á™è-v »MC!4›àÉu0K^¸n}þMxZ 9Aú×—Ýçÿž'ÿÈЫp¿‡`ì·(G£¿+Û’Z§K™–æ¦Q õuéšêªÊŠTy2/‹•F#áP0à÷y=n—Ó!ÚmV‹Ù$ðœ±Ä ×i5j•RÁ2£Ä˜àØ^_¤·ŸÇOÒóà|Ì? ÐÛïÐØSûôûzån¾S{JÐsÑi=¥BOi¤'æ|M¨)™ð úú_iú²xÎÔÙP¿©=Øéë”ë“åúF¹n€ºßøÆØÏm÷õã^ߘþ±Ÿ»vLo;Ün»NÛl[¨M&Ðv­ª:¨õۂ˶c[ –+Ä6¦q;Aj ªßlÓ/Ûéú™ð˜ùçôwL=¦Ýé÷w&ý¸íìà‚~ÝoŒË]P›ü˜~e[¿J~Œï<ú6ho{âÀÚõY-èëÏ ž3îì~f~'}‡ç¶÷Û.?l?q 7Úf¯>¹Õɬc?ÏGO×®]íëß2uöÉ­~šwvÂ=àZÛ»v,O'gÉyÁeœ @êÜÎïlLúý~:Áë²Z'ý«¦Î.œûÐç’RñÎ~ÒK[ ·XfЖUÃ-#—÷’w"êXúÕ‘‘?#g59·±[ÿCóÂBû¤iÁISçÌöYÛ[Äí¤é§œÚëGÚŠµ~SÛlÆIŠ5âdäV ʹ#éÉl}?†?¥LÔçdUj J‚}cû¹Þñ…¼Së÷ÿ—eó_Ñ«äâÄeÅaö7ÆO=uÊù)ÃÓ¯e`Àl„Lš>gíZí)mcA­];6è»¶wíül~Õ‚  ®Ý†Gdí²1½Ã3šÍï]ç컾^â\ÜÔJÐèíA|ãÔí¾qÚœÙ{8pynœ>{Lš¶ÞÑÛCÐ6{d® %#Pzæ£ghJ‹‘69÷H­’[Y ŸŸÅH†©‡a%'Ãà—¤sOõX¯äßÊ¿Áþ SÃÉ?L!Š€†ú9Ø q(Ú1›óyÐÊd/¨ÌÕRŠF¹ØQªZEKA.4Õ™Ös-ƒ´ Ò«XÔùÊ"„A^È3(tƒÜ¾…Ù‡ú!€ô$ Ù ½Ù ½É0Y„™'™Ý!/ŸçíPއsZŽcÆ´{+Z—Áy´ÍPx;Œ¡ÆÔH¢ ¶@:(Cz _ éUHŒÜ3íp´ÁÑÊ´ÂÜC‚ 1ŒGަZš¡o3äÓ$¿côj‚'5®šàÎM0=`·BR1Mû˜4ª€$Aê€Ô I÷IÀu Ø¢à]$Ážò‚½µ™¡ôK/Y–ž—ñu¯Ôª!;Ák؉z!-ƒ´ŠìPÆV3ô£}S¦@ê´ÒfHÛ ©Q¦Ð"éH†d˜)d ÃuÇv45UÉeum¡t¹ ¥ÞQelý94ÅÐfH 9CŽÁ«Ÿy! (ÚéUH!Q„GQ@F^0 ×Gå^J¹ß—ò ¢(ÜÿÔ> ùj/¤ÔIw¡ÐR€”ÂY)\S }Kzr,_AÛ; m€´¿Ø‰9 gî€Ñ¦ ÏÈ5#ä^&0@4Æ,à7[ëïS A#¹ °yàí&J!„2q Z2Å mƒ¤`öÀƒ# G)8üpøà€d<0{áØÇÍpÜÇz8ÖÁl˜·Å÷ÇIOzizezCzsz[zZµÌ‡£—ôJZdµ‚Ìxµ£•·f.2àÉùãrþs9—äÜ&9æÏ5¼0×p×\Ãms ³çΜk;ךkÈâ’-nø nØ7ÌŒjã†tÜP7Äâ†VäYÈ€ž‘óÑr^%ç9wãY¤y w!¿(Gwú¯ö~äϲxÀ{­?«†âšÂYW¡E»½þÅÞD)!ÿÓ,ÜÍÀ¿E*—ªU=*IÕ *W%U¥ª¨*¨òªÌjAÍ©KÔzµV­V+Õ¬š¨‘ڜ͒âTƒ˜•-”,ÍY¹ÎšYÁ€ß¬&h"ê71“Ȥi£ñ¤þg£I |ýǦ³X zYû…IhÒôÑöþºø¤¬*V}|R¿¦£kövŒo~r#¨½é³³8OA×;© ¼aœ¸þ&g±ìì¤×ÌÞÎâ›nêDÖ‹3öŒÐÂ7Œmÿ‰¬·˜ÇOüìñ“O`$îþÛ'M›Ýÿ¨»³¿ŠVòîÎI€9j1ï!õ¤vLûRG‹ÎÙ{´«Hý˜³(\»ª½óD?äxûä§…Üùh?ä;­Ÿ‡ÔÑ~aZúyä~žSúmoöißî÷÷i–û4ŸÚgñ©}Ë}û0…>þ“ú¨!¿Üǯ:ô£>žÿ¢Oø'ûœ„Í…£ãÿá‡÷ ‰øím—Sw£78f!¤ÞþuŸkï_µÀçÛƒÚð;EO$Ò»àìsi9a¿\ØÞßl÷mŸxùÛû/§ÍƒíÛÑåc¦ÏÞ~¹´°}`¢4qLp~{çŽqóË?åqk†·½lþOÜl>½Y}ָǢùqÚ<Ž>ëqú¬Ç鳯IãägÉTd©F£;Á¾•ËD§îuú;G[¹e-25òÛ¯rîe~éÀÜ׃ëh€D›’­ÉVÚ\F›J¨WYl²_5ÊïÜ‹)6q惣‘}Ìyíð·|y±ò_þ-_¾|żåó–ÓRþ[¾â"HtšÐr´|‚7hÕËúÍ Ò˜ÊæuÖË2šY¾¼s’çtùEˆÞmÍNÜ|¤vÜ/?™ÐòÓ”2â¨àvË/ÂЋv¼¨H6Ë14Âmdñ.4 GÍ\ +Thìv¥*‹õ;ª`i…AZ¥*»†84* Û‘¨žr…=~&w´iòPәܱ¦ÉÜMCM4UVøy?† d:îc—èäcÐçý=ÿ7ò7…ñ M§HÆwtéˆZ¥E6­pÀž”LäÐYŸàZ°¶ÅýoV=E& Êá3‘=Îë<|˜;|e2ƒÜ æ…ø«¬À}˜Q*ƒH”‰¤kj««¬3#çÊ @DžŒ/ØH˜¤‚Áò…ÑxsKÍØ[†æøyÈ® ”—µÇÕÍñDSsY²‰ŽYKfžeßk„G½ÛKYrƒ¤ÅZ„¶ö]Í^òÒ‘g$½ßÏ¿Êä¿äü^lE„<³CßEYòÀ® õRÐO‘»À*üwÞãè 7ost°×Ä5Fá5üÅ·8QgUúDѧċåªÝáS°oä¯7‚J ¯}ù«X^1£®‘TÝaêpm6ovn3oslsªÆ¡ñ‘ð„V©.‘¬íš ™ÒvWŽª$xR04Q­Ü.S×ÄlþÀ@ -¡BᖋݶôÄÝ6ƒm»’´MŸí”š[pû)¶%éÛÂ]“P×x©«.ÓUßRW3¾fR íâ5]|‹ •Ý^#H´ Y¡bL÷XÈ’)9¹¼+ìª wMuML·ÔÖL¨é˜ˆ'Ö˜œ]æö-vâêê0o4o13æ AÒÁ­8ÓFÓczŠ|Î-÷r`ÿÚšûÇÓ¦gÈáÜw6“É–Kxì¬ÑaÄ>Q£p¬–ó·Ü"ËÙ¹\Õ‰š|ÕwÀìûòŸ°fðquȆâ¨tÆhiÒ "VðÏÔ…ðe¼,¦ñúü`(‰jJ=öÉÞàÁ ÓL`2'¾&QdFÕ¥ó­u)K“7ÖiôuFà‡¼P§Ìâ%®ÝÓ¢,m©¯3&p"ßRW•%ÿx²]ƒRº³³Çu1wlp8æB® nph&¡!Õ=ÈË9ð¶­Á¼Ýv™4=Ù†mM5-¥¨±¶¾KP]5N-”¢­¾›Y¨Y Ôš«G•â†:È2•­¥¨- ¯2–bƒ2“ÂRŠl24¢W‡+W_ f—uÚ¤þðÔ9³%ÍhW£Ëê*q5µjò pòŸ# J’9¸~ø×‰úº±YY¤€*«ª& (-fkuU­BQ€×ÕÖ… BI¥dþM_räŽóοýöóÏ¿½iùÔ©ËiÂg?V¢Òñ*…ÀhKÔZ¨xï8ÿ¼; ÓÍØï–Üyç’%wܱdÚŠÓ ½6Ä z­V©,–9nÉwþŒvš¾|Å´³.ZÒ"—?ÈäXºJ׊ŽIWª®Ô\Ùð'ü²O1ªlfÅâàâÔªëZÖ´nUý¦å÷-ÚP*&¥SRwdF£2TQYlhí¡©‡™ÍJ•éô&HU•Uõ•Á`eeiÌÐØÂ¬&ØÀ”)GªF¦¼+d±c§'!Cûðí˜QH¶z¤Í$]YÚÑæïÒooJ(ÅÑ_?eÏâÐõTßLIIõ ÊLÌ4uˆƒö”cðh7X “"¹¬.—ü‚{ÎŽÔÜP3Бs_§Ž>·šŸƒ‚{Žru_7êöG‹óP)Ά­0EJ•*R˜›©¶®R•*[‘“±²0cuÌtiÎÍ—î¾bÉD÷íçÚq-/–XZ½­1‹ç|Ò<Ëã0š£- ãdn›~öõÓ–M8ûºgÖœÿ€ï’I¥gßf¶XEAoÖ]ÎE™Ö ¹ Ëï° «zk÷L›I°­ùŠ o¾ Ò;Îlc?FNÔ!Ecú2Ž(l%&­`U*œÍj²´˜“5Ó–’BL±èúÓ^¬@v,Êøëž÷®3"\>ô^UPÔê)nök"+ÁÂwHzt¸LAD–já3A÷A©ÉôFš5˜¬¼ôRÓ+ù¿1} È–î€ZǾ§K.؃=È^Ä(W…O5¯g„ê;¦ÖÑìë)õgÒÏ?’ŸÅ|¦¸ÒR£FcÅ¢†©G š±x‚¦Kó3ÍÅøRÍõÍíø.̓x«f7ÚŸÇ/jÞÁGð§šcø;M§Áº,~a£kA]š,€Au©ŸN1˜y—Ïâ}ÛŸ’ÍÀ!ÐE¼ôuwãÄÔCCsy'/jÉý:s /*Bÿšz‹â[‰hÔúÞûÝa’Âïˆ6¸7ÿ bòG’ê˜ß ÒüQÍÿY!YòÿÜí*Ñ”¨KÈÞüw`|3à.IÒ+ÊòßHÁ˜ÂUâ- ¨=.•ã¨Â–ø›…D³BP( Žfð ^Þ]j.+~½+Að%ŠŠl"°‘¨½*3ZßP}`Í!å\Ä.ÚD«hÍ¢Bérº§×É*£‘ÒH,Ra•:½V¯Ñ«õ*½BÉD|HB>“CÂqeXBI6%á Ñ/a§YDŸP9ì„%T¿øÕhØÀÁõ'ÿÀ¯•,¼Ç$fÌÞ–áifõx„L ›ÿA’ 5»xÈœd¢2[I&H³¨Ùj€dŒú1A—Ij!³ÒšÛ,úéM>—lP1šm^z•7C´ßb£Ùˆv²; ©[8YÎD#j s²2³Yá ¬(Á±€Î¶ÁQ]%¤™O®^x÷ÄkËÝcŒ6¨Mº¦ÜÓÎY§·•‰¥ ãnÚÒ·—6Œ_¿…|ðZîëû®•ößÒ­ÿ†¬l©z“>YD¤âi|«@¨uíÔh´zG¿S~ö>Ü.¸ú¯|s¸Õ@–¢$ø]Èýuä”À¿æJîØQÊ”QÑæ'(nш hîtyC‚ÝöE,~»„LA^Â6¯YÂB²"w]}ußðC}¸/ÞYç/,@ ‡êZHZö TÊ¢æ,Ê/¥R…TCäzªXŽ¿…Ñ7}Ó½O\qᣢR£çxÛy{æßû·H׏÷öN÷ÓIºèÊ#_,=wJé’‡®ê¶«´6®âyï¯mœ¿|EîÃ_SZý}þo, ÁÄïXR†H­êªª4ßššn«ÿ9R®ôßP{kúöúÓÕï1íµ½dzÉüŠíÓÿØ>7ýË–Oñôº]æLŸ…tA%¦6êâ¥<“‚Ø‘"èB¢ÇWIˆ0õ;|>!‘Å7íˆ4Wƒ=sÓ.¡Yl®Íbƒ¤µ43.WãhLí…)p‘«ŸÔ‰ Õ ¥áó½xUa"¨ëHEäáÃgrG÷“9?¡³1tN©çHÅ¥LòÔX)MWM:6™YE¸&(QP¡tD¢.£D½@Lg|¿x¼¾»¯Õ÷akÁ|‰Œ˜–àŸÁ¼DŠŽ›M>“gi˜G “ĘV\þmvÉ'åFÇ™7=~Ëæïîö8Dq|ß­w_9ë–Çëxû¬ËîÞüòòXÍ®w|<·‚8»qù“Ë&mœFy ¯íš·±©Æ¬±q¥Í3ö_7ývÐMïP~ ÕüèuÉúÜG<~…Ûë²Zìv»Ÿ±-B÷JBIÉ3Ÿß¿˜0`)3ÄïõâŸdVá÷ìOãj~Øž®Q5Ï1à ½‰ßÜ6–š„cå<÷ÍïMäfážùLôøËw¹o‡¥î!‡ü@çÏQ:Ì%ÐÿJSã hbb£]‘Œë‰]»3ö¢øýïvµH‰ØJ‰Ø§/¨6s¾ÕëÀ^·= ˆ S9Ø8,iÜÍ,«E‘°)‹ÿOÒØšµŽfN…U{Éõ(F–삞‹Ã¡,þŸ'91fµÃ$|g`nŽ ±jdÒ˜‡)å 2ËÔk·»—ô·]™Sé–°¨¶ \`áx¼»óâ‚zn§Qn0 *Z¯EvåÄ›ï{û«—\x¦±s¼éŽ[<´êÚk}p£&RÂÞ’[èõþ﮾K‡ëüVAnzñá›ÃÙ­$IåˆO°ë)DøQI_0‡jž¸Çï‰ìÍ£ÛÓ¥’4;JÝÆNRÏ`稕a@ðÀ¯¯Xä2XÊæß’´TzÀÕ!µ! W®dYVmfÍêQ—™M“L]¦óM—™n4]ÚgÚz_÷¾ðwƒI‡j•O!_Ø¿Ðw¶ÿ2ÿe¥ËSË*vö•½£ÿ›öˆ^˜££‡ãŸÉìµx¬n›ÈÙ 2èúˆW¤Hy´HL/SØ”%†P%ðȃ»’Í £qfñÿJVo³YmÖìU6£2®ÌWVQÆ–=M^AU(„CHOz2Ð\ŽšX¹×ã«GLºn¼à†ºÁp7H£\ƒ‡ ±k[Ñ“¢b*œðùYgä‚‘Qê :Q&Ø2 ûL,þ­dA-ØráP©€qERÂ~£—¶èpØ•PL•PÑãšdKŽÊµ>YáÈÖRAõÄñ R‘)Ô¥•"íÈbóééà%g>¸ð†×žyø‚§kÛ2[Þ¾rz½ÝÊ„XóïsûÅÈýK—mÞ²pþœ&bZ~áÁnÿþ†u¿qßçm^0Š‚MkÎmÿØÿúî{¶­¿ö·Óê€+ßÌç˜w€+-hÕv C·DWQ*òŒFo0,¶ ³Å‚,`Lèm:‹1&‹uZÞÈiYN¯Û œˆÉ#;mÑúùIæóáɲᓑÈ90$3ø±…ÐÐiz§ýD¤¡‚‡:sõÐCT–0Lî µµD°+Ù%™-6ßðà ÞÎiƒÏð±ì3„Q%^-µ þ„¾@_èYë¶Ä“³â ‰BWÂÚ%fûZû¯ðÝê»u·F7ÇïInÅ÷Gw‘ýÚ½ú½ñW´Š›.ÃúI¥9 –Í€+èÉæÿg "X¾7ÿ?àl|·“W—–†(¬¬4°7ÿ9 ç?ˆüÔ ⥒:Ø‹)ÝÍ&EªYifñ_$.³r‘f毎æŒuŠ•X³xPÒUûš¹¿&š5bÕinÇQ5‡œŠ¢#2¡R:•I³"YéôòVí|r™A•«Àg¨P€õò ‘œȒꔄ*ÁÁ8áLPÅúcOuãî>Ô×FtãùOv€7/òÉph)U€ °Ã™Â5LkØ.ÃÌúŒÅÝ-f¡0 …âtŽèo9ºYR)ëN ¸šNª3¦ó~vhË–C?;nYãÛ·ßñVcÌðë‹VüzóÅ—l¶ývÕªß>¾råãd]õý·½ÿþm=פ¦.Xûê«kt4~ºdÓ=ç/¸õÖœjé\øóG¹h¹hº£jÜ!%Uj¶LGå†ö†”*$ƒ ÈJìJwT¶ü@‚RW8ë°l”È.ׄºe1zV¦-\£°—ÅcñÒx4‰+”&³`æÍœ™U¦"ÕጦµÙÃÖd¬°ô C•®·)&©[J±-.ö syIÖ×””âÑÊöá%Îaå8l /{¦•Ã+‘E3䨅7 ÕU#«A#A5¾T³Zxka=ŒQËV¦K,®¸÷÷Þ¸h\&ãÎÛ'ñooêÝÔq}Òu&g»qÉ}g­=ßl6˜ìí×ÝvÁ>lÆå:–½sÉÛç_¸>$†2Û¯Ííú]îŸã8;ç‹4ªñÞÞ8uîÀè7׌ùõyC˜› >„¯™~Æ" ”Â#ÄîgoAe¨ï–âµ<`ÔÙ’¨KŽ&8ÎHŒMvÖGO¢#ù]™1ŽÊÊ嘤–Ë’$«aƒa³4`CŒ78Þ­å…`Œ6•D"Õe‘H¬Ì,Kh¤TVËÆ©[C’¢IY­3«Õ$¸E¸(h¼yWy7z™×¼Øsz½.§;àt8ee§Ãìt:ž÷$øåÉP0¨ÎÄž¸±Ü[NÊË5b2q˜"‘8öâÙ([$sYÄ)5Äc£Óë<äüÊÉ‚3’Ø]A"|2"ìÅ-ˆÚ絞Ò>}ϳÀ艩1K€… ¡Á> …£2uÒê#¤Þº¼ä.oâz]­]õÕÀΫqêRjw_êès§­­þ÷§òÕ*0Íi*,`0§9û¸H‘~|ZÃ抡÷úä¹?Ò¼/ÿNŽH>Œïn•ÁÏÓ À–[?ñþ¯Î½2 `>£bò‡ßV“³‡î¥ûžf u ¹PUáEÒÓÛÊ‹ÿQûÝ»Zņ²µñ{}›Â›ãO„•W„V†—Ç/JnÐn0¯ m«gp ¹•ÚeÜ2~™°Ì¤šè›ìŸš¿¡DQeåkô7†3e£âcŒã8µ&%ú\~gØYæLeqõeÜS¡çSÌXß„ðž|k+nó=èÛåS'Ô.«GÈm%jEc·ºÂWÂKKª|Qw,bFÔ·§²ªÊª&Vu0lÔ{õ)}F?Eߣ_ªWé³øZ)– #`>bä7òø×øCüW¼’wÔDK=X^þŠ*Ìꉗh‚Jô¾â^®n9C½'˜/9<Á"jÅò©YfyB Á¬Õ™"ñp™9™Äam0‰B,‰BºH£^]'ìëëë†_˜ž¤ëTERœh“¤ìwúyUm!ìæÇ¨O^LãîýÃ×^Þñàü!y¡í8Ö3¥¹ýW—ävà­S/mé¼o]îé…éÞuùÝ=©{æM_·€N9© ºÎ¯›rýqëøó¤K[è—ùƒìì㨔.Mšq eÐÄ(¬ëLÛBó9ÖóÊ—™—[—ÙwÚ´u®ÚЉ։µ]¶®ôù¶sÓ×»îJi«+>g#F]bµÕUù‚£1‚.¸3.„ëtëXO8^ǰ$®)‰¨{ý‘ˆ£Ñ1Vz+S•™J¶RlX}Ò$L¦»& Qôg†·1Míâj½­ZÝ`Œ¡Iýºi“úCSç€ÿäo´¯Çbåm©¹ÝAçøÇ~9ë±+1ý¨Ý¼w1ðžD^ò߬]£»QXcZc^oÙàÝà[ë¿)º6¶¡LZ.ꋹüt+µæ®è.?iSÛÜTÞê1äp¸‘Û¦&ô<­ˆÉ±·š/7z=V«ÛcSÇ= ñ¨I(bØè3££<áñ`Ì6AbrnÀ걚Ì@Ít`9£Q„ÿ´ˆ¼Pã+ÓZJŒ£Þ¨3²ÊH8. ǬÒ$˜¢ô‡Ë´¡rì³ËqØ/ÇÁ[^ âÑ%·âÂèç“ùƒ.YÑyS„2WPÿ&*s†[Þ]‚Ÿ;[ù”˜ûçן}M®‰B6áÊó÷t‹¡Ñ¡õSs¯™bv}Ïù“Ï[qõ7sFS®Xûì¼;ÏlîìHL~˜ ó‘‚ùHcArôx—*W*^W·.àò¦ƒA·‹Ñ(©­eôdh)%ŒbF9“€V4;lq“Éí¨)§N*ãé´»<š¤q2RDÜIo/‘šGtÁPÄ‘F‘°!ƒèԈх¿tå]ÄÕÊDwh¶h^ÓÒ|¥QhÒ‘H9JrI’Ì‚F´†Ã`Èz4g™R—ÂWÔ¼®¸Ô^œ¹Aº{Ö@xu÷ëÒW”fC…04ýé5ˆ¸o»ßj©%š|7ŒÀé懗iø‘¸ôð,ñÃá£}Š<ƒÜ@Ñ~|>‘>Y†1Ë)dè!,ÇPaì$óÊz,·ó„¶Ê¤Wr“zä–/hÞ³´fiÌR úZš×«ÀF>ÎqnßéInguÒXá­ ñšwÔH-U#‚h‰ó¼[Œ$PŒ‹‘X<v'ÁˆXƒÂ¡B"ÌŠF$uM8Ž —èH0 ŠïD(D8Â"Èés’ççk²¢pžÅû8Œ¸UÜFî+ŽåÄô±=”FT Ÿ+Î] A¹¡¦sq:öÑɳÐý“€»O_£,NAÝžƒ{ ‹—9çðuæ^Šø¡_œ: §Ø íOOÌÁ*Ð׃æh©~½ïnIqn ÇLÐ ÍÔuëg†Ò=zJ¹W¯aƒ¶`D FBµ!e-j؈»6¢«ÚX…«jË«ªRåî´VírIöØì ž’µe^7ÇøM‘ÚT¤vQ:ÍšüáÌ¿ó$ŸÙl"eaVãYT^žô`Œ-шQíUµØ¼zéijEÞ¤ÍÉ.ˆ,Ϩµwø„zi(îè‘䧈¹î‚œ+œPÇî$ÍÓªçìs¤ÈE‰üg(©4ÿÙ®5` ëP@t«]æ©f)'EÅb+®jë£ÂRœ²T 3˜¢°îɦz÷÷¬~åæ)k¾XÿÒzÅÚÞ†•¯_±bßÔZŒþzÆ5³ S…ÁCæÌx wgº¶cãÀš»×bÅÚ¥•f£ÃóŒW´¹g,Yxs÷Åw½~ÌWŠë`ŠíØf2XU0£?®Z \Õ†'é…_[ŸHí°îO±WAgˆ=‡O¶ü97vÇýn·Ïïv$ªdJáT¬:•ªªv'šFSgÌx3$oËdF·¹› ~„N/º'Bg}ˆxX¾±—ÆC¥¥á;>*MAm¨×ÇkêëÓ5îQÁ€a¬«"‰DÜq„#ñxÁgh5J Eµ'Tã µI.oÍæ¶mmdCÛÁ6Ò–%û$çÁã÷óž "‘„™B^#ÄHzÈR§È>ÔN?.)l©Îm’÷P÷Å›ähåØ&ê)È6!'oº,˜ˆ§“ÊOžýû“ÿtÕé÷%€2NÑ`€Ñœ±J¥@=Yb‚È á_ÿ–‡÷g/1ú9ÝݸvèMY\ç>”Ù¾†:ßË„$—y¢÷{ ©éî#z—‘ÚœçT—CägàÃõãÖáv ¹Àùh΋ޕ’)¶\Ôû >³Ï’r¥<-Šj}…¹Â’qewÕ"—Ë‹ÜbÁÒä/ØBvžG-µ¼Å"ðn»7" ÑNHDmŒh4jê„òS8̉¾õí#6u˜ìŒÌø/¦ò§fëG{JN rÉ/OÛWrH6òå˜ÛrY'I¥ïc }·Þ"èéÊ«˜•¦ËÍkÈzfƒi­ù;«ZCtf…¹›Ü«zTõ1÷‘ù#«’åq»¹Ýf¶JñÓà`ùD¯ë»ÝíUŽõy‰ÒÔn£¢T2ð$é¹Ì!„WÁó¥ÆE%µZET´a• «Äè½{ðÛEu&•±‡Ï,ºØÅ¯béÎÕÈÖŸrœ•(-V3ä¼ÂRרT™[sJÁV"$ Æ!e½xYÑê.nCŒw2t•ŽúK§a–Ùvø¾ž'—Pņ'ß1ñ¬ u]¹')Á’Eä9î:4ël\+“ï7ãÆ•znžJŽœQ\q6`Ù–Hõ`¦©H ÀžÌÀŠV_šZ}$¢sRCΨÁØ “ ¼ì¤…Žtƒþ/¿Ohùé~ºÍòÄÆÊÓí¬âÒõÛ¤\ÞÓKÇþ 2é|4L2øLÊŠ¹y§‘ FvxŸð>õ$*5þÍ}ÄCÆ¢‰õÐkèMüžëu÷1t skÃ(êŽz"õã\³\xöxÞBoá·ÜŸáO܆Ù¬—YÉ´™:^p$b&£Q0¹õ^Yxs(Ð X$GÜÞ”,¾uUÕµUUéZwJ§ÏÕÕ¬Z­`Ý:§¥p3;6Ú½vb™ív‹Ùí,/-h”xGœÄcÑx¼4ê.Ïæ×I.7F>—ÛíÁÄŒiî©GÈãö˜„²Ä-é<áˆ×ëñ¸ÜLÏ'º\Îú:ÂX"NRžŠÖFR)NÏš"zu$Z_ïöxÜuµž¨„^ÅÞhOtit[tT•¢±š¨$¤Ñ Ñ×¢‡¢_,Kþ*YÜ^܃Éü*&³.K ŽØe’ÕäcX3ë™bzÕtÐô¥‰5‰ ¿+Zå“©ñ'ï=æR…¿î>8íŽÇûì܇¼;„B©‰(Û†²™Þ”*|ˆC÷²5 L­þE!Ьø÷\Üþï•Gßÿ›ê“%×ÏÁjéÃAüã½(Ãjã»]%HîëÍ=Ím’YëO4—¦ùŸq nø³¬5 ;X^ò8ÞMݪrº¬J·NUÌgTöÃÈ^ TœÀK%«š`Kt‘ç Öa¥Ó‰­NVÇËDVJJxàØp¼@L`VÄ¥¥ñ„;¬eå.ªjF¥b0jÌò9øo6›˜9ä¡çµÛï÷¸Ý!'Áöö±c'2Å#á°' š¹|·ÓÎwAUÒbV‹Õn—ƒù)9JHá´11%Ñ“XšØ8˜P&å„ñNÚÝ$ô˜–š6˜¾2±F6‰ÉÆŸ˜­}Ô—ã ~8¦G ꨩ¨Ž »´e«uuyœîH5bµ¹4ƒÍ¼ 2Î)¯wÊÁËÿߪëÇf‡Cññ¿'†ÓDT%K†nÛT˜dyË’lJ|H–l¢Ò© uDZ¶ãͧE(?fþpBÓúY~†‰ø¸”Ñø‘G¬Gìßsß G­GEåóÖ¿pÞ±¾kÿ”ûTP98‡`±Zíìó¿ŒÇL̽šÛô­Š­šôRþI­¾–¬Wܤ^¥_cZcùÙ¤P×)ëÔÕš&}#W-T[íê2×§¸°¶¦ì£ˆê)ã~n@0 Xú­ûí{EõãÆ'¸…ߘî·<`ÝfTTÏ2MµvÛ7s·™nµÞc¿KT1±Œ±N´Ÿ!Î1ÎáÎÔ1{£±ÖTgi°ŸiœÈÔ:¥VíT:Õ1cÔµ€å/bVm2X¤²æÃZ¦$Lƒ•>T¶ ºÄV‰;m—7 ÓmSt uä1Z,Fé Ý@»¬ZŸ²ùc; ä²ùïvöŒ•nÊ-1;3V»Õ±ÓL“ÍÚaiÓg´Tdóëzþ;ZjŠ¥‰–` Xèu…ò¨TfÅgZLÈ0ÝÌ`3†bIhÉY2úbi§küÞÔ‚K Óhí§÷6S:Fô›Ðúˆç  ª!tC3Ü ,ºñË5/å^Âé—Ö|±fÆOoÿ«|ú 2ö‘Ü_·àN\‚xö–Üß¶¾‚Çæ^üð³Ü»x ¥­ Iº@’Q}%ÙYëTy×ä¼agÚ9ƹ'®-¢Ùüw‘ã:‰ªËÔ·:nó’aû3^´?ÚÞ¬´Ä‹öfB¶6ƒÈŒ¡Lˆ„Bv0:ca£ »©$ŸœX~ìD(t8GãoÔ~êF2‡‡$À}ì~ÈtFjéw¿:þï Rºç–†ØþM a˜åÍ·ábô9èÇý§GÀâøäñÆUMêhœ™ûë»ïŸôè5¹·ñ¡ÜŠS9úå5S¯ ×;LÓ§]Úrö}ïçæyö\àérÔˆ_”zVÇo¨X[sGü®šGJ*{ \-œ_y^5ÑF™¸3jŽ›“4±¢­¦­vBäÆîМðÌhwÅ´ê5³êºæ6ž?§rqMoÝ÷×l©{ºbwõ@MÝžÆ?ÆÿX¨Ð×µïnÔV¨C´zt R]M‰µ3®ªcÉXCº4SÖ˜ll˜¿)´.zmüºòë*ÖÔn mŠÞßX~[Å]µ¢‡âoÆ?møWűšcµÿjtÕÖ54²5ÕL"À`€æ`0à^< ‚æ€n‘*‹×H&&l« MU8f‹1AMÉ¢Êâî<è£êÁžN…i É—¨HlI(—¤ÃqpxvdCp9wlè0 \dR4nva7ù¤ÊÐp5ñÎÃâGv€Ë¦AÉ/ž;!†wYË1Uy¯‘46Ø$3åçx„L¼²r*!j=ÕЬ–~³PC³Zô«¥ß,ÔЬ–~³PC³¸ŸÆ¥¸[¡*n<‰¤Ëk§…0{ñ ¨ISþ¥\l:‰ƒÉÏ—M«=£Éß8Î¥lZËÄúÚ²_©¿°Ù¬áí¶} Ì ž{íƒööOç}¶”`3uvŸX«çy…ÝlæVÇðÄO(ÃçÉ}û6÷09çd¶'rŒ÷àú$ªÃ¿Þƒüùç¤3}þ–¸ÙfoéJ/ª¼¨’QÅ+'VÎqÌ®\á[‘¸4}SúÁ²G+_¼í}Ów0òvòËncåïXÿ¥‰ë½k¿ôþÆûXâß‹þ#qƒg_þ;¤AÆŸ”Õ§HˆQ'$„×W÷+ÉDÐ[Žj#"8¨v’DžT9eúrÊïååj{,)+£«w/¹%ÉÉ€àE<\uØ…"8äµk¥kƒ ì\*ÑÓØx-ðU€ P{ØÈKNq_q„ë'.9u¬»ïp÷ánù›¹&ù ÙÞWFASQ3ØtêzØ+vêѤ~¡5ðê}{óGóGwÆõi«ôÓ@¯tÍðÞ¸bÐŒ~0ðï,Ž¢˜²Z‡¿I ’H4\="°fl-'É«ã÷¾{ý=sVÝ$ѳe÷<¶4÷íGõ²ÜKD››xªØzþs6§[îùFÞn{&=½cIýô;Áþ܉¤W;ú@*k®>Ã9¥º»úë ÖÕŽ5Îõ wÖNðm%”$¶¶>2úmÛÛ·6•“¾¤É^K7WwÆ¥Xó(‡Ý¨0#\WRUdÊk躯#MM5|¸M·Ž-_­ ûÛÔŽ_^>« ÷x–zˆÇ1Ö–*#ÁˆÔº4¶2¶!¶9¶-¦ˆ‰cîÝ‹½'í¸=<†bá;ÒšÚð¢Ú/(PØx[Øjm+ü ºOË4ž¶^VØHæ!t1¦¸MhxËhqZq¯l42²óš¹µ`É 6¬¸ÿºu”ŸÑ»è±ÖYG~ÿÁµ­…–}÷Ý÷äØ1w¾>wî›÷³-.:;ïxèÚ æWUíå]îèÚy_ZSA›>¡«ksï¸oÉèÅ‹#8~üõ×=Cý‚ À×M²6¿Y*3jôiº$pyké¢ Q+ÒtÀ$ZkÁ‹ƒ8ØHÌ⥻9Ž÷À- *ù8WÊÕëzÕÅ]×WkpÓ6×A—Úõi˜:t4¾q´¸Ï?#+ßÓÂô? Úÿˆ„ý#RWÈÆ÷å(Ð?iþ~îyßíã}§rÿC©_’[#—Axïi@WÂ{WàÀ>äÑãÍ7àå\ô“<'XzKœ‡•G\Ÿy¿'ß*¿u~çýÁ§ÑV‰:ïõÎMJ¥`/xîÎB,Õ¢ÅbÝBYEÁiJâd %“È]Æk ‘à˜Æ`ÐjÜ|,HÏÇEª”:ܱ LÁH,fÚˆÀ7ر¿ã¥07Ĉ¦ úf•èð¨ÕS4=š¥š•š …F¬<Éïé–G”j»O„Fžÿ§€ªü=¤,Vꋱ#ÀøÈ²É°+ïä©MŸæÇ0CŸ?¼ì‰ËÇy%zOÁkÙôÌ5ÓÖ,–}Û€m½ý«Ï_Jž‘MdïuôºßqßÙ2d8þö<ûÌT)ùº Ã%»ÅN¬6…’ã¿ÔaVF|z¢ K¬ ^)q5ÉŸªË_ˆIÓ–:–:—º–ºo´Þ`; 8`þĪéåzù^¡×ľJ0gål’U²±vâ´yD¯ÛS³Õ’Zk¥m,kmµuâ.ëlÛ¶Gl/’¬ïÛÌ9ÏsæÒfŽ3™Ý³Å¥POÈZ"(Ä…:BB¯…¡¥¡P´Ôí/Ez¥ÜEcÔx5Ĩٯ9¨ùR“‡ÉܨÐh” ·^Áú´‹ÙÝãÆî´èv;D·O´#xa_6÷/©ÆÂ2>³‚e=³äL)ØQvÑl·‹{ì6¨ÛC0ã±X¡‡•DlYr±ä±GÆŒ%°êhÄï >Ÿ)bPF z‚ŸÁ „@ v#xº[ªzUÄ^‹RYZ”jjkÄU)¨C5¢‰ÖˆÉXê-í)]Yº¡tsé«¥_–ªK÷‘ËÀP°‰f³ÂeV) .µJŽ´Ñú¥¼|öN"EÒ Õ/Pø,OÃã̈G³8)Y¼f|ÀŒÍN‘bŠbƒâU«xZch žƒ;§°iDÉ"wØAÿëJ5¥ìGDn¨Ïa,ìóí> ­vî 4ÂÅx4hæÁ!9$ ®þRVNl\‚û²ùè§6#ýg@JfŸIýPÞe ¼Ÿ$«ˆÃæ°:ŠjzR¿cdÉ>@Ô¶lþ«íVnXÓ¥¯îîNî^:-F`2U›L§Á˜w¯ûâÓë®ôÊìUO¥ÜsKÿïêO/øCß(ÀËdŽ?˶Œ¬(˜Ôñ×™ÿ=‰Ó:€ÓVѵ,R-Ý&¤q£¿>˜n“ôS¬SÊ[ë§ê{¬ÝåSëçë/´^X>¿þ¾òõ²BÖŸ­É¶½ ¼à¡æ…¶÷Ðç5_fÛþ‰¾Æ_s;ܶ m¼ÐäAÎ_S]…ý55m‚ xü5f¿¿¦*È œW™1®"`‹qcDkŠÄqŒŽ´Ej"éȨÊHUÄ—%—J.°å´j‡z)#_ÖàšH[[¦¾> –—GÛ¨ù&dZ\c…^¯p»õV«S0oT¤ «…BáS tWt‘žDÛµS€ç–º·Ø¾Gä–‚l'µrGé¾U*aÅɇíÂpDR¤SNe˜'#Àî¢ñ ƒ'g”©[áá$±*ÃI¶$dæÒ <®Ä`…Lovh8ŠR¦¼‚%äßÚ×Y(á2¹„+€‹ås¸ž–§ÞÂ?Y¦Ë÷©Î-™JlÞÈ»2¼ÂB3Î’¡ñÉ ¿ÛhÆÙæÂý¡¬‚òI(ù°ûñˆ##/ž¾ñî4@-N[;û€Ü…7<æoh~CîÁÜÖäó£t¶¯ÍÝ(ÓøG”¢çáv[ñ™ü%ÜÉ#h‘|§€Ó mgä½ê…g~öéOVœØWRœ̼tòúØ6E+]×ù—lÁ'½ªpä‚È€ ;U]XǦR…ÿƒpêŽò¿ýp¿<ðn¯(½:‚;‚6‚ 5“Y…JQ-^ M}Tõ€÷Ñr&¢ {Gýí}{|SU¶ÿÞçäyò<'''9Išœs’´M“6i›ôAú8P^D*ÊCôòPÇçH¹ã :¾@DÅgçúQ)Õ:Ì(*Ê(x->g@u8¯Øô·÷>IŠÌ8¿?îçs?»›½Ï+m²×^k¯µöw­­»Ìu…ïGþNþ§¾Ûù ¾MÆ.þaß–dñyû3ü6ßöà^ûñj7EXé{Ø;|Ô5U?¯º·ê û¦ª—«T\m*G–×ÕMÊѨ"+å\‰Ë«“A] ÒµVs¢®VÏ7”¦V¦-f;–'èD,kµ–ó÷9å#¾a’$«ˆ'2LÊ­òyžü ü”¼K>$›d_ƒç¶”lÀ÷—4ì22è b}ÅÎ!Õ ÆÛû?É/Ði]_ÅKvÅz‰|´ ÙÆav²âļ¸ßŒHÁLôŽws¦*S!TÇŠbÍý뜒6iñÌyØU‰h?Ú(‡‘g°B´Eµ8“DRlŸ»Ä¤[·ih‘J­–^¸ §T6K2’É Ô :m4[JdêyÊà«OVÉÍcð²ƒ_Æâi^Œ{k190Ž}­8/Í$‚"|$GùGI &>mÍC¾Ä S–Td\ùÙå ËW‰<·[ž>[¶[¿×Ô§ûÀtDÿg+èªa¾ÙÒ§XΞ gè;Œ–…ðýÅ–Ë©k˜k‚W…n î½ ôD$Amµ8Ë‘]þLPТõ;àŠÙE4n`0<Ì,‡Ee°âîw{¡!÷÷žÖ¿\„þzààí·Ä/ݧýo¿’ûòÅݹc¯¼ÿ¡W[j"Ë:kfBÿkÏ!Éñß«oÚ²ùìÝyiôç ‘™{Õ¨jªïÔÿĺººËºÕº-þb¼/ÎxL³uÓ©˜ÓU V÷RºgPªÒ UÕÑÈ”+ Ú“Kà$±ªÒk0›E•© (ùö‘¡¹Aµ%ݪ{¹{¿[ç3—o‡¿Í£\Ú °¶Éù qý4a—h? cÐ1 éb¯ˆûA!÷ÇB§ª]½ú´hNtãŠG7¸ÝÈP9Ú¿ ×{ŸÅõ³›o½bM­ÛË›\w-þ·+àDÐÚúÇ´Hj;«–Ü'˜ŽóО‹Ç¬"j™?Î]§»Ì2P ƒjõ~9O} ¿ýL>=!–Æ.©\\P{µíÚØŠÚ›cµ÷ÇÖÕnŠuÕîÚ)–ó‰€0ëõ&³B`¼Ú+9=¢¥=¸¾Z–˜¸ Ö—‘©c€X^"A‰aœæ.óÓfÚaÆÎ §ÌûÌz³/S%w†×†»ÂO‡u»Âû‡ÃǺ°˜®8ÿ¤ÁJ¤ÆÒ b`²õ©­…xÆaB¢hïþãÀ7p|k… Ù_o š^J˜R¸‰YkñÅJ!9äŽÊqÙ3ƒ˜Þh§ÂCÙPêë2XŠP™4W[sR\õjmî‹x—Ïm'Ú/&\Q&¬9ðä‰OX³÷–[^ý–[öR{î!cûôQ‰sË 2sÒ#¿ÝaO¹‰wüöõw¼ñâ…ˆ.A¼ÐWª•}'$JÝp¡árÃZxÕ¦ž†ÝóˆáQã6}ñã{ÆC>£ÏÄzˆÜvð!žâçzyÞãUØX’(<‰¹©D"™RbNF“÷6h›K}ŠSÓ_-ѹyý§¼Ü¢†3I’ñRi€84A+/Gän:£“1™%ñ¢yâ!Õ2ÈRõ®Ô¾•ê…énwþ`,2„£ò"Ÿ¬7°§øß_‰nÉàÀ¯qŽ ¤ËÞÊúÒÈÀ›MÒéó놨_/† ÏÐXgZ§Ø Ç{$kˆ×´ŸÙšc\Ë12¤£²n>€ðt pÚÔõsæß8÷\d|„rŸ³ï'—Ï™¼¸[M8éE'fŽsÛ”þ¿ò/=çêJéŠþÏsMµhùGÀ h4zÐHƒ]¥V(b¨ŠÓÄâeâõ¢ÑesÎâ‘k°šgéõŠUˆÜH¥_¦záÏ 6+àNˆ]³2Cì:^rOá!/–œ¹jÈBtö*5µ~ut˜¡ŠÑéîpÆu â1ßÔÚkWÁ ø{÷{‰17áKŒþѳ￟;óÛ¿I*¤Ë`¹´-wÝ@¾Y xP;qª/ÊIÏqÌ í.p™£tÂNª“Þà°O6Ýfzд)°# ˜üxy)€¸Yo1õÂ'ŸÕé‹ö…U»Åà›.JœË.¬b—ÿ<•¥(š†¬6©¤dŠêÄàØßÞ!w4ú\þýGZ¿êŠ‹À9Ј¿ùà7.VÑ×Ôe¨C×®ÎÙ0ð…7kVóôÜ—¤ÌK¯Çß¾ÿ[Âù –®­ Æ¿ùBÄå»]×#.ÏP½ÛA cÁÖÑ'¼•´êÎÒz¡ëQµ; +øŠhU¬"]žiŒ´F›c­é%ü’°å »ê\TœŸ{?ú~ú³ègéÑiÓˆèˆô’È’Ì&~SØÉ„Ã@ã–AÀL¿ „`(„ÿ©ÕÙ"á•HóÍ ‡BJX „Ae-‘©ÔØt*U›V*ÓÖBþ=ÉØíFa1ÆYPÀÏ»‘ ü?ïJ”âëãb±¹ÑX¬4ª$¢‘h$"eÒ|&“ó.Î%0@¸2^†J6pgý†Òl¢6[Y™HP–,ÇSR Mhó²0 ßÌÈì€] Š®Ø–§;Ó””N¥ÏKÓi,Jê]hîG³Ïrs§™rš%s àyÈ`ëvÂûA§æžŒ $‰Éq$ É ‚±yÿ+çQXžÆ5º*ͯåèë6aˆL_w ^kÅ­õT’v+_Þ òy:Öè¬åÕGÎÞ“NOx§‰²S7ÚMM±è8Ü틤y‚ûaqùá­¨%©‰¿wHƒ# š7EqZÇôÀWÅ è ôÔÔüS_wGÅ´4q‡]jƒ °!½†…Ãü½ßál{¾ÈX|.Š^±aÁq~®>x>YØ=†¯fswÁå~^d:~X|ìÍÍt¢]Š8j'â(q”t¨éùîKÝ?q#åÃ: ëŒHKœ…5DÎëÞÀ²Š Å@‰u:§8w9i§(KC’VðôRð´pÝÉòïoXþL‡"¡Ž>«Gv#l,U¡69ê öFÇG“£Ù¡:ÚcÌ\©µÎºÍ¿5¡+ƒuš˜oœ¸ÌxY@_g¬ Œ1Ž Ì0êS¦úfŸ‡FÀc[FŒhnQêÝ|)(qp*·Ÿ;Ìãt€sr*Gscíç°+îhˆ( @q*”26¨(¡ ­KikµTíØdmm*©ÔUñÅE‡Ú`ÛØÖ¶6µU©L‚¥U•å%4VÔ«Y0ÖP!Ó>Ùl¦õuuѨ›±Ù% †2)¡S „oKK‚RY)>/í,¥J¿mI©µ;²@Ë®–}-t‹8®âIo‘Ïç¶olƒ3ò`ýª6×þ?¢,:†áf‹” –ÂD™®Täµ ©<æ«No‰Æte!¨7ˆŒ'Ëõ!èµúBòR‘¤@HÝð1:ô2Dÿë R^Þ.èžPËdÄŸÀ×B"Q‹?ÉVÔj)@;\n-‡71ÜCÙä´D}'Ÿi*ÙôOK/9_n¸tÄœºq$²òÞɵUŒK§TW&šÛÈ冓Òóg\:fìØ1ÙIçô÷¼Ýw©ÓÇ,ꛯk›Y[¨ #h”_ŒFùL4Êàµþ€á€‰ÚmØm¢2m5l5Ñ+ŒFjq¡i¡Ÿ¾×ÿˆº&Ô ·Qt ´$D¨£¨ ‰Ó|wÈM¹Ç’Åk…®ÓjS’'›Ÿ•4Ö ¢Î(5L±µeÆjŠmM¶ÁwÀÃ@‚ TW‰¬3"—ãXÆÌH¾C"ñ„â$êíÚTRoE¬Û©LyÍVœýÇÑdñ¯Gý«z-ïèMF“ÁDz4àü¦M·­ º­ƒÃ£·þá?¯ ¯$4½£ipuyÃó”Ñqò(:E½9ëÖÙçMi˜CÆÃ‡òûï—œuõŠbí6?VVÍ ÞtFÿçCÚíìkÚ~ÚÿŰ‚tÀu‡tMh„X€ŽW8A'ð~ ¾f9@ýNÿ{ã‹a©ñ"–ZD-Ò]dºˆYb»˜]äºÀcrË´C6Ó³Ñ*±+¶’Öî!­jsgžÐ Rà<¤böRkT/'TÏ«¢g–vöŽô†^øQ·‰ ‚Ý‚&·£ý+°ÉPÈS|R`àN ”8¾ÍÉÛyÏŽÐŒûQ·-ȇìIË¢ò,ló¸b±{Óå¶ZxT™TqÅâ$g%Hã3òÝD•À³žW./dõìV9tÀ0HY3ኢ¡&Üåa09öÝü3Å^®¦ÜÑwçþ ¹Ý/B׌»º>Ä/øÔ¯sÇ » 'Ø>ö›þpèþûžóÜu„{qÞ·Jµµšq4–¡W¦òL8ƒê°-„ˆ&†¥¶Ëà5+«,/~ͼo|ß|°ìýêO 3&‘NÐ×o¦7Ò›iƒ ,+&KD1P¢Ú,eáöœ4%T’ùÙÚbIGÖÈ¢‘jOÊ&&Ãõ:#e£†RÙa‚&_mØ¥ £D[ Õ•ˆ5ÅÎw¢Ú\ïG›ˆá»üÿØVì+·¦°ÝQI`m6 bªWüþ™²ðI¡ 88Jã2ìÝÁNôÓ²ÔI¾ô‰›/¿ö?/Íõ¿ðáÍÚ2ܲ"—úýoß½±¯oã]}ôüsæ^¶oeOn๜ACY!½"K¢‹ÖíÛ¿vÝþ}؉h÷¢]$á$œî«­ŽÆ| ŽÆ-à1ÿ–(= Ìó-ÿæ["_ ®õý¨ê'àßϪ6–Þ—¸«êñÒ͉G«Ø‡ÃðÞØ&iSŒÖì{±H“Í÷ž¼XÖÄð4,† Æð•Uz³Väí•r€1cQ™ Ö+ÆÍ¢ÔÉ@s˜9ÆÐŒ¯ºBÆ©eºBO‡tûB‡CÇBtHLœÈÅž!pƒD/"*-ðÄ)n¡ eO&¬OK-‘DŠp”OàÔ„å|¼Q66Œ²š×þ´Q1šS42Ì=´ùeâI&þäÜrâÒ»é£çsýþÕá›úî¾»¿¨×6b žx¹@QøÍsö<;›¸nß¾uëöï×2NëΡ¯@ÒÞ­ò×ÚaÂ<…YÂ]ÅÝÈÝi¸ße hnœÐž¼õæwï ¶ cGUÍy£ ànQ§”O&Ñ·JÜbçÉæÝz£ ºow2‘hÄ L«M†ÈÃ&™Ÿq)£¯ðRÄžÖxdž°˜è¿Õ[”R‡Àip$‡41¬ yô Ûø}¥ÿpD”có”ëqñv 4¨<×r}:,E=üИ‰«Ecw…Óbý½»àeDu¿[ñ{I =¿ïŽ‹|.Ñè ûfmÊ¥ q8ÖC=Ÿ×oöá=f—†_¨7ð­‘7 ÌÞ,m®ÿEÃo]¯úƒëá–ßú‹ëHúO£¾uO=г¸ ‚¾Å<*är îÿ¨›” éËL×9 5,É^Ýp]öƆ³ð[yæÖlOˆ:Ó…K«Õ榴Ïë°ÝÖF®I…uUu»•fÍŠÙæf™•Û˜^˜ÙFKU°ªÞ©Jëdd3å)A /¥ƒ¾±ÕÓÃÙ˜[Vñ,) ùP½,câ˜6#m(ed˹y–#°üÖ0~4ýIЦ˜ÆCPÓÆ"°i>âžÓ2'6Ôâ¤@Ôõ´¸C ëo Áz UÜ(t*´zCÀãmiQÒ„t_¶©!TüH–¨Ò$.”TyÑq Ôß–åÓLàùOqïhĶ-|=âÞnEh ùuIN JÒ€æX32;²<ªðŒëuºÑªFã)v4&ÕѼÅÑÀõ ~è9¬Xð¸*šbÑìþ] gpRÙ"-o(‚Ð%œ-+äs;Ò×jÖ,^×k˜¶æ–ÉÙ±©Ÿ=5úüyo¾úê*“ÛF@´¢'¼qÙÃ]gN˽zä¾õ[èx ©kƒ>Al*khŒgšÊ—7|íø¥-Rx»/ø$¾îªPªõêÑ““I)½¸éâUØê¼i[Y[^S#'üÐæ÷ù©‡™æEæmæ£ÿ‘ýgö öGí¯XÞ±<&œíy ÐÁ•ªÛ¤ÓM tòf7ëp²¯­±^øÊ³‘ˆ1 !0XeÑÂß ë…«|"a2K¥ò+ à Hå]=Ò>î®Ä†Þáƒ,³/$lÀ!ÚBé)Yh´õŸŸ±X|æ`üÖÐÖWòáÂgùáKT¥™“×[7R÷I4m®áò3^©çmN¯MúûŠõ[Üó^L z>fîþ·Î˜_+Ùp¶¹ýç—SI|‘d0Áý8õãlz>(C’ØÊèzª\€>“ÃL$°5i²ZÍ&Å¡-¤Zü“ó ©e2>¯Ä©”ÇJ‘ˆ,)ePpð’œeŒÇ› ƒ“9ëtx™¶HÛ æ˜“•LûŒÐˆìåÃìMMZºM-_>bè{O‡q«2PÅÂV:Éιp–S—Ž ÎÀk=¯±¡+φ/7b?)CÜÀGùC)+ê~B›ú¡Óþãg›÷\£ž¥y‚O~c!ÃçÄŒ¸æ¾¶Y—SABŒ[¦-y^;ÔüȘY4ÞŽh†óÔêMp·ÙEKŒd‘pŠ»äå–… Ü×Ô…ìEüEá§ÐCO¸85qÒ‹-ªÛlN[ÒFÛ&“ä ÃrÚ$оm¹Aqf‹-8CÞ\’ÚB1SPs{¶z5¿çäA·'KA(i{ña$Ï»\¼‹ƒ€É;8ýÎ,Cg³!œå{áÕ⢲I¶•}Š¥Ùp pA³jS9˜â–q]Ü~NǽŸBc& å<²)BŸïÈï1ˆnmJ& áÃß•×îŸ$®ûnd(ÙF,|ŠS¯vøêé[sMÜ^$GÖM0…UÚVAMxEdm+¤õì§Ù_XÃÀ€îDÉr:¡>R.”y~F?!<â饶 Û<&@9©UÂmÂS¯„CBN0uQOSû(Ú¤3¹½:¯»œŠéÊÝež]ƒ{¼n¼{¦n&?Ë=KœU~\ª[ì¾Ðs¡xaù5º+Ýw wz¥6é~éîòôP;u½î§=ωϕ¿&¼êùÐçù/áˆ'nüBœŠ qÏqMùfa§ðŠþþáOðOž¯©Â×VÃÙƒ " Q½EM,@‘"j„>†º"û#ôòHg„Âk*ÙHðÕJ_½EÍ#y£¬§˜éÏÍð)µ¦q–óFµVòPk4*KJ’g­H¢wÁYLPk 8kig-ᬥ"œµ”ÇY‘¹}M‡±+VÃ:0BzºŽ)Ëʾ¬äÊÚ Y«,I6›Õ°Ì ½/‰/£—‚õ¢šÊˆjy<-ªÑ2T•Q%úPå`ÓbV=¯–ï„€õMªG˜A©Õi ?Gáç(Õɦ©^ø˜jÓK繡û%^·žÏ걫+•ÁMwCcšœÆµSôoH‹þiÑûI‹þnUNð¤õª;³J›žÂxlJÿüÄŠ8櫎ŽÁ¹û(Æ\wøò›c@vG?þ ¾ ¼­M…%¢ÍÇNÉöÿ"$û'GÇŠ§^;õb—]ð8ô”›D“Î9¤ÐÀ•²‘¦Ëèa¸ÔbÈõஞ7,ÞÞ»xK 3㧸Zº¡{aïmK°wú¬ô–C*ÐqèßÿuO1—.Bòv âÒ6êvuCˆ q×ÀÎd)?ö±„”óà%Ü2yYø¼¶—àKÎ7¹7å߆[óbúÅ6‡ xÁÝ ý]ðj©–4H5ZKԚʬ#‹¥+Ëe嬔õUgk²‘l8[1*Û–ÍdÓÙ¬Z€S—UU•µÎÖ§{aÕ6©ížV'^,òcXµ, V«C«ïqè—¡¡áSƒîw‡ï)ãÈsò=e³%ɼA_"ŽfSaÈ>Ùƒ›²á#ƒI‹ÖJaÖNÿG½Î#|u¾õï0t5©ôkª €éׇ¦7çÓ_usaÜ~„}[¨ýÃVSË)k5ì¬Ã íÄ H›Aos12:ˆ‘ÚÊà»aÖÚÔßÃzmŽtmïÀŸ¶¢V›üµ5 <û#ÕÌYZÙ …k­ÅÛVL@,#xZX4e¶´ r­Wmõ¶⪭ÞïDG¨jÃ;dA\ÉL‰Ô’v ª†ý-N¬w×`Eµ\¾mëØÝíä±ç{·jCá&Tɸ:mø8VÆaðÏ Ýù|ê§tÂT\]Ê;•þ7Ì7å¶çv’ ,÷yÐçp•ÂÕ¹'".tÿc<Ÿ-„~X²³ÐÇøn¾œ»Í(ØòËQ¹W5_§M0"Ãt¼‰ÜÁ>šÏ!«q•U0!®Ú»Nw7âªø~?ŸõùGSXõ‰d+ª³ñx…§©åÁÃÁcØ2MOÅ5N½¤_®?¬?¦7èÅÚŠDŠk[_t¬p~‚Ä_ÁÉP¤üä‘–8S·>/ÿÉ‚ë÷Uˆ §ÎáOMÎ&“¶×ª ;Yvx [•©‹sW‹AŸÍ-˜€p&œ¶"1àä«ú?û {$\‘læln3ÎS¨g´!„×ÇËè£ÐE£ÉþKeìØE Mv†zaà+`ø0@‡íc’¤EQ ™Ñ®¤Ãår:Á)Ž’lvÞf³Û¬” 6Ê í xÖ+Y¬ ìÐeL+³ ûëD¡cÞdÒ{y‘‹®=w>2¸czãÐ~?HÔiéÍ(mÑœÂÓ8W¤E µïmEòª ¢NÞÈdø®&$î=ݰÑ(gààN'ô;ý7Q oѨ•ý_iÆÝÄþf²i垉ԋ+ñÁ«ùȆGtŸ‚jpwÏî€B‘}.˜”€’â˜^„•HË9i‡>1GmEF\/åí’iŽê@Gª $(뫲öéô¤ó÷ù’UY–ú€Ïé£|bÍ7Ûáq†nBjý/&“H!ñúŽŠ¨é@íàÕ)²¦¸Â…³¤²ÚyÐ`†Åá¸dØ8¸ˆ–ãÜð‹Î3ª:ÄØ|â”̸QµA¤G^9Ýë³™¥š‘åðo¥JeKncãzÚlER«f̸¸~¢Žx—@ë&ÖÃÅçþØËqV†ÖO¨ÏÝÝÚŽwémÊõSëP_Ù€¼§ZÕf0 "F“hÏÇœ4µ“m€‰ò;AkŸZó @^Ý–oÖàÎ×_…D-Ù3!÷mžTW…¯V^Wþ¬ÐO(0ˆVâô÷}ª”(¨ áÊCTÃÊ´o‚Z²¯•Œ‚ø :œ–Ì¡¶ í@2"šá "ÍâsTL=µD£YžP¾AÒ!)Hö<õ,¨…;áÞüvöµ´æT’¡–lEX ۠Ɖ¶•°´@¯Á-<4jjÉ8 …\ž$D‚êjYD?ÉlÅ©éqmµ~ÇŸuåY¢he ôK´ÀÅ t´ÙÆqÞÚÑ swú¹xB¿Ü݈~.Îj¦õˆ˜¶NÆ==éq‹u·#}ÿÍm  b'~¯jq¿Cœø5\z>·××ß›­¯‘­Ig›ÆLÜIõ‚ñ`)‹Žã >ÞÔOdqZIµOj¦Ç3Mééí“©v“Inw⤬~?™Ü>}zE{¼=a9vÌèt-Å2gúƒæàA_ÙÁps/µmëøë'¡qÑ#eR“à¤^ø j c63‘°Ota[V•2Yô¨*¦0q}kjJj^jYJ‡¨”8£°“ÅŠvdbÇã…ͯr¼#~ä¨óˆVpD†áàš|Ò„d‰`n*Þsxp:2}Ð÷ ”äï-ˆe÷°•‰LaÎÉ ªGÿ,¾­0[!žu.—û¢›LGcpÝûÂír¹¡Ckÿ–{²_&óØ™°ÖOÃGG°•Êmþ¿<à´ÛØ· +Ào±6»ó¤©°=ðVß+÷÷ãùiÈ—ÛÁ'ðø ÕO¿C¿£«Ô]¯_ ÿþ ƒÅPmXcü¥©Ò”3`VZdk³õ¶'í¿°qŒpÜïøØ9Á9Ç­v­â[ÝF÷Aá>ÏO½»Ä9>;*?ò¿Ø_’ îý,Ê+ç‡ëÂu‘¿”."åQTþXúÇ2di!»vlME¦¢/þï‰s*ϯ|³ª=ùUjRõ®šýµ¤¯Í\P—¬_Óo8ÚøŸÙGš§¶\Ñêj}¼µO½ñ¯Œüð‡òCù¡üP~(?”Êå‡R\ˆ>I‘ÝäN肈 P:0L:sì”YãÆÓ!ùì™cØ‰Ó¦Ž¶Ì˜à°Í¶ž[_/‹'Ï•ÎÐékFdåó"g•˜î<¯£ýœ9îꆤ‹øüNÁëGUTší-*Ž¥êÒµÙéàÿμNjîŸc3P qÎu¨6€IàL0L³À8dÒ dp6˜ ÆLÓÀT0XÀ 08€ ÌVp.¨¥ Ê@Ád0Hà ô×ô Œå`ˆ€³@ 0;Áy ´ƒsÀàÕHçO¢Sø€8¼ÀD0 T€J`vÐTÀ0ˆ!˳¤A-È‚éäsˆÎùÔÚ–]¾ò¢E+¥É‹®ä.€kÑgø¾?¦“Oc']€ù|\H € ØIJä¨X >kaì€o€OÀÇàØ^ï€ÝoƒO¡ ¾m¾ÜÊ»¨?g‚UàÔÓ‚N°½c*èûWÅ ½fíà6Ôog¡žŠ ÕL5Ã&ØD>ÊZÔ?›Àuèï‚#м¶]èÓ¸ÁÅ`º×‰îîCÕÔ_ è¿nG᪠Þñ¿Wз¥ÿI¿ã~¥>¹à™§ŸÚ1ÏÑô¥É¬â¡è™çã¶çµÇÿ>Ð÷ÍZ½ÏG§æþ‹ñ? endstream endobj 96 0 obj <> endobj 194 0 obj <>stream xœí|y|”Õõ÷¹÷Ùæ™}ß²ÌL&™I2‰ É„ˆäIH"°šSÂND%ˆÚ QQ ¸à¾K´ Tm™L–µ­v±b­­úkk^‹Å-?i‹ˆJfÞsŸIPZÛÏï÷þÞ?Þ÷óqžÜ{îö½Ë¹çž{Î3I€€:ƒæ³KÊ@ýLÿFó–\±¨#¿ø}rõ’«×ù?ñÝó+,ø€4~yÇŠ+4ïÌи„߯¸üÚåéöÙOŠV.[´ô×ïv4¯Ä±+±ÀºÑšÂOb>wåë®ï·ØÿºËW/Y”Î×Ã6Î+]Óa¸ÉÊíXè¿rÑËFÚ·a”Ù±zíºt¾y2«ï¸jYGm`)k0¯B¦vC&‚L€ÔñÑlOguŒÒqô¬tù$àYø=É'~è%_€ ÎS€‡ÏS{aî;ÌûˆrÁ sa á±Mn%§®N}Â]ðDê9rcêi¬¿~gpâ TÂtl?–ÁÜ{K=Ø:˜³ˆÁïðùçp7Ü?&ßKÁQíp#öW µP›z>u áV~‡ð¦¼î„CDL-IµC6ä@¤~—zBƒïó8§à'CVÁÍðñp?ÃÔ½ð$$‰ž¶r“„£8Ò˜WÂè‚§áÄJš…7…“©ï¦N€6ÈÇ9µÃ¤‚L£OñúÔÄÔÛ°úáe\/{øünaA²&õhêpÀsDK“ç…2áöáR§~zœÏäÈtg1ÜÏÃÏá¯ð7º)µ &Ãlù§$‹øI9þ;ê¡éFîu¸WÛŠ³];!Ž;rÁäÍÀ ¼Gì$ƒL%‹ÉäoTO—ÒW¹‡¹}ÜoyÂÿù„<äÑ:x À¯àx•Ø)i&—‘Õä~ò(¤qú1ýŒ×ð7ñ_òÃB(9˜ü25=õ)¸Á Ãu° yû}è…}ðkxþ‡ÓÄLÆ‘•äq'ƒäc*Ó:ƒvÐûèSô‡ÜtîNîy¾‚¯ãWñ¯ðo ·Û¥ERòì®äÝÉ&_K=—z eLjý‡ 9zJÅSp^ÇÞß‚?»L~°ÿ d>ù޲–l%÷’Ÿ’×ȇ¸JPŸ:Ö㨫éUȧéÝôýU|ŽÑ·ééGôSNàr¸±Üîq.ÎõqǸ¿ðf>Ä_ÀágðóùîL™p‘0[Ø#<#¼ œ«Å¥b‡ø¾t£´Yó«áÂá?%!¹2Oö¢ìjP’®CN<O ÜïÃ=ørô×8ãA8…»à%ÆyW‘FÒD¦‘KÈ¥d¹‘l!w‘ÈÃä ò#\®J8÷­¥³é"ºŒn¦[èmt>éÏéïè›tgîâ‚\„ÃMáæs ¸+q ë¸ÜfäìÜÓÜ«ÜëÜ î}nwÍÅgóëùëøùÝü>þ5ábá |žŽ ÂkÂYá¬HE¯˜)–ˆ—‰{Äw%Q+5KÛ¤ßJ×tLRˆ3÷Ã×>Ôƒg0›>Míü&2„Y„®<‚û0OÅß¡†Kâ¾Y=ÎÍA=¼!E…#~9ä§°I¤jU~ät‘^o6âáwsW ¿ xµÑz˜"u°VÓyôÈ{d¼‡ò~ ÜCV‘µð "ãÉõ¤’l‚ßR'7›l†êÔ”'2™BNÎnà—Âwàß~Hjë’ñþ{¨Ÿúà>ÜÑgáòø‚©Q»q¨¡–¹åýf`Z¯ÏÙ&<Ô —‹¯Â>"¢Æ¯'ò×ÁIø>¢DÕ¡&=‘lçãÿœªLã ÃS{ðÜ­„‹ðļ‡Rró,w)žt-ê’2<ÕÍ0–Âõ¨õîLÅS¤nJ]›Z ¿D줈|AºñDô!¢^Æçx‹lÇsxÑ¿_ç¿ú$—Â|HÜ$”áy®vO û„ ¯ˆcÛ›áa”èwQ𵏂%ð|Ÿ îŠ Šó‡soËiŒ;“ˆ:ðÌæ£¯YÉZìåFäÞ#xžàÙ8‰zâRø1¼I(qኖàøì§ ù¼[)ôbÉRÔÚ…ð®ÛHÆÑu8ž‚=݇Zkçôø r;¥Î«õB=™‡}}—ÀRa,4“ÜP…šµžûò;—˜¡Žä'׆'ÔYP%ü™P(JNO£íܼcRXÞ·W\HÖà,L¸ŽapP‘œ…sx@©£ÔL¼°zÂøªq•Ñò²1¥%E òá¼Ü`NÀïËÎÊÌðzÜ.§Ãn³ZÌ&£A¯ÓÊIxŽ(j6¶ùã¡¶8 Nž\ÌòÁEX°èkmq?5žß&îoS›ùÏo©`ËåÿÐRI·Tε$f5Tù‚þø+õA™?³Ó·Õcþøšž¦¦w¨i¦øÜ+ëýqÒæoˆ7^½²«¡­»ëÑi''-ÓAV‡I¦â®`GqM$j‚ºÆ÷PÐpRqo°¾!î Ö³Ĺ¼†EKãÍ3[ê3XqQœLZ\‡`]ÜQ›À$u˜¸8).©ÃøÛÙj`»¿§h ëÖ>3,n‹è——.º´%Î-б1,·>îºî¸û«,vnÔ²åëµ\Wƒ»Ýϲ]][üñ™-_¯ °8Ã>KóÛºqè[‘‰M³ý8½9Ö'7ã~¶¶ªôú–XIÛeþ¸¬ ®ìº¬ ·ÆÛ‡Y×^¯ÒŸoƒ¿kNK0¯ÉÆÕgöØ¡kÖµ½Åï9¿¦¸¨ÇlI3¶ÇhIè _O,;W§¦Ôæ,Õ4ëg ›Qp DÜ¿Ä3i âšÆ±hÙ8èZ2›á'F_Š;Ò—'µu™Ç³r† yæ ¿ëS@ }|~É¢‘1Ïü)°$““s¢†õ£éx$/,d""MÂ=Å9NTóÅEW÷ѱÁ³ ²š‘·‹bãKýÛàí} ,ÆL¼sfK:ï‡Å PJ"±8mc5£5޹¬¦s´æ¼-ˆ’¼˜QïˆkBç~Lf§­aåø8qþ›êeéú¦ÙÁ¦™ó[ü ]m#¼mšs^.]?î\ÝH*n›ÔÂeБÍàÔZÊKÏ5f™}œÏÃQê¥q…R- þƸ¹mr:Ži‰é“4_õ¥N2”J¾‚Ì2>>r~~Âyùóf§ïâp¾|ˆ6Í™ßÕ¥=¯®PWWcÐߨÕÖµ¨/Õ¹8è7»úénº»«£¡mtCûR·gÄoá"V’ñ(¬êz‚dëÌ…l=¿¥ßŒžÊÖ9- J褶ºXO.Öµô£)¢¨¥ô\)ËùYš z‚jÔªŒ~ S­åÕ5¿¤€Z¦-#°¤¦ËÌj~Ší½419&™á‹/’!³ZrÞ§••ˆ]xWÃjô(˜¡o.§§Rh+ÐÃî!0¾Ô÷@¯Ù^¦ôqöšleJ­™»š1PˆsÓ`…Õܰ ÅæM‰â1eý,Ñ«5–™±ývðcèÄÀA7ÆDÍ+Xûí½6'ëþ¦„ɢ⾛(¦½fwYs­»·Œ»~‹Wâ•êã– ÍBº˜[Šî-›§Òk2—uâx5ؼm§¬®åœh‘ø¸z΋·!k¶>aL³>‘_XV«å&qnµ‰‰3 1àã4œ”(óùqŒÅ ·µWÖ±ùmM˜eG¸›9 5׉­\>ÓN %ØJæôʆ²µzn.s²Å‡s$°SîÊv„ã5p™èÀø¸U\:S>®‘ËN8|‡¸»Õfw±^p¼‰ M9#½cÙ@­ÌMÄÚ8w;rüvu´½¡qhk…¸|(Å@‘©›0µ‰m'×…©.ܦ.Üš.Üš.œEn5pÛ°f¶)ᮃnìÀ°ÓÚ›0²)Ä#{"#?J@:×\â;LDŸA/ÖG˜y±tOo(×g:„ýt×%²|ÖZ-}œ´Sب U¤`¥O$*Y';‡ý¾~ºƒîPÜ•JžR¬ìâJóJ‹Kwqþ<±¿Ò¿Ë_k¦·ƒ€ÌÃK·c\ ~ŠÒƒAÁ°ƒnKð•ñÚa\[…NŒ»ÕTÆj &0Ÿ«=©¦jèÍ0Å>6bØ„¡à è ì ×aø.†ïa¸^-Y‡a=† ¨>:шDt¨ˆDt ¢*¢C}=†hCD"ÚѦ"Úцˆ6D´©6ß6D´©ˆfD4#¢Í*¢͈hFD³ŠhFD3"šU„‚ "¡ BA„‚EE(ˆP¡¨ˆRD”"¢¥*¢¥ˆ(ED©Š(ED)"JU„~DøáW~DøáG„_EøáG„_E˜aF„faF„fD˜U„YÝŸõbƒˆDÄ ŠDÄ "1¨"1ˆˆAº¡‡;VûS„CÈ1„S!Çr !ÇrL…CÈ1„Yú:•Åf#†M:10ìb;€Ø; Š×z GDqDÄUDqDÄWqDÄW݈èFD7"ºUD7"ºшnÑ­ îz ñßÊÿöÖÐH‹/WÚI Tº >VéFxS¥×CJ¿»Tú]¸Q¥×A¥J7@H¥ØŸJ×OC¾JS­UÀ 1¬Æ°Ã^ G1HjêU ï`HÑ %‡7I3¤Ò^é¨$ì•%jgˆ;ŽâQQØ+ŠÔ_›A ªEÕw¨ñ&Œ?Á€—Æ5jª†FqÜ(êÙ |¢4ªX†üŸ’W ÉÑB²·ÜQHjezáUMç‡Jt }¤Eч&úÞÄP ODÍtû]¾Dh¬¯N“%‚ôc =va¸C%†2 Åò0øÔ²BlߢäŒtyCCƒŸ N'š?V‹Fé§²«÷§Ù8á|ÄJ„K‘ô%Â3<—/öÕÊä„™DöãÎ=ƒtoÂw«˜&Ï&|‡ìIø¢HZá ,H„_ñÕÈ\ðñ :g„ÎÆu3:+ᛇÍf&|H"‰pˆµ.Äò°¶€´Àq¤y#¨ÜôHÁ„o’œ„¯ŠµÖ@˜m<¡Xž€Q®'ôI?iቢó ùîö}Œð±(oùûx$¯æõ‘yŠÖw¸ø1l\ëKÔjY{¼zFhœÑý¾]yÛ|c_$ï€ïAß¾Û‹û4X|Î{›:DÂw#:;Ï(6_§¯Ô·®ø¸o­oªo‘o–¯5˾K}‡Ù4!FZè3|ÍØá\E^ÂwQ^Ÿ:ÅFßµ>ÅöUù3þ¸t¿•Ň ,=zò·0¯ÉøÜÊ>bQ ¥“ÒiT'M‚RŽ”-eIvUcÖ5zV£Ñˆ^C5 ±÷¥•3‚í¢j ‹<‹y5m¦,¦ ÚÈ”h(L…¸k¢M³ëHS|` 4-öÇOÏö-úB°ŽÄ­MÐ4§.>.ÒÔ'¥fÅ+#Mq©yAK!·Ç°4N·¢¥>§¥¤XÑÍÌiï!pómý@ˆçæÛb1p;¯®q×X'Zªë¿!j‰#_}Ü_OfÅïkšÝ:+/c‰TV¬)~séû©‰êû©‘‘XK?ßAM ³X9ßQÃfÇÕf(ÍFlaF°™¦ü¬ê“:Ö ÷(Ý.„pl`Ûi RÛ…´µOX»ž7ý õ=~¿Ú&àMµÍ›yðµ6(1ˆ­ï …ÔVA?ia­HKЯN¬@íÈçÃ&Å>µ A»NíÈGÔÁâ%_5ÉiRq®I…:G¾jãK·±ç¶±çc›Èÿð³¬.BzÇ¬ßø"{KÒlX†¡-¾ýê•îxçb¿¿gãú‘×'¡¶ÅKV2ºhY|}pY}|c°Þß3æÅo¨~‘U Ö÷À‹ sZz^T–Õ'Æ(c‚‹êc½5Õ-µçµíÜX-ÕßÐY5묅USû Õµ¬º†UËÆªecÕ(5êX íLî›[z4PC¯\¥½T§EnËÄêœæŽ‰L û'Ü3ò@ö€.‹ëƒuqVU\[\˪ðœ±*#{6RåÞ8!qì©2c±%X£¬Ö¨)^1³)@Oš‰J\YôÍ{¶–}Ôj74´×ãæ×©Ÿ¯·„µßøY÷MŸõëׯeÑúÈZ€¦xáì¦øØ™8I¡ÚêcXvÁhÇ©e=²ÜЗÀÊN‚¬cñT„DƒŠ½.‰v‹Ýe®Âº^oVÙê#xƒo€~Ý(Qýeº¡7'ù/ëzK*ÒýSFÞ@ŽÐ[‰PFóÒT±cbGÞŽâ•ÝyÝÅÝ•"–Ø……¾]ì*M”ìâ`]dí(#0¹.†ÌÆi±ñOdf©w³D$‹¬%*¿þ™Ùd”éç»v¤×µj÷ëF7$]¾ÒÓ•‘õ£ õ#µr½ QDÕ‹*XÀ­) êöQ’¥>Z£Ø@à“h%>IÀ£…$å“È$NÜàŽ˜OWWO7Ÿªž6\ 5˜6ŸÅhLiÀ°äa„ŠÎú¹³Š_‚Ÿ`š~Ur&])¼fhTŒù¦ÝÕÈd3X5GHȨ¦spN÷(Zùïú‡ý|)Où>z_¯å©UlÄÖ¡áSCæ!¨©1W›q,ÒJ‚!Za¶­,§Ôa·ºœtÙóv/™·y`ÛŠ +‚É™'Èß> B$_K^òŸO&÷<¼œÍdÎDQg2Eq‡iX»‚®ÐÞOwÓ=FIÖ˜¬f6'À«sÚ§ù»ð°žÍÆzÙ$6›¡áãçOÆ6‘«ˆR®ÜiuØ%Ê5Ì®Ÿ¹|ÛÑûw×5=›œ™øñ™wÖÿ'ù)ù}2ûÌkŸ$O%¿d3¹£g‰ùŸ«8è8ÐÒ ï_?ºÌ?‚hЬQVJ^M¦åôN͘œ9%ï?ÌïX䱞FÏ%¡åž¡[Bwyîöîòög¼ä}9C/ЇSô8Ãb#æÙ@o¡»ÄýâÏDýÑè[fš•[6ÆRdÈU"Ds•œ|ŒÈ&XÓJZ'µ(ºåârírçrw{¦ÐƒÖ;æÈ:«Å Ȱ°Ãfw!‡Æ¦Y¶…xnL¼L÷/èQ¬Ñ)×¶Þ´yŲ[„ƒÃ'ïIžH~ž<™|{AìZøÔŒŽÏxüQv¦çâÚkð$xà)3[L1k̹ÒÔnmw^ï¾Ös?½_ÿ3óÏÜ¿7ÿÎýøæÛŽ3¢mœmœcªuª³ÑÓ·ë¥ñÖJg¥›Û l0mn1móì±îvö[8e£*¡QF÷[íQc¹•x²£*5Y¢†ƒè€j‘gV‹l ¶ƒò(§уâ±Êï’+%(1°„!0ÃHŒÞ )`÷x[j¿Òa­Ó†"§†"P3|ªõ8Jìð©H)2…y*„ŸT©[)0¡ä$Š"?&ù‘qÉŒöë7­j^î öÈ©W>H~DœC/¼G?.›=çΧ<²`uÉ_ !‰äífZdònшÜìPŠ­11¦YÓÒòŠÆYîÈî̦㹨~¼#ê™ÊÕë§:ê=ʲ]“Ũ“Œ&Ü ­«Àh&)&xï`²Ðx²ZªÏ­pÍé´Ä¨š‘I [® eÅÐ.¶kÛ­ii[c@ÅÈ­åe.¼y¿.*ü¢ä—µ=óŸK~™|!q#ñ [Kê¯[´uóŠ¥[Y#a¢!F⹇šÏv<}ñ•O=ùÜã;q½µ¸Þ0ÊŠ2É÷ûÁŒç¤QWõ üá>óa·ö|ÈÐçÕhìd2½HlÔÎÈÞc8 ð¾¤}Yÿ;í›ú3ÒgC¦)Ó¡ †p(FKÔä8êxÕÁ9TiÈ®Q©Ñ…”Þ¦èMFk³±ÍHn+a†”'#JÊ­ÀÚdù£*Í)HÓHqšº3Uª˜Pv3ÒŒÓ^hµ2ã×Y݌ݹ: ¤Ä‘¢’ì…Ù«³wfóÙ¦€F1˜¢ÈðmaoeBu ç3ín%ß^ãV²M¡ v3]ÍL¦XÍ0³âPw ôb +› 6²Ž¨jF£MQͪv— ¬°V±I'\ŒÄ{eíD5[¨QM¿Øq¦A[Õá rÉÈ5²á 2Kµíb%Õ¨œ¯ŠDª‰¥œ]—kP[&âþp¨‚É8p';’è¢_÷Øö&?º¹Ø_"VqXán\T7?Ì]3ïÒêjBf•<ôøþ;ÿˆ²I¾”¥l,O y¿Ùo‰ñnAÃuS‡ÓBíV§Åh3Ùhc®½]Ö˜td¡.¥£:¶Z‘XLN’r'Ëf³7'Ù ›]+—×hfhš5œ&ß\bYh¡–>Â+£-Dí ¡Û9à¤N&²>êô¸®é§íÞ3T©Ìâ<ÛŠF„ç8¸ñ˜´®©ÆPƒQU™ ?#÷­œÝ8x8$U+8ÊAT¯A÷#U®¿fmhÒÄ +~ó›ä‰GøPó-›gçþÄ\5³égŸã¦¨g?9“oS-ˆ2]Y¼!kKµê cn1tŽáý$Hƒ\))§åœB&ÑIÜSÌË›W0·j•éŒåŒÍ:ÁPîœ_^Ôd¨w6å×Ô»´·ã­Ót…zCØèt9Š z—“wç²°_=ª -ªôêôiš_˜>Á¼4MÙ‘¡^ü ¦p|¦0#Fm1c¸Î!¹=ba.äu3¥#{<^ïcÈTA}è'•笞ÒsÚçÔˆþ1™‡^Vç®J»£÷?¨“SOàæ¨âK,V”mâ*$yôŠ[£ê-S»½=oEÁòH{‰Èn9—àtÞû¨ÂFØU°Ø4èGCÁfÿJ—]Kj5Yùó®¬Ì³6üîúÅ„ýi'‘&vº#ù·wÏÞÔ¶âö­+—ÝÔçÈ8Ç¿óð³ûïxƒèˆ÷‡÷ž½èðÁ˪ûo7Ò›~ðèã=Õý(2ë.´sc¨×P"&â#Ul#Íu¤Îò'ò9‘%Á)äÒËJ‹@µÙ-Vš×ÄĘšÅI²VkwhèRkCYñçF÷Ê$%ÙŒ[âÌÉîpw»i‡û¤›~âF¿Ér:Tµ…m»䤃8<®š4ã×\aÎj"LÉ©úý‹š!ä©K5¯4ª•Œ·3²©E9ª^w"K’g¶YôÈŒ¬ä ÿÌ ¯,Ož@³à½“;¶Þ1|'³{~Eý¶[†?ÆE£l«¾&™ï·¡dœYE[£ÈÍ2í”ãò€|LþD|r›¼IîÆ%t 9¼Å8ƒˆlE›HD‰×R ïLU¹QÞ£Y×Wë¨Q''˜ÙŠÒFâU›tÚ©Iž þá“g¿œÊ‡¾|whîÐBœ¡þÞ\ê½K ÇÆ¸ÞS•83gÃòrq¯ö¨öeù—Ú·µÚÙ\G ’[n/Ñ\- äwø!þ,ÿ©(L—¦k–‹×ó·òó‰Ii´>Þ*FøˆP(J…šCß$hÑ&•µ²F+heNäu/2GY§ÓHZN«Õ¡Sz…âJ4U>‰HË T"@|8a¾æ»#&6[·Ç|zOó!½“øà)Ù¢AçKS=zš¸ÔË 9…HŒmn+jõ«Ð¢fV z¯ø#Y¶¡Ó7…ÌOÞKnFWöÓ›„ƒgO“«“ßþùã¶ä³£ž¤º›³ûA@°½šÚ)Ä…á˜ð‰ ø„6a“ÐBÚID›•À计?õO»6²O#ާpð‹Fk#€øjÅ0™ÐˆnűðÒ;D§>ÊE5Qw4XO4 îú ÞÏ•Ì–Û : v<)î–vé÷‹ûõñ‚cƒF(()hÆŠ£ïˆŠ73ZƒùNµR¼äÍb×FB+ÔÛƒ—ÌK8#33Ö¢è™Ì!«E™_Ñf!«Qúh£bòf„²2±lu&iË$™X¶// 3‹+V¹†Qe,Î;ŒMÃJ-†j ¹áhXa´$üjø0g ûÂaÂþpi8æÃžü?W:Q#¯WÒº²ú4Þ÷x%^ÓÊÈèÑ5«Ç·f•£ª‘ŸWEصD"¶€ƒùG.ÕKr9Õ£>w”¿:Õ ·}`ù}¥O\ºþ‰|<ÛYá™V^<‘]3¶veqòºósæÎ³ðÒú†ctácTOÞ~_’ÒÆ‡ç5n~pø,îÙLÛáž9a§â–l.Û|ÍJ ßÇÜ-s½¦ÞôYUÕf‘ŒQ¯Ó¡©JIÈ ªjC;ùWªM« 錿ƒþœ†Ó““x˯áTNý“’KŒQ+7pžJS™„ŠŽ%Oäάš².‚ŠBØþzëC3|4ûÙeãš7'’>>ôȾI+7—éµYh¿>„+5 ·s¿2ù}rBó™í3ÿ}_ Và‘iÌ<Ï6ÏsßOÐܯï“ß ÿ!üA~CB8!¾o0ïÖü’þJ|Qó3½°^³Mܬá,ªê\ŒEv^²WIÞ¶ŒŽ ša ÀyîIÚÉKí£·ŸÜn^Ž6{»›'ìê#­¶¨—;:x¹¡¼¯Ýs³º†ù+‰&þñ]ÉϺˆÿ¾+¯¼÷Þ+¯¼æÜJÄ®äKŸü5ùâæÔžÇöìé~d϶ÞíÉËùûq½fôOR.g›l£Ö(We¨²E3ê¹)†)¶úŒÏ3dæãŽú-§¥Ï34x~¾îÏ:u:³É8êÏZ ŒFSÈlVÝ?z´Ó†ªq#ÍÇÿɧUï&vß3Ÿök~ Úa¸“lÍ#N-sU¾Zõv"–ÿè²~B“gû[î˜[ì¼}ùâoY²b+nmóÒ䟒ÃÉÓÉ·çÀõ÷>óhïî'˜¯²×¾×n,xT©´VÓ¨!j¯ÎœJë õö©™šÉÒ8\јÓ^b˜g‹¹bÞyY»´»2Ïȧ ŸÙõ0f0&ð:GÚ©—LfÑY¶µ=ÓÅ¢:õòfböúÒfÒ鯭ÿÔ?,?²f„íB»v¹­ÝÕîYž… Q5rÒž(³rHô+7•›RùäÂýë»7pÙÃÕ„Kž¼yéòm›-º+y9u^4{ëNb&xÇÌ_ðèܾïï|"¾÷á1 } W©îþ%ÿ~ÈF2[X.¬¸k‹q¥±ÃÊke“Þ§§wèSzZ£Ÿ¡§ú>ºA)$<áµù ›åR¹Cæeï&ëN+]hÝdÝk=få­fN•J;I7¡Äc©é'™0úbãÜ>Ýê™–6Ä‘x¾«ÊÒ°šâ®ÙìÛ…ù-=Ú²q( õTŸ3ÉE éfgzÒªú¶Ø%]8aV ºU}ŧÔ>ü+®±O´×XH_PD‹Ô„]WðëöûÃ÷Ê’½ÑN­‡ ýÆ—ïÏN爆¹†e†{u÷[wçôë¥Ú ’[Z‘³4´ÅºÅ~KÎM¹re¨AlÔM5Ì05êr¤œÜp¨R_¨È©VäJ¢V°È·!¬ÏÉÉ J¹9JÑZý5ökW¬/ÜêØ\øãÞÂ}9û‚†Nr‡ëV÷ƒ…?(Œ‰®€S £N%Óõ9É;èô”kÍywäÑ<ÅÍó²×yŠ ïæ"RZDJŠHQv …«œ`änR)6Iß̲oæÈ5}Œågñ¾QßÝèÐÈ–ÛhÒÆ„R!"' åŒ 4æ˜k)iw&Z⢼7Cóm=Í÷.ä ߘ¯köo£MB¯ ˜?Z×dôCNê—Ìçô¥iN_j°7;—å{}¹é¼Ç«æ• L¬2±99îÉùIÎosÄ@ŽÞÀó^ñj œù7½®â2â«ùœ¼(£JÞþ@J‰Bš ßF:ÉIÂÊ}3i#¼ÚÒæÄ–„(Ó€' ù“åü´¶ªŽ}nê犬³Ö˜ò1B>||ÀP¥·ë«X2¡¯B}Ø£«‚‘osbx#ØòœªÛS—}…½S¦„´Ãî°£È~óŸ½*-%^ë•K®¨Ì³;¦$Ÿ]°ñí÷Þþm~ò3Ë–եþÌy>Örê“·†IIdÖÜüÌ¿Ãniš8ïÁ®Ã·o3±Îç f;2—Omºå®ßÄýRéûôNáQ¼_Q ü€Î«¶À4Þ8Õ3I¸9§\V›¸¬ÔNÜœ,i%½›±Û®nWÜŵ!pq.tÒÂ.^p°oŒÖ)F½N.Ñ–úÉ QK07>ßÍ…\Ö¹ŽûNû^;×fï´ï°³Ÿ´ `7ÛýöR;o÷x¯é5§šâ•¨'& žè{j€}Mp6ý-ù”êã©ß4aÓãhHYÊG|üV‚½]å©‹1-„,µ+Ê+ò,ôº]83<Õ½ø{_W¥“o¸xùÐ`rΑ̌· Ëg6Œ¹—¼:øú“ÉmÈŸÛPËÌæCh!=¢¸.±¬°Ü'p²è«iµ¥‰6YNPIõý,¼Î Z‡Ý®•E›=äpSF§j'¥_tü;IÖœ34䤆hþµ ˜¾dþÁ>jM¿ð á"éeË’ÜôñGÚW=}1ñøfÕL¾ªxvÎ]ü§ï£ÝI÷ಠ3Ö'èTá:uh ÎÇuêH†âò½%Q‰E"‹4,BëÍ^¤ª;ç÷Ž>Ä‘Ói4Z½}Vjå¼²W›ź—tz<Û'g–?ªAg. uQ¯ÛrZ%íÓƒ^íK'»¢<™ˆ …ššjÜFöÖ ª*C±ê@Ëë´²L)1-W±wÇŠ;3?ª3ø ¥ÅÀ\.¯Y[£nX-Ut<­Òñ5ü žãÒR4Q;“¾ˆUG<úŸ ly˜pEÜÓ†Zñ¦jõLoXVÿ5¯ZèÌ<·Vœ‚z´#­ì}šzÔ$`s±/*lè‚=—œCÂ/w‰Fó/H ‰Ü~wƒ³¸˜f§y*£G4yª§ae rV "ÕJ‚œNšÍ[¯d—³µ½Þá"bPWÅU‰“¹ÉâÜ¢úþ\¹¦è"d¡Žç^Öiy}xy§`—=Z‡^„|>,ËùÚ°~ T åF¸ˆ^$L–¦Èà~ƒp|vƒ~ lå·[å­Ú-ú·à-þ á ù-íúáCþ¸p\þP{\ÿ9|ΟÎH§åϵ§õÅB_êuEÎåCÉ}©·Õœ–åô£uÀr¢úR<çêŒF¶XCÐg`õèÆªº:…åt"*q %Hf›Î¼"¦Q™>Tw~ß :^ð÷¥¦õŠZéÅJz?¢8=PÂë9A«“d¨‘$Aày&z- hKŒ5FjD¡ÐÔÊÄ~äù Ã GŒûüÄcøI?ñ¦m¯gÚ°×=<ìõ »ÓBéí¯ñÒÌÕê„Øë+5‹:K” øê«x4ç#À4ÿ>b¨ÂŸIªpÁgPíë=+9‰jŸKÌ &t,78z ¨·³‹˜|ÙØ p‰%ãÄòÒsÄÔóKâH>“üÛsûPÆ&Ó>¾|›>3<¥Ì˜œÉÏB)³‘è>k¾@lìÞuëMQÓ`ŠJ,Y$8±Œ²mðáéEÇœ7茢™‚Mäm”ÇñØë×64:úÈ^Ù‹F˜O÷K³[2rh‡‡Õ/‡Œ$’ß>ß>ß>ß>ß>ß>ß>ß>ß>ß>ß>ÿó'ý~FþÒÝœúq¼DL4×Ï»dò\næE3bºY OÕ·L™m>§É2¿îÒÊÿ³:óÿÓ‡‡KÕ˜gü9™JaLXÌþ ãf¨‡yp L†¹È¹™pÌ€è`4@#\ SA-0fƒ¦Ãh ̇:ì5Í<‚åTý+*ö: j¯j_tyQÝêË—²ñÈökýÿÅæüìI8™:¯`ä?ˆì·¶F­‚ž†U&ýS¨‚»iU*ùÿZàÿ Eÿå°6õŸéS0¿é\¤sÖâÝjúÏpWz­p7–ocTÊ‚X~'†Y¶cXÀe©øRÄù0¦ubÈ,`Ú˜>SÿæÃö†þeEO|ïÁ…¦êO5žôf>ñçjõ¿îõ¿±ßöÅg‡Í q¨ï)Göò­«P> endstream endobj 72 0 obj <> endobj 195 0 obj <>stream xœí|y|TÕÙð9çÞÙ·;k23Ì̽™Ìd™Lf²BB`nHBH"$ì  HØD“€¢¢B\QЂûÖlU¤Zj@-X­u£Òâ¶5´¥¨U õ[…Ì|Ï93Dìûöûý¾ï÷ýñ9'ÏóœížçÜsžíœ@!d@½ˆC-ÍÓ#ň}.©4ëÒËçw§ÊsŸBw_ºêJñ¡ÏVÿ*þ€ê‹EÝ‹/ßumͩ߀òÓ‹—_»(Õ? pÖ’Îù uý¤}Í£ã”/ Ýßð# WÒNK.¿òš4¿û½´¼ëÒù©ò´V„´S.ŸM·a‘ísèÿTŠWÌ¿¼3Ý?PVw×Ê+Såy-´½{Eg÷Ç·ïúú÷#dÈ‚rõr ¡ä@þœXmŸ {b!O ýiH}¡ý8Ê4=€~¸ `Z‡oÃNV{/Úx5º݇02¡µ°‚ÜŠžAyP…Ð,ô ¤¯¡dA¯Cûþä?ÐtÍ`ýs¡îA(¿†o âCíçè NòŸc ÷Z…×âÿâæÁøÂ ²7Ù€¦¡[ÑÕÉgQÉèrt=ºý›pVòŠäa¤Dà]—|"ùš­;P?þ9×ÂßÜ ONGW {Ð.\Èwðoþ%qs²+ù;¤Gw '±K& ÈOÎF£ÐCsÑÛ©·Ç"Ÿ7˜Lü1¹Æ¡ji-p½ý@ÿÀµø T Nú’o'?B*4ž½sœ…'â§I÷.÷ R LTOÏEh1êB+ÐVHÏÀ,OàR\†kI-i'·“ûɫܽü üØ™µèEŒ0󱌛ðtü4þþ¬ÖµÜ óá}kPºµÃûn„zƒÍú0Äf°wáð£x ÞÿD^ãfð“øÏ“‹’· úºX/ å q0 ØßgÑN´žþptÂÜKp Þï&rYÅ•r-ÜÅÜõÜ&î î=~6ÿl¢4ñ÷ä­ÉÇ’/%?Hþ>yÆ3£,FM°Ò3P+ºvînôõô!úûñ|¾ ߇‚ŽŸÅ/áp‚ÈÓ\9w/÷ƒAxID`ÆsÉ"ryмL^% Nǹ9?¬g× kz·Ž{ÞáwÜ?¸òFÞÊù±|'ÿ4ÿ ˜?£¨WLV\­4+ïTnL[Žóö>¸Ž”Âø·þëaÅŸ#¯“0hÄþÿ éüOôž€þŠAÊï€t#úôh6©ÁÇ@’~‚Gã{ðc„#ÐwÚ‚ãžÁ›Ñ ý…èsÀ˜,Á…øv2 ¬áÝd'ú HÆ~ЗzÈï‡ÎDû¹ý¸ý ‰ïB'à]:ˆ-Æ¿Ccðí¸-'yÈ®ÄûAÂà£y¬¸ìíbj{ùûÉßÈýøª'›ÙœïÄóÑœò¶_Œ¶“¾œ¤t"h© zO#J|-Èæ ¶’×Avw€žM­x´w èI5Ì:]‰jðT„ñ?±™ñ í—€fÞóy=¹ðš˜ÜÃàS9¿Qÿ²ÜÖÏ’?@¿À @wa-ú!úºˆ;ÅÛÁcœä=Šº$I,@‡’SÑ[`±îc4 ýo»1 }„èÑäòd)HãþdÌó´ÍTT+¼`ç“åèÕåÇÊ*e‘+V+*¦)š5ŠÑŠ"EžBR8&…–?Áÿ‘?Àÿ‚œ¿ t··ózîc°Ÿ;¸‡¹ \7™‹q… “Ž'_“¿“ÏÈÈ!²l#kqfùûäɇ“-ÉqÉÑIk"‘8x5ñlâÑÄý‰$z݉ŽÁ×ÎþñìÁ³;Î>¿<öëüVâ ø€«’s’%¿}³%ïMŽK|ˆ7Â;Ð è×;`Wï…}yÖ¶,œL&a%ÐitVèhßž»u Yʨö;šysZ;ÁÖn…{eƒ¿öd."`¥sÀÓ¾†žI>ÆÍ„1v0eÙJÞÅbâ§(¬ÌàŸšÐ_ðxô7H»Ð®ÁG€ÛSÊ­Àu·r:­üwFnš=kæŒéÓ¦¶4OilˆW5¶²bÌè²Ò’â¢h¤0\ÊÏËÍ ²ýY’èózF¹]ÎÌ ‡Ýfµ˜“Ñ ×i5j•RÁs£‚:ÿÄ1ìˆóAÿ¤IaZöχŠùÃ*:â"TMÙ'.v°nâÈž2ô\ô­žrª§|®'Ä*T.ëüb|­_ìÇs¦¶Bþ®Z›?Îò“Yž²‚ ’Oˆu™KjÅ8îëâW-Y_×Q ãíÐikü5ÚpÚ¡ÕAV¹x†¿{ÎY†dÔUî Hm€YÅ]þÚº¸Ó_K§çuóÆ[¦¶ÖÕº%©-\Ç5—úÄ‘BÜb]P cWÖÄUŒ¸”¾Ú î(Ø·þÎ~-èéúΟÛçæ·Qæð­g¬>šy¾ƒ[jZ× ousëë2—Š´¸~ý:1¾ejëðV‰â¶6ž%‰ë'ë;é*fF`"túôUR/Õ鯣5ËĸÆ?Á¿dý²Ø×ú8šv­ÔçrÉ»“G«N\?£Õ/ÅcnÛüÚQ;lhý´kw:eÑ9²%\°C0§Vs‡Ñ”Îè Ã3çÚXŽu§¹¦iç–Óù@ ââ¥"̤Õ/2†¢Î1hý¥c |Ú0<_Û°4®©éX/TÒzú|\üâúÓ¶Ýü‹‘5óÓ5Ê€Jú]ˆ† Ú‡òñP(žŸOåBU sÏÊeá‚Uýd¿¿[Àò¡–Vx¬­2k.ItW7ôËhâ½S[Se-p÷!9j‹“Ú²o¨Å>“¶ôµœ{¼Ãâ» ÑCˆ=®žû3 kÝ’Ê8vü7Í©ö¦éþ¦©sZźõéµmš1¢”js®-‹[kZ97I爛c­ ‰sÏu¦…V}œÀŸ’IòÂ~•D‘Õ`qb\蘔ÂmZIúêOž¤O1rþ±ô4ã•¡‘å±#Ê#¦§_ÏÁ„ù iš1gýzíÈ©O Åõ¸&R7âF–·úÆ™!1nì€1Ãaafë{n©Mlã3òÁ²TežŒœ¬Š·€ºÇuWŠl,Ã6¨=Ïdb¡êlUŸHæ‘“´›6@Ù›VâB nfyG Ïi¦303Þ–s˜"tÁ 脪ÿy&ö—ˆ;™H¨RŸEé¹0ûÇ©ÅoiípÏo£šGÿ™­q%[^‰šÑôz ý¥†zoÁhiÛ)Í”R ûÀ\ cÃ~È!–ƒ~øƒ*”b¨a`ý·_jëO&;¨Ue @:"m^ßY|z>m Šn0Á6xŒƒ¾Á•¬_?Ñ/N\ß±~~²w_üëwsα¾»®cHIû“{6¸ãïl¹\‚+Á4a‡ß>u‡ŒoŸ>§u·§åÛg´öA¬^Ó1¡mG6´µî’Y-9WKK"-¡& ‹ÐGԬɽ[F¨—µò¬‚•/íLjթ‡ê0º´Ÿ¤êVŸ0¢ú¬Ü6ø0Dû!‰*~Ä4|D°LkA@18)* †Pb¤Ú”LBAö ÙÉ}Üé•UŪmÜi迉aà@  `#Àv%’¹S}}ñnxîT_Eeqµ–æP*IöÊ;§N+öU{¡"Ð @(`ÜS¨`}”n§€Ã)´à­!¾ì+¯d\¾ì›2£¸z Í¡÷Øè_Âñ?EŸOÓŸ¤émizkš^‘¦KÒtVšNOÓñi:.M«Ò´8M‹Ò4¦Yi*¦©ÑôM/Ù/ûX¸î3Ô Ð À¡ÀÃk6lˆì8 N²ÜÜI6§ÐÿSèÿ)áÓ5›¶Äöà>íÓXÄj™»E(màá©Gà©Gà©Gà©G D€(€ Р„–¡åC#ÜAt€@ÝA¨;½BïƒÐû lÞðǽJæ¡…ÈÇ=NÚûú"°}°á}°á}0÷#Ü{0Ö{l¬÷`¬÷àé÷àé÷àé÷ØXçK7§[èëç~ÙWCÉ+;¥…>SuWÃ×€ÌÔÀ ÕÀKˆÜX¤}€Àp@èèPpõ\bgWEf¡2 c¡Li%WÀhEšŽáB}eÀ'‹‹Â(QÂ(LÜÄå@)J9¬” ¥l(eÃ4£€³áÉ %@³9?-Ã&Š}V'“X±O ¤3…ÅÅ/s™‰Æ².ÒκúâŽj7 æ9 fŸÃ¹Ñ‡Ý}EÅì1wßÄútfêôâj3—çÊËNNƒÈù8Ð< Ö4õõy'øvãjÒ »€@Žô°ÚzX*=¬¯–Fû¬‡å3@/À&€-q€}8ýN£Å"÷“7û²K6ï!o ä y&%¼YqBA6éŠlæNpd39AÈ^å^ñ)cÊyÊ.åF¥Â§Š©æ©ºTUЉqͤ™ãE¯˜%æˆb½Bð ’%äB½r^õRrlâ<ò8ÿt©¼V/ù=Ô‰ä0à(`€ÀQø0êf¹^À›Xn à8Ëíc½é3½¬,œ{Žö<p€cõìYr˜,gÜDr¸‚Þ‡‡¿§X­@>„ªGd€ž|Ha}ž" ~€Cù€\Šå#ï÷•š|Õƒä}2‹•ßô6¤· ½ é XPƒ·Ø[½ s%8ƒú€n€Mû°:oÁ»m!:hÿ·ÐF€½´½ðÔÒ±æÆh-¹­&;€ÓZr Àµ«®ZK®¸ `ÀÕ¬¦ `ÀJV³àr€+ºXÍ€¥Ë.ƒš.àÑÉxt.àÑ<º.àÑ<º€GãÑ<º€Gðèb<º€Gðè]ŒGðè]À£‹ñhð5׬¸ŽÕ_ pÀ*€«YM7@À €•¬f9ÀåWt±š%K–Ðñ+Ùø•0~%Œ_ ãW²ñ+aüJ¿ƯdãWÂø•0~%Œ_ÉÆ¯„ñ+aüJ¿’tíà+«“À TƒJÆ ÂD€AD€A„1ˆƒ0ˆƒc`Æ  "À  "ì"0~ÆÀø6þÆ€ñ`ü6þŒ?ãÀølüÆ€ñØø0þŒ?ã°ñ`üÆ`ã¯%‹Ažx„k-¹`!@'À"Ö> `>ÀVs1À\€v€KXÍl€V€6€9¬f:À €™³ØÖ/FË€O'ãÓ|º€Oðéb|º€Oðé>]ŒOðé>]À§‹ñé>]À§ øt1>]À§ øtŸ.Ægð™G¶¡9À‹*Ë¥ :±öyó°š‹æ´\Âjf´´Ìa5ÓfT'Ï œšS3pjdœšS3pjNÍŒS3pjNÍÀ©™qjNÍÀ©853NÍÀ©85§fÆ©85Ã5ŸfÆ'|*Ü¥ :±¶yó°š‹æ´\Âjf´´Ìa5Óf̘Åän1Êg<"À#<"À#ÂxD€GxD€G„ñˆðˆãàÆ#<"À#<"ŒÇðøˆñÀcx 0Àcx Æcx à1Àx à1<à1<€ÇåA®ÇO’ë° ´ä hË7 5nlÙ º²tf6hF=hH hJhLô" úQz’ú­Èí@KDÐ/Y c.‚1;Ñ™j?Ìú˜ýc0Ç-0×Í0ç…0÷Ù0Ãz˜i ̸ f…ù…až0ߘwf—³”`¶"™.;½ük¡ï€=E…ýØ%—Adt` @=@@   xÃç‹Y-WgqâdÀ/3¼‘á0|5Ã1\Ïp¥œÑbx¹Å°¾ÅÐÕb˜×bhk1Ll1T¶^Ä ´z|"{Öî_c¸maîCãÄ5†ê5†Š5†ò5†äEü®‚Ž?aø†ï¦aø_ aø†«öâª>ÒôãÓ}Ò8xïS}R3ã}Ò Ûú¤RßKøI$ñùðã}Ò%PûÓ>iÅ}RE}R }R ê]RÔ÷ÔÏcÙäû“´Â÷;©Ñ—*|Ѻ>ßfÖ¤ó­B¾N)ß·0U=;Ej(yÞ7NzÚNÕ¤jfZ5Vͦ~¼[.QmúµjS‡jSTµ)¤Ú”¯ÚTmÊVmò©6yT6µE-¨j½Z«V«•j^MÔHmëO‘ è9ͦ(Qòó,/Š ;Æ!‚Õ5¢Ž=d„ ãvÑq+×Dš¦OÀMñ}—¢¦bü«éþ~¬:'®ðOÀqKjš1!´2³)îœÞŸ>uNk?ï­máwNcÅ}µmñ Ë©òÅé¼ ùÊt¾òõé<ôo‹5õ«’ÓâcBMqMËÅ­;0þA”âäveFk?NÒª[Ýô>r7ÂØwë]nJ“·ÞÕÖ†«b™1ËxsÅÄÚï@i:ÿÉ<Ÿ¥¼[®•õ¾gU¾:•¯Dåó«h}Ót¨Üô¬jSjlDª2Ó izk<éKgš`צ‹s[w“WW»›Œ§¤­u·s ‰ÕM£õÎ-ð’çúrÆ èf,Ýh?øV¿,2žöË¡$Õ/‹õËÑoG½TW»C’†úÔ³>õ#ûlÙg ë³%݇Kõ‘†õ±ŽAë#YÇ\Ð'ë?è“ó}BÿîÓ9áß6 ÿàÝhØ1v½:îð×utÄ7¬Z’ï] Š»ÑX<¾Uv,¸t ¥ó;ûñ€¿³6>Ö_+êÂöø*Ú<Í_»­ª›Ñºc•ÜYÛ7MžVçŸ_Û¶³yqlùvw ±Û[üƒ-¦ƒÅ(¯æåßѼœ67S^Ë)¯å”W³ÜÌxÕ-¥Ú×ÒºC&´ÕÌMÑD§©ïpKmB÷x¦c¥Ì5î=<ÂO!]¨-®÷OˆhS¸:\M›@ñi“‘~/nÊ\3VrïÁO¥›¨6û' P >uµÿçÓ•ì³ò?øü'=ÑPû•™uKk‡ÿ1¥]Z ¡«Î %­LW\¹2„`e}GNGAG=×áíÈÊ•m´òe8UÑS=_a¨ÃW"¾ôÒÀƒéŒ’Ê :¢506N:EjBܤ ¯¼ò*èè;?C Œ"jŸé7³ ÖS¡±²W©: u þ$‡´JÅIŽ#.Š?‰‘SÝt]fhŠpªjò`Õ᫪ÉÂ`ŠU VQ(ŠJfÉn¹}ge:ƒD~ó[¹ù÷‘™Ñ9˨8„´ /¡Ý¦+2oÓ¼‹·‘"õ¶jÇá´¬Ü#ˆ1kŸ|jðÔW§'&Ê KœRéÏ æœÏ¨±ºZ!¹Ü¢/¤YÑ Yþý„#èõðç) –üÝäÇ¡H‡$Y¯ÙËéXÿ÷”Ný¬À…)ž“CŒy¼(°Ñ±ËJËKŠ7mÒ¤iWN™R @ÝÛ6„øE¹ÐöÝÈ•Ü' N)–áZäºÚÅÙ]qõ'Oö9œ¥ýЦw`µF«ÓŒ&Á¼‡œ±À \+`ƒàâÝH o9¶¾ìZq­möõ> Ëó‡²ë³oË~]ûšNÕ¤‰–£Îì諀ʒ%ø…l!p ë€ÿ@ö€Ú«´s|¬”RÙTY¶YÚg8`àzµX‰¹~|t'æ8ܿإôg£Œ~¢Û%Ô{úÔ„úRF/šÎhߌ2M?™üóòq–Ù›RF;>7ÞˆÛQûhs­¢Ø‘á%vØDØÅ´¢)JŠG———••æƒ~ÿ«ÇÇñÏî­,˜mÝ[¸9±´>Z=~ê´@åÝ—ÚÏeáŠpan¬=pße nzs­7ZX‰ý…?è™?¿aÅLj‰ž‚À ûÐçr†l’]²SÈAY£¨~Uù¦øžò¨ò«r5Bç~b2ô~6°ãÖhþè,„oËÃyù£Kõ-}±ˆW,´-Z"k{µD+Í+À͸  Ï&‡ÁÒwZÉ«ÊÓö–âR‰×À(ÍÞ%ÍËÂYlU,ºX–«"c]´ŸÌ”-*9Ãó©DUTÅ©œcbϧ%}òàñô¢Q?}¼„" ,á91A6y)²Å`!ÛŽ‡,‘žÇÁfÕÌeöjßNèD§ºú1*¸ÓÔÎh_êQx¸ÂRQa®¾ÀfÈÀu1í=`⤲´-Ë.)ÎH›·´m2oCÛDZ6»-ÃQR\>šóMÞsÑ¿ÁªOÛW7w]|w¹7¯Â–]qÑå½ýt÷N^·ä†9cÜų_lˆæåm_vãmE…•Ù†±…®`†`w>±11‡n-îÊ—“ë±H•ň$?Oð(Ì Êùx“\¤ VÏ=¯~]ý‰ú ï#‚Þ—-#DÔG²ÅàçÁÏóÏ*ÏŠÉlC¶¬1Å‚lí!“-ku¥¬” ·Ìg¹å\mP¨’Y¬6»#c¸Cç~n4L1 ¢!×Ô‹1æ½(KâU&m®¤Õùè†ÚJ`»)«ZTʸ QaãmÖÆT®0‚8ȶ äQÇ»ŽÇ GÒ¡ÚìÀŽ¡ngèÒ뙀³§PÅD¡§}òqjÒáÏ\Ñû5fÈG=guB)Uº> `ÑÀKÕ\+»òòEI¡‘>ÎÓ•Y>œ¯Îõ¥­Þ7Þˆf\+ Á>¨Ïõó9º€é X¨ ¡ÐP«?›pÙ$˯ðsÐ 6}¨•JP;Íl¦"ì6Dã9»ÿ[ ©ø€„PÁ ›m~ç&Ž}2í¡ú“1¹ÁÏä‚›Ùwcoß–{îyLaN”%>:ðëÄéü¼b¦æWS|ö‘µñøõ=wß 6shúÐô:!O>l>hû0ûpÎg–c¶cٟ圱ñkÕ6Ÿ”[:Í‹-öE¹gôJ[,“sÚ,´ÎþÜöY¶Êå4è‘Biuºzƒ ÜØÝ¥]YhulÔ7»)O¥éDz†(R–N9ÅÛÏBŲnï/iñð¯+leÊÜÄ((£Áî tü&µ‰í=“A—+@©2—<*Ž·S•Õ˨ jG\PËGLK‘†"7ÝT0LYS‘NÅÔ“ƒÊ9·´iµ´©ÀÛåÀº#ê㟠䀡Ì÷Ø3 §®¹{ûS¯öNÎòçk_ŸøêÄ­»pöç3ïáûc ·4ŽÏ´t¹£?»éš .aòøüÚq_zë'¿Ç>‘ZÖñ Ÿ¥õ¯[Žhuj£ÂÎ2bAç³ûD!_ÔEìQÌÿ}ð÷ùLû̃âÙl“Hõ-Ÿ‰6dDª‹¬” ·l¥Úg𫇅Ñ/÷Y4î¾P3½ ÚÛÔ+Õ>«J ÚgÐe8> U-êÂݘìÃGÀ¹ tg\>¡Y˜'t Ý€pBH 꽜¡†l‡¨fÑ 0¥lBÕ9-¾8§bZªbŽo«X¦?Çhñ[>”c”m š†)XJƒòòuú|èWžÞçÇ:íHý%›]´ƒ~I6hµ;¾K¿Ò¶ÖϬ2JmnFYZ¿¬Ãô‹çoÁÊN|xÚ'‰c8÷ý–Gš˜~ùSêu÷Oæ3/Qm*†H÷ë8XT”¤AÊyí"hìö2Ð.ÊÆÊÚ~K¿í÷¯Ý¼Þ6Œò–.$Ëm¿V~¨k·•Xeƒ©Ôê 4,OYeêžRýa PéPÈ\# ‡ÐqF@ŸóÊp¦¤ï°Ó–›¢0iFýFûr‡<ñßÛ{B!¾´9ÀíÌ™“ä{à2b¾@þþä{}@iwj@8$Óü´âóçe’†©YJ¿ôì âO7¬xyŠ7oŒ77ñÖÆ¯‡qìÀ ¿-™ÿyhé’‡¢ø’–E¶Ê‚ÜQìxû6µ–4^~ÑÂU­³g·R‰HÌà_á~†¨ß%WsHQé3ô’¥È2ÁÒïë—Þð½!}óu‘Npû¢;x@ÊwFú&çLèTøt‘.‡ê~ÑÎés¥^Ù™LÙŸç–#J<ŽW(Uÿ)½äm°e-6‡+âq˜ò$¿ ­&X‘<:“1x5v‚ö÷i¦FÀ¯Ý¬Ù®Ù«yWÃwköiŽh8Ÿ&¢iÖpWI‹¥ÃB,¯0wí›Åyb—Ø-*öŠXt7,2 ƒÇÚa{RVA¬:JO±ª£ôØf›n˜ÒöÁEíºÀ>äz³ò³ |¨Ð ($åùpØù¶}ˆ¹G¹#~>:*èÇn×û`ÌÎÍ äù¹ÙÐ@é¶aÆ¡lÈ8˜á°âr¾ÅpPf,†ÛnÒIf.~iÕ§4óçK–ÔÞÕø[0®ßN¹+¶õª«¶RàšÇQ;1èXþØ•Ô@,š²° gìÿ Î'zž|²gÅOÐó‹ ”íð cвs@ƒ•J‡2GÉÉYKRAUFf¦sùð\X•›—*G¢Ñ¢ Ì}ê'ÝpˆWÃRÑy–$‰Ã‡AcèÑ›þL<Ýw—·a ûñ6Ù„O{ 4ËËÍ5›­3“J… nÖànŒ#T*‘D+Ñhoöá"gEóâ´Çf÷cTÃ{R8»¾Š?u<¥¬Ô[ãöTÔã€H¸8#µtéG—g˜K™WV]P?Ý+ß\³ìÉ˧dFÇ7þ­!uNÎŽÌ­]ÚÖœQkü¬1V”9…Ùlˆ„š‚I®J¬5ù*è‰gŒOÀxe³*kMô«KHô+°M°œ×_Ý0˜a<¡je‡L‹ËÙHR‡Jº5{Gœ lÈùßÔßþÜ/ï‡#$Fš GöÙ`}÷ÉcEŒÃÀ^ »¨xjmˆ€fâkxæžívѵuØ8›Ó3çñsw3ƒ§Ò§ÃXØÏÔ±j‰Ý_öoOŠ$~ _±Sb=Å_=A}ŸÂ|øpâºÁê¡3"þ’­ F7ÂÚ\k“‰ÖÈ:ôŸÞç1A»àô«ñ6dBç´½q™v‚lÓÇD!*t[^p:‡^0JËQì¿?ú’ýßñ6)w}> †7)7¡çÜ2l—ÝTÝôpša:7t’¡G™óÊÌ¡Z.,Œ\ sCÿŠB£¾ )u(}>ïÅKÿãŒHaz=ú¼ ^ªvf|Ú jW¦ß#&m†–ç9µ\9½Û“-8§ФÁ×h/ò1%,,ì`_Gœå#•°ŠÙàsëÇô*"Óá+:LÍ®Ì.½ž£·8ØNU3ƒªæy ,/ù7ŠYbNëåƒcÖ5?¿l½ØÙÝ_¹¤þ²iCZÍl¦-ÜuÑhÏœ{7Ÿ°›k|¹£Û7›¼•)…4á/+ÃLgA$uì” ôñ—ò%/~åÚÃÆ,ïÛ?È|ßù‘ûШOŒÿ$_+ ¯;_wËqëQû1ççnþ£ÌF}F>QÓ~nüÌ¢Z˜¹lÔŠ­š'u?3 šw,¬ô¹áˆïƒ¼%Lã‡(è9BÅ[.Î.PSë‘–MCåYK¾[¿/¼Ýryœ™è¶ìäqv^ãQe*‹¥l0ŠÆºÊþ¨(‹-"‡DAÅ#"‡èÁUî^ír9(øe4RÈ ƒÓŒjÞM™7R;*ëd½lP¤¯SE)«Z?B¢tús>“?ÿÒFxŽ îÞ½Am1‹áH©Y®®$JÍFÉÎh1»ÈÙé 2ú¼ÍYŠóŒº~ì‘%# h”.§©EPèu(µrœ—\!ŒÙB%Μ…$QŠJ-R‡Ô-)%g~?Æ;®r´ì€;E8¶¢9ÛÉÇi, ;•©ö[†Õš2¬)ñ0 „ˆÑ¯0qf?Ìì:hH€àC ‰M¦˜™"æÍ¥MHÏ9Ó̾Š`ÖXú7ΰêÆû«^" gH¡§{qöùûàÕìÎèÎÞöñÅ¥ã.Z¾<ñö·>ì÷ƒ`}«`¿ëÉ+r™Æ¢,sZe‹¢·Eˆ>^¸«ðÕÂ4ïi?(:¦ù¤è”þ«ˆY‹U •FUž-ÔçMŒ¨³©ttëLpÒ¤E&¬öFãó&"eù³sË"#õëŠ,ú%ñ¿üZ‹BÇé5}4CgÓ{2}NWÔRy«nCôwºßGŒÇ*þTùu„3p4;ƒ+)ÔkReK½3J EØû(Ez8eï,,.Õ¦©žj|e™6EXkyEª(mÝÙ2½T›¦¬½±9Õ”=]OŸÞ“"Gd]MY˜ó9¨®2̓RYãÊ)­¬âôZm?Y.×E mÑh!'VùêÖÖ¨ãLuÍuÄW‡ëd ´N./«û`ܸ*e†ì—f\#€¼‘8$Å$"}àÒæH6Œè‘»zJˆMs·Ð+lâÂ>ሠ\ ªÉLˆe²á´­óz¦øJÄ’h WÂΈ’¿´Ä9©ycúÈ=ùýÒ‡}uÞŸ]Ñïi?‚b=í±ã댅¡„×e8 eT|ëûóæÔeüõ°{n*¬.$k ±Š&RTGQ-Eô¾~'Ðì4ÓTJÝfbQÙa`÷é²&Ëd™œ1sœNzTg62+M¥4™Í„L 5—õM¤¨Ž¢ïüÒ¿ Ó‹zÜž»h§É‘6š9©{úÔwz©ëú M,¸Úòô•!{€«-¾¢êꉾ|±ë–¥+æoø¸íÁ˜)ËÝ #·ÌºkJ ¬ìÉNŸÞ¾æú›«¬’1Œ ŽŒ!?ôùrÌ0Á4jTàž©W4^æóŒ±ÆºÆX^qn^#3×岸.»¢a¡{”šŠk2 ©.Þº¸‡ÿ *@Ïô9ÕR?î“}’‚€G©9­Ìºn'v:má¼<Ü­?¢'z¶´ ñ®Â@v*Ðz¼vd£'ˆ8CtÛâ¶}¶#¶“6­•´¢×¦°9Ã{0ÆehÈÇV¥¼ìáï¡vsEä#ÖoòàQOÂÙþ(s£‚ÅJxB ìAĪð Ô ›Ù7ÃïX‚eçüWjÙíö´ #¥îüå?ºµÄ“;V,J \ºw/³SÌ*1wFö&:'Ø¥WU(×i~âü*mÜMÛv§£{/¬ÔÃÜà¥Ôr–6Ëhid@Z£Ë¸T»TüJTäÇ×ð!Ó§&åÐï.øÊÕäm=ø¼_Ügd‡l—m²U¶Èf9CΔGÉÙ¤î²>:æIÞ<§R¥•h¯ÕœöJz:+K¢e&Ô·ã#˜£_‰¸BÒ‹Dƒ\ÈMÃz³¹×‚}lqæ ë±X‡Ý¬P]†ƒu¬ÊrÞóìÒ€¹MÇóÎSÇ4ü”Šö¹-0³haôP™wÑ ýÕÖ-“:ï ùRaC¬züÆ¥i1XM#óHnî̦ò©˜-ùà«ÇÉø'i¯‘< QÂoaýƒÜŸåË6Y6Ù‰†˜8eÀI|œ#ð õ!ÛarÈò¡ýƒÀßȧ–OìÇÂ#øò€õaÛÂJË>Ë>û:`9`?ŽXŽØ“è¤åK»õVÎ+EÔv¢^¯hTêu•ÙdW™ÀN¯#ëç•ZÒÔNmF%+3ÊÊÎTY¾2ôʳ×Ò›f¥ò!Á"Øç¡K‹}3¢óÖä“@ ‚”ÈÄÀ\ótÇMÖ;mïâ·È›–׬ïØ^·ÿ*°/ø5Nšm¬$š€2èÄ^b8‚cqI°×gá«°ñ°° PÕȲÃTá‚ò¨2Ÿ¼0ªÌáuV–šû“Þ 4ô „V2ÁÌÄ©€\0›-{Èç£THåÏÎì%_§[Ò:½XxüÜC©¯¨ñKµËÛPo¥µ;lv ö“òÆnƒ¢#„5ZÌ6‹ÅlEÓÆ\8[,Öì@®-hçˆ#9V æÌÈ‚ì\І¬‚•X­DcëÇËdÁëõx´Z !XëØC>Bfò‘,ɨu£^‡ÓÖI¤¢Ç®(n‚ %B¹9¿Ùï#ä¤|ÕQ”™>À¦~ u”ÕW7b^ÉL¿ bd]ahÝ ¯­+̤ó¥ÌóÇ^8 h<)¡J…¢(½¤Z3ä>ØÁΑïY3 昭?ù„çÞ˜µ”>–ahËä ßE›éx@a#?~Á’#™€†y#ð´hEÏ…RWÕvZ`ä(›X-Kš²2ÌÀ’¦öô­¸%Míéï«-iÊ´!œ/œ‘*ƒgµ¤)ëou¦ú[Óe˜«%Míì˜TA‹;tߎÒž‚à˜þG1é+Ó”OµZK¬VöZºN©âüdÉ^yfLE¬qoc,ÛÝö¬P¿«Ãͺyººíº½:…NgŒPýoÏ(DÂÈŠek‹µ×ºÅ·*¬V!O††¼#6ú|½3–¶û°*Ÿ%„m`Á+­žæÁZýŒPa„ l<^u†¶"¼;´Ûç-(ß2þJx=ôfø¬ñD~gÂê{B? o3n^2îv…^ ¿c|Ux-ôvXoyÃR¨0\ª ׄ. o0Þ"ÜZÖÏ2Îf…æ„— ‹BË´ÆLÁr‡eXÝTŽÖ— êJ€š2Al*ƒ ¼«¿æ1æKøQTˆbøÃÔïd{Ž·÷ :Oµgo?*°ßu¦,èÖ:uaH‘v¢©£aFEúv¨u•º*uýÉü—Ìn†~+û^š~¼ÓæŽy¨#*d™*ŒßaJ;“6PØPOû ö0]#<…ÁÕ™z)bV:ê4ÅL 1׿¤ôãçaH“<Ì1¥~ÝÁtt­àaºHRÖpmÄ锡;|*ñ£âÒXã‹äÖ~yr„º¯4ÄÊËù“©“JTÓà÷éWŽÉ-d—$‰<üaê´ïì‚”Ÿ ÓŸÑÁ§ø\ºúߤÓ„}ø#)þŽô—¡ÄÝþ¿—ø«7(ËTê2u™¦F«Ñ>ÈÒYíYÝoõó 2nf Ÿ›Ÿ6?m=l_œú>}Ÿ¾Oß§ïÓ÷éûô}ú>ý¿N,ž$éÿ%̆8ö_„¹”™=±õ¢fsS}ÝÔIs¦M·7ΜUã±q+jç¢ÿ><šÅ0O×ç¤;™Œ)¦ÿ ðl4µ¢‹P32£&TêÐT4 Íaÿ'¸5¢™ð|ÊA¶²#ªEsÙÓöÿdÓÿi[@¨¦ëªK;WˆS:¯.˜Ðµ|!ëð&úOGÿÃzdñ$:™Q‘þ_àøýh1K´JýoÒ»4¡m芑 ï¿0Á› ¥§þ7S[ò ´GXr¡^–n„T‚f%O&O¢åèAtr½ÉA‚Äýk@ß‘[´#¾}Ï> endobj 196 0 obj <>stream xœí½ xTEÖ0\Uw¿½Ý^Ò{º;ét'¤$‘\–°ö͈Ù7Ù%Œ Š¨èŒ¸Žà2ŠÛÐ CÄ—Œ2:. ̸Ì+3 £¸ÎDÓýŸªÛÁ™÷ûæùŸç{žÿûŸéKUª[÷Ör–:çTÝ€0BÈ‚š‡&N˜RZ†ØoÕXˆ¦Ï]>g¥‘_ñ3„ðCsׯì,þýz(øBbß+.ccÃýIfÈ_¹pÙÆF}o'Bc-š?gÞoï߉ÐÚíP8`8Êihpä -_»!ÓÞc‘ß,[1wŽ‘¿î[ÜËçlXieÞðn(Œ\9gùüLý ˆ<+W¬Ykä×.£÷W®ž¿rrêÈgPÚWÓBòAð O G^„ÒŸ@ø”¦©ÅéOé}š’ÏáéöL@hz/FÏ¢ÃèE|žÚ‹¢6ôäA#ЃhúÚŠD4JnA“á ügØ—nC¥èa˜Ç‡ÑQ¨{ºu 7ö¦?C›ÑÜ›ðÔ0Óùh(šˆV ÛðØô:4 ä¯G•h,º­ÄÍéúôíé»Ò¡_ ƒÜoÒÝÈ„üh.\GÓ_ÿþê OÜîC'ñ]Ê~¤C+ÍPóçh5ºŸkäqzaú;èAº úÀ£qè(î$ xû|ô öâMÜpxË£édúÔ ¢F´Ý:p<Šä ³ÒãÒG‘ÚØo½µ¢pµ£Ð lΤKŸA>T‚ÆÀxÚÐoq'—êÞ’ª`–z¡*¸³ýzÇQü+²B0 e‚.\~ ¹P?4 zû<ù1þ¹®ÍÜËüÈô0d…y¹“Î6ú5ú3öãR<O'½È ò·ÉÐb?¸æ¡Å0ß÷ÂÛßÇ |€˜É1îQþiþ¼˜›:•¶Fâèôsô+l‘Fðüüþ '³Éäîgü“üï¥90êËÑrtzý;ð@< ÏÄ‹ð&¼߉ïÃGñqü)J¦’¥äKn·Š{×~ ½p“p«øiª>u$õ»Ô?Òeé›Ð$ ‡-Ðû»ÑC0²ƒèz®“è,`¶ÂÁyx¾®ëðmø¼?‰Û •ãøüþ Ï—H$äÃ%«ÉUägäAr ®ãä¯ä[ÎÃås ®?WÍ5p+ W[¹píçþÌûùc|æ¹LØ)ìöO / gD³ôÉo|ÿhwq÷û)”º9µ3ÕšjKÿåý0 aT ½Ÿ×À÷N ¸½èMl†¹óãb<…™™—àUxÌä ø~ü Ö÷_âC0KÀ_BŸ-$Èú܇ô'Ãȸ.'óÉ*²ƒÜEÚÈ;ä;NâLœËኹQ\#7Ÿ[ËmävrIî î=îî÷=\i^åÃ|>çü(~6¿Žˆÿ„ÿD˜%¼.|$ªârñ&±]ü›4@"M”&IÒÒé-¹ ¨ó%´=‡züð)n WËíG·“rÞG~K~ ô<ÍãÆ T²ßL®Åm¤@Ø &ƒñxt†Ã\¿Lv‘sd07×á)h ég¼MtñOARÍ¿„ºøC0¶ß›7ˆf|ùR4£VŒH´ùk®/Ÿà^G'¸“XâFäUìÁ]ä n"PÁ ü¡åq¢_r«ðµh?©ét^Þt<?ra*.ÃßpiÄ‘ñ@E•܇èz´”ü7ê>¾݃çñ Ñí¨oBŸ Ç+z WŠÅb~•,æ·'nC„FW… 0'¸Ð ¸‘»_ü’¼‹Ö¡c¼ŠÞçžÞ#¿äÆñg„ÉxpÀµè&´*½mêùßã…ˆÃÓQŒ?ÒmWÆçAº¤Ê,i€»;@ åÆA‰(g,ÐÅ4÷Ãu/È (h1ðøe Å~‹ÚÄ©¤-¬¤Büë©ÉhFúqt_z!º2}ê ò`kz¼qúÝöàS× •(œó>+Œ$Ç„‘éÞdy—L!;/Æ/Ìv {Ñçpý2C„çÑ6þh ªIoO¿ Ô]ö>tº†Q~-Œæ:Qyj‰o„&#t$tTÆøæGkiIÓ’HR‰‹.Ú¶¤ Pãß–D“7æµúýúÁô)ä¯l›ZÍKÖ¢ sF[\hÛäû|zÄwñÞ%-šÝ˜Ø«-˜-=ùî1ˆU§PÝä 3‹i¢c€ ’‘¹èI}Æ4Fó¢msB5ø5`x*90²8© oÚ¦ ¢åôù¤Ó¢‘m_# €h×_/.™“)cÚ׈‚”N.ÜÏÂÉD"Y\LID8…>aùþ½KÖ·“ht¥¦M„¹Ó0¨¦?/"øÖv]™dó¤z#AWZ‘^šhH’&z§3{'g½Óœ½sáñ¦(Pr¢ªlNRŽ_øgÓÜÎÚEƒ’Øý¿¸=߸_7%Z7iF}¤v[Sfnë¦^”3î¼p/%Ãë¹É@$À±»@”³.T¦™zs’Á?‘õ¼vIªd%822©56â5/ïß|¨=}†>Å’Ët39(qq~ðEù‹ºgÞÆA‡a¬›:cÛ6õ¢{@jFƒc2 P<šZŸžDÓ€3cð¯=Ý9††@R‡)N+ýE™ìE¸~”:{—ŒA·mÛÈhd䶦msÚÓÍWD#ZtÛAò"yqÛÊÚ¦,á´§;n $Gno€¹Z„S4¬%ŠožÔ¢ã›§Ì¨?¨póÔúV‚Éð¦a -p¯þ`!•ZJ i&B3¨à [‰Ìêê5³»<+`ù¹í±29[†ÑÜvb”iÙ2e¼Q¦³2ú£2føÔúžÔÃX²¡7]ÊÀr’†k軽©¸ÆJ.úµÒqD5h=’  Väé°ªZÒ / Ò2Õ64Ÿó /!¤!p( q)„ fC¸Â."²eJV@Ø á0„3ìŽÎyZï*×Û!¹•%û–,+cÙ9FvV#ËÁHÇM2ÒcŒjƒŒjý*Œâ>ÃŒ´°ÄH±²fšª–²Î¡nÎŽC h%ĘA6ŒA«ØÍå $‰™sì+ˆ—í:Ìñs„Ã`„ÓnµØË†ª$M¾D&_.ãéÚgµ—íz)ùí…pG>€ëÏäÏh39Ó©A\a„ÃŽAø‚HNÁu®÷ÉûÈFÞC¥j ̆° Âa_BÈ{käO9,¦p Bþ±FþÃú#Ä6r ätíÍÖʪ²ƒ H”f€p,xÀá.k'¿oý¶W¸|¸/’ïÚ—¼…’4ö¼ü-0B„•D€ÞèÔ a„Ý’Dxæxæxæ5o@xõ… C˜A&Ç[¡™vr¬5>,<Ô *ó+`¾†ÉQò–¾A^féëä×,}Ò¤¯‘—[Ca4Ô÷<£AªAZ ÷ò«}Žpz¨†é C\ ¡Â³!ÜA$‡I~ë¼°^ò!Ír:TŠä•}!šæï+®1ò}•­:š¼¾hx „À‚^2z ^ò¼Àq „Ù:!| ! A„ÚùÐñ;Xlƒ¸B „Ù6Cø‚Ⱥó%‚Vdº¸—u¬4Óé 4G^‚‹š¼y$OÏÕ‚ZBÍÝĶžJ‡H%r»A:ì²½[üÃòÍ?,HªÛÉ(±#“ÞÑúmn¸ßÛ><4߃Bü‡H;[ÃoCÉÓÂoo ¿ZÚ.CÉ¡x;†¤#ª  ?û«ºnÜß¾Ž&Â×G…—ÙùÆË×@N·…'Çg„GÃûF¯ëkàÂ5ÁËÃÕF­þô™á¾Ð…„Cg{Y£Ñ{á´Êv¼H/‘vJõҰˤ)O K¹R@rÉY“­²YVeYe^&2’]íéSz‚®3.‘-7"OcžÁ¡11&‚eFTÒÉÕ‘º)Ãp]²s.ª»"’<7%ÚŽUÐ!„è0œtÔ¡º©Ã’uíRzr²2Q—”&άoÁøö(M’›aíœZߎӴèÆÕÖ"Œí7Þ iÑ·54 ¯{}·Æ1Ä^5rÄ¿ˆš2q⇟÷"87¹³nJ}ò©Ü†dÒ¹ uÉŸRuþ þ Ÿ©qÿ& õ¹!ø«ÚÉ´œ2¢¡¡®OgõPÿ êÅüÕ“C(B롈2êÝoÔ‹ÁóP¯€&POQPŒÕ‹) «ÇcZ¯eMA툖‚VÇAkX5žHÏ:¯Å N,Æê¸›Ñk¬ÎkîfZ'9„U ¡J(Ȫ`? ²*AìgU¦ÿP¥4Så– Una-qø‡:A£ŽåT¶ŽåÔIü»¿ùà ¼opÃÜYÔjŠÖ·Д¼uý"o²ùŠH¤enCÆFŠ7]1wMçÌO6DçHÎŽˆ´ žõ/nÏ¢·GG´ YµSë[féóG´Ö×FçŒhØ7jbEåEmÝr¡­Š‰ÿâeéË*h[£*ÿÅíJz{m«’¶UIÛ¥bm!Fãë[d4¬4o–î#&èµ)×0Ì­­ˆwpž÷º@(${ 3µôV‡Ò[ÀSô–•Ú»™[Þëç:ðžÌ- ŠíÑa(±vÝšuÈ[»x„ño ü hí::áFœXó?ýà^-˜®#¨¼.Y<¥.Y:f‹$AiRrP¶ÌdªÛ(ì…ƒh!Ç]¨H˪i™¢d*þ3þ×eÒá” šÉóû°ÂkÑš.ª›J@LÍ .ÑåaM p Nà5Ùw°n#Ft¼Ù°v]ÊÌÃÚLj<¬ÉNÇ…%*§˜Z@°¸HåÙóì1ˆ@¦¡ï#\ç÷º€Î£ßI…Ú n.: r\ÏA‡…/â¶DðLðqÕÞ„v®± Õta»£ªª__'׿<‡»¹ÏѾð¤ãë¯S_À[6¥&‘&áMÐÄ/ÑÕBÈM‡$kZ;.߇vYeHu»´Ëz9â4.ÂqÜ3öŸog/î>×¥ƒ·W×T÷ë‹qœØ+*T–‹\9Æ'ïþí¸‡¶l,¼$šÀ‰Ô¤CølýâD÷ùã Ûv>ÿB*œŠ\Ôþ|Ý\DŠ4¢¨F…ö@ÝÅaHÛ@å¿Ü ¶a›¦‘i|Óf³1àt›Å€¿ê6U%ÓlÖ°•XŸqdúH'öGýtF‘½¢0W¹ÛãÎÑH÷œHä_Rxõ–C3ÆKM§ðŸܹmÆïÏwŸø"õUJ†^>•z_Ž"߯jžÛñD=޹jB°Š«‘ úÎ7g&´;·“%d9PC‰î[IVrdMFñ +¡‚_y›71^;ݨ}ŒJÇuõë‹VÁ ûçå %½pûþý”R: Ú ½çPL÷ÚÙj£‹{¿îïæY/Ï52j1:ÕqôèQ¶t¦?!U€M9ˆ¸ôû­®*Òž~_¸ªîá0ávq{Á–Y° jùrHå>EäSÀÛ“Ð8¿ïjxsµv¶K3p°Uè“h¼V;Bq‘HäàrŒŸÜ‘ª÷ ý޾&8/tÂLïÕ#œn±W,å7“;È}2ÿ $ „Sl&ø5sëj^´¢/”–@`À纒 šÍ[)•@éÝg³‰Óf6ÓØbØot‹­B ï²Òw 8"è|¦\oDÆô®JÀex2Õ㺙5ž*l¯¢CA‰¼¨]¥þ€Érr¾mè›Sïù t-ÍMá_Žzm6[5̇c áWZDjHëŠ]³xNqš¥=}¶ÍngÀº¢i…\B¨=ý©î¡B!z7´Â™ö<ÔNž×ÍDõx"aÍNH$ UúÖQE¥]´§54>RÖ¯o …\hÐìpÖ ®Øì$ÛÎ)Ýäp’i!-£ïn…WCóm&™æ¡Æfñ_µ–HíÑÖXcú€ÁÂ`ñyá°ø¼ôŠüjPcn0Oµ.5ϳ^í¸Úy‹ãã#ÿG3~óaÓsNe9W iâ¥Ï )} É*€-HÕdQ|-èwƒ~9芓ýAÎÒÚÉcû&Ø1¨ÒÞýtˆM‡ ³ºÆó&̶¸ÄÏ“-(‚4`@ÿŠx4Ÿ1ry™X„ (ñÒ÷•Ä{ôþ/÷ÜwÍOÄßüîÍs£Ÿxñ‘Y¡gŸZ=·óº#-XúÓ·9½ûù³õOzìæ9ý€R¦§?æÝ@) ÜAœÉçÕéü{ƒSRM˜!ƒ{EU‹Íl ©j¯œPõ ½,Q‹ÙëÑ(ñG¤8Å"­/M¥ÿè…U55 ˆº]/k/;ª´#‰2(þŠ‹ÛRk¹ÉÂ×Ú/³¯p“ÝË´%®yîu–®›,Û\·~aQ…ÇèÆd¶Xy C»˜¢…:”žÇtÕ‚û·™Í9¼·ƒ<†|d‘^½ ›ÇšÙ‘ñRJŽ4Kkâ_}ãŵ8Ÿ}ŽÞ‰ïèímÇ[}oâ0| Üõ"ˆ:¨v”´ã»28Lt1,gvM01xrmùX—ô¦ý¦%‹·»Úò>ÿå†×–.˜÷“©OßùU_ï½okò'›v=D6\;÷'7ÜÙÿÊÂÖy³ìzáöÎÔ×#’î†õ½Vm YñÂØjyõ«¶ ðM7¡ÜÙ@¥”b¦±ÀâR­¯¶P^¤4i7s;´W…—ÅNíŒf’…ÕgS(‚Ê,sÑJ´6·Ý»Ý/<ÍýÂrk³¼b9ŽNçþ=×nuäÚss¹b±È^Œ„GY¦».Ë™î[$,ͽÆq«ã~î>ëýÁ=ø1²Çþ¶Õ‰\ȯ¹4?O•ŸÖ¢*&ƒ{Ui6„ù€3dæ!^Ñâ¶KQ<‚1ö‡=ñˆŒe3íì ÍEÕMãº`¢!>kÈ(;›LÀ@ã*˜Ð^="Í/€‰s”—ñ)NeÉq9¨„âÛ^¼$õÒG]©?<°ñO¸dðáòú䇳–|Ó£ÒïËó¿ÂWþþ#<­åÔë½wßõHêË;ŸO}¶í¨Uè!=3€¢m0w饑0.Ôi×B6$C—fˌˆJQ)E)^VÂH‰$8Wû·IïYÒû&Kz¡“^nüäúõ¾QÀ$Y”™—yÑçõ{‰hRTNÌq»ÜN7'8OvX!òÊÁ<ìVíy`Á|Ão n¤ê=ß‘ã"@Ÿ±¼²ÌZ]Tùþöé×5¬]3þê;Þ˜jÁUwþ¢_í¸{–6õ†Ð‘“;öŠÔ±#O¤ROÎ){v@¿ÚÏÿøÅ!õa Ã-0zc?&H&TyÜ7ð¦Dî+¯0ÒÞ}´¨—‘FcFš2R¯ßP:‹-ZEDØ!ì8.Âù°’ˆ/…5n"¨Ãgàˆ@áıê&6ÁÞÌÄÿ5;ñ_d'þœ®’qÄ#ü; =&¬ÝÖfß «VWw7fOªÊ‚Úo/·~‘ŠB#•~Å̺\®›1á¹€äòä Ý&îß&€sÿDâ?ÀÇêNäåì|‘ü:ò÷g¡‰{mМ6t¡ƒ žÓMôõ²ÕbgË& *ÏE2;èmÁfæ0KdÅdE²BT“HûkÒhMÐÇ´–IƒÎ}œµ¾ÉŽä{c$ ÝR­‰ê¸55ÚñãT“H$ß&P £Ê‡%6ã"‹9ó,X,ïG)D˜LàD:+Äúï²Xʪ2°0S‚À܉¨Ž ‹3‡°Õ„d•œ¾ì%Ï“éÈs5]· Ö³ÓÏ^‹0ËÙRóÌ«6ÓhŒÆpe°8 oFÄ&»H@æ×›o2ÿ¦Ò<Æ<ÆÆõâc–k=7“_oÙ`Ýj‘MD«,¬H7BÒåq–aVõ^r·SÚ)ïážD±Y­}â"ƒõÒW”Í“m“±J…,+ª (ÛjÕ(žšÍâè {@ƒì×*DävÜOWÍŠÑÍ›MØÔƒ´bÜ!í Š(6 DÛJ ƒU0ý¹ˆÐ$4 À,dÏ>û` ~µ¿«½@gLÛØ!sºt˜­Çå„ê [¯e*$ úÿ j¼€Ìéó@ƒï€:÷Ó4ê’f¸W÷"Kú›«JK™kÉ’~ë@^•µ$¯ ,®·TVYË*¸¿7”ö®2¦¼t´ lІ ìö ¨Äyö¨G±ý^\€göuûúãÙXx>5}oª^è8ÿÕ£'>À}ÿÝHþõóýùSç)3>ò= œ¢àk[&f;s*d¯Ù Z]ó($ƒ²‘dPûd"qœ¬ð„(’ÌsQ²E äÊD`prÝÏȹ1bÂÓDS“i¥©Ù$˜d%‚™%nÆþ72ÁàZÄgVë^ÔÁ=ÄU¢1A…¨ŠLHiÔä’eSmg2Øú&N=g¶WȈ€‚úõ¥ à MÖGVÁð;Œ¬’õ2,«’ò}Ì“qÀ`™ÒÒ¨áß0E«$« ‚“æÏp˜k€¹æPð›–œ þ¸ë Ë1ˆ1À݃¯p¤ã•ïS€°-üf@VóùæÌJÌw¦,È‹Zõ’ùö¥.R§Õ¹fj3]¼ÉnA/]™‘ìˆËL÷“µŒ9«è´ÉþˆÃ?¿×ò¿•Å™y7gæýŸÕA_O‘œÑW5!ȽŒØ]mh~lY RBòòì_XQI¯»Æ-»«á‹Ô«©›ñ5‡jÛï†Ô-B‡Õ1ÿÀòçSÝÝÏpxûæY×çXè,Ô§o¾Þ¢çÜð"ýîÙñ]qâóVæSóÑ@ÐvEÅb¡·',T{ÅÇ c=câ´h}|…p wµ°Û.ÜîçCOso£·Ý¡<yýA!Š…Áß(ÜåÝ;ÎÇÜÅñ wU|ŒwL°6\­‹O—ëíÓrfgäN_¹,±° giüšøíÁÛãôþ)î3yqpjk  ¦ê-}` Š÷º¼Å 'œ»ˆ“Šâ^·€D°,ü¡$„B6ŽÈ!IñÇ^Š gvuv2CÜÌ€3Ln;©Û‡b€zŒbÅy)ñGŠ›‹Iq^8Íä¥õLLˆ›|½²z¤¡H6žwö‚¶ÞUÃÔIféVPGöríUíÕÆŒ~‰VSù²juÌ :e¡ØCǤÊ”°WÐ5‘]Cåe•ñBþë­««úù£¿~%uho×¾ú:ùô•ÝïYþôÆÏî|7õüiѬ™óÞ˜ØZuÍÌN<ëÄ»x^ǯR¿8±?uò¶ÒÆqU+VšúC *§~[8ØD:+ý ÿÐÞû’½p.7—_íåùXa®*8œ#Í­ (Y8…kfå^Vt‹Ó¥æ1¥í‚,Ëñ,P˜¢ŒìÊËñ,PHµ¤‘*²Ä HW`«ˆŽˆÕ–ΈLN‹-3-±,µ.pÍ÷n4]m¹Úv­¶®`Mì&n›éË6ÛmÚ×Çî²ì´íÌ eVÿÞyqG îWâ½p¡^~_Ö/Žæ‘[zo Ü ˜ÛÒ;TÃ1Á-Pdv}¨· ¹9f $o2I#óX”vW@ï+°ZLBhìYyŽˆ8Ve¢ ôö딲îyÐåF½™}Â$·†#x"nÂ+ñ,âvœÔ½i“´ièñ¥Jõ½ÚÓŸ·Y­dZ/Ú5 }®—¿ Æ„ãº$Ð[ެ@q\p8¦R¹ãë7w&£ÊÆq§))j]Ì–üÁÈÑ@Óë9Ëìß\»â©)g N-›´xáu_ýìÑoo:lÏ>™|¸j ~·¾ùê›Îÿü•ÔßïÃЮ¼í²akFÔ.Œzæ$*¿âWó¿±Åzëí[fN(/_Z4xÿúuÇÖ¬ýŒÚ™}AFw0OÍ-ºE !˜pÄÆ*íd;ˆáïxNŒ`RÊaàý8ã…þT71Q,gäðWYUøƒ¬@þ>+€S†RFß(¸¯§V Ó +ÞéÆ5¶o`XE`U÷ÏËɳg*—ß– –gŸýîïÐÛôŸS‹¡à/`“!àº?€|üð¡Ì{Ýààá0ÿdjñO~Båï¥éOù ?¡JÒ[/Q,J±Ïâ/îe).u.§20¨xLq£¥±x‰eqqSßm–›zÝï~Àÿ¤%§(krÒÑú(ô¸ï©¢¾ç‹ŽøŽý>ç½"y„‡(5ÙéPŽÜXý©Óp…ž°7QR\QÅW•ŒáG—L— äʼnõæ­æWÍßZ¾MØ++¬˜×J *á9áûÀùWÏ_}æž §Ã¾ˆPj+uõjlº0Ö6QX œÈýšÿN3k9V^$(%¬æ­&oÁqÖL:hÉÍ&Þ𩘚¼ãó“ ¦¬*`¢ÄD‰ÇD·±ØrOi ”âÓ´ôIĺ3å\ŒN 2}7Nâ3˜ã<s˜ŠJ´|¯çRòÂŒT0ó `%ÌHS“R«ê¦Mc/m3×#ö…FUf”Ä*Z]=Në†ç?f”椔Bõ ´*/ J0ÈnP5Í/ä@t_ð²ãÞO´­n¹bï*=õÕ ‡–’Šiw®æëÖ?#tt}Ç„;^[“ú2õÎÏñÎÃÓn=úúñ—‚d˜þ”ëyåÇ32»"ÖÍ6l3aê*Y ²wM’7È›°5G’éè%6zÉLG/itô£ð£o½l¬MGËh »£3‡;‡{¦8§xšœMžÈÜý–Ç´ÇüfÙâS—ÅÜay¥¥Ùò¸y¿r@Ýo6»Á8þpÖüÙ¶¶Í6ΆAÄèû2ÿMtkÚN¡3HA6› ýÐÇ t½À*3ù”€ñ˜aŒ¦ntŠ ag4ÉŸádL0§à˜„ÃRD$+­$©´’ÄÄ«Ô/Pq$³†V æo\9®{V‰Î ]«Ï&ºV³±;¨K·Tk< ÿØJ xkÀÊÛÈ^á Ëî…U—bŽ«nÉýò—'RÿXýÙ-Ïþ)¼×·yÆÍO=vÃ’ÛñžçŽá\¬>ƒÉ–½–.{éÍw^dkÌHÀÙIÃ‹Ž§é©„·Ä,–¡¿«ð22Uìš\Hæ ó•¹®¦`gø-ámç{¾œ¹¾ôüÅ÷ãMqVMÃ|F½yý©,al x»~à3nàü#›ß^·ä­ë›v–îëŽ<³ný/ö\³áá›Ú~þÑ]˜Û6i(±~7’8ÞxíW/ŸxãÅYHÑðYàlŠî £`™Æ5 Ê4Ó|n©°B™o’sŒÓlNë“)”¤q¡ã]á;×9?ßÏ1È×/8Ô1Î?48É1Ë798DZÜ?'¸AÜsŽœójÈmg¢»É½Ò͹ƒ¶Únhªê OQŠÍJ³Nà˜w ¸ãn'pG·ÀªË*‹a`±ÍóÏR,´¾RX\‘´`‹?LÝ­±xMõ¡t™ ã°»\+ô‚âŠ,¦"=0d˜2,ÈpäføLõ”‰‰qݧÁPN$Î1-–ÚÊ]Ô­zš1˜Ñ«ªÙæ•#»eBWÐÕY£†²»¤<¶‰óâlå.ï(ùâàg©/±ëOoc+þþSµõƹۻOIæÓoÙô$žîy´ ‡AØ›qQêýÔ·ZdoÇ"|÷MÃ=RÄ (l ˃-zÈ¥`›¯Ô××§ûVú0?hyÒ"û-E–¤¯ÓÇûè|ùù²…3Û‚*Î! —“çD¤îraWÚ©óž8rf.ž}ýV0WO"®Ø°O§lâÓ-À&ÈÅtÞ"¦óæSÆA%m÷«ŒûÁ•q?|ΖæteÇ1ÚÓß=ÇÔ°G½¾C¸å¡sXEÞDâ\¢PÐYÐ…Á¾èj¤ q5Û­¯¢0†oÔ]š]T$Q ISdmœÀ‰â-[pødu¹=Ú¿¼E%5'@¬Q©–Sžµ·îÚåô_¿~ì¬ÀÀ²É#Žãîß¾jiÅÈË?WG6]±ýûÀÃR“¸Ï#B¨¯Ð›L&ÁUbŠ¹Æšj]¢’ëË-1Å]%Ñ*Ó×¥¦‘®éR½i‘é;õëkŸhIáè±…;Jv—Hòôª)i™WÛkjÞÔ^‹¥¹ys{5•4—œ(ü4ï‹è—…v[Ìi'-mEA§ÄV-¦]GšQ':æH;¹V/‚A›Z›4«îœòX¹óz{°æÑ=Mžf_SN¦•0±æabÍsA¬y˜Xó¸Ù=zT†‰5ZK¤yC¬y¨Rp)%zÏZŽ¡üpÁaÛ1ÛI[ÚÆ‡m5¶ °Ð1ޱù)nmùôm¶ }“É6“m6_¢dmo‰ñ=ÄÛÙ.íG®ûô9z:ê4;~ASæjZ‹’‡nà0’z-ˆ!ç<ýËíÌŒ;{»{MeÃ×^{³×Š×'ÿxæÊßÝvèêÇçÿq÷}~ßã×nÚóìÕöÔû'ÅÊæÍ¨LÞŠ«ß»ãí÷6¿ä›cžæŠ×yø—^~‰Ú~[âèõ.<ç ráçx*ØY¦^Çøþ\-×aáYÑ ¯Â#ÛÍv'`d ’ˤšcŠ^> "­àN»ÙãÖÙ–Z‹] 5,ìlsévŠŸÖS¨w‚¡DqQ”(t1ÑvévËŸ;À¶ Æ»)/z*T$ÝgÜd¥{·;éN»y7qÅ —¬}8COqE€rN!žùþ2nÂïtãRC­”iÓ³ßú "Œ- S9Ç猚ØÃGÈNA1ïl¢‡†ÈŠÙI/¦‚•op§U´J1«h`‹ |‰¨Ãt ¦Æ‰rCK“Þµ34Š9ö­m×u®ÿe]Ûº¥o«•ð«»{°{6yxë5Sn¿¶ûyàÉ›QÕlNBGõË•t”Ên%©t*'•3Š„”°²RiVveŠN)iE + cI<á‘»#QyU”bâwñ»ù$ßÉŸâÅNþ OáCŽç ]™Lã/ÌÏæWi«<“l|V²ñY»ž§L¤Ò9äÇË?ž½ÕÕìYuæ|¦qF7®^•pÒsš0+7·µµñ9vì|?‚Ò%Œ™ûÆl"sô€hèâtq†ÂÙ,Ήœ’=^p–YjP²Û`'ߦqW©Ä!Fœy2!û… 5F u¬ è7@‰Èó/V*£x!&öVëÕ«¸uê îCQz\ÄQ1.Åä*q Rc™`iàÄz©A¹–ß(ܧ¼,þžG<-~&ýCüVÎq¨ªÀq<EIQdÈ(²“D—$‰ÏÇÕ%ª ˆáe Ó/ˆ’ ”‰T¾ÛtEà™!_¦¹¼Ó‚™©+ùwÀBoŠ!›á4(Р÷c4Î6ü±1Ê0†ŒÒ™ÚŒ˜ Ž|fËŸóF-è!©¨`§1ÖªsÌŸÅH›:dè>„ÝSEw‰xzREèãeçÄ$M®–«9g\ƒ–:‡•8¢x-tktlÀóðYõºª”äV)rnnµHOäV‰ÔÛaIKž±µÐÀö…V¡ÌIe1ÝٚǶ0ZÝ4y¿U«„åÌ,i1e÷•è¾mÊñe—Zs¹ªYOkõÒ‡ÿÚ¨Ê¸ß +Ÿ:â_Òã™Q,%â§>K-Á‡ßO=¼YèøþN¦ÖwÏ#á«S3)]^Q%ãÅŒÙwå@c«»¢¿‘öíg¤ùÆV¸±jÂÂ.á¤ÀO€èŒÀ……•B³xZ*á AFßÄZ¬à»îsŠô”jßü Õr{H5׆Þ!g”ެË-Î:á2<ŠÆóó(eRê"1¶Ç1ËÑ™ëÛØF¹±VˆqÐ ¢øºÝw6»{kÀAÿ­3Y*büiþ´ògÏGámá\„xäHTñ" ÇECA1‡.£~Ÿ¦á±Ý1óxüÖØ;¶óÌ2aÛ væŽb–‰‹ÒÎΈÒÚ ³OÌÌ>aŽ({v¯ÇžÝÿµ·ãFÝìíà{]àÂëìuº‡n§¯ °Õ À Ìå%¶ÌôŬ‡+@ßçF¤<ÃǦ¶. #Êã¿Üâ?æ­BîÌJó}V<«»Ø’c Âj°dA¬oØ—7ª§þõCtŸîášèáÒ‚L÷øÚù#>^V.(‰ bÛ=ÆÎHfA2»œq—ÙÀKNvAʨè€ßætö°ÃœlYbúbÏêá²Ç—¬¿'|Ýk=µ/:kÈÊŸµÕÏ»e¿{üì+ê;öè.$?_6{ÐÝußCZ7l˜xÿÝïfu‹^ÜøZÝ)p¢“ìÑÚµ¹Oœg¸sN‘§"·f£†ïÕŽ{OyÓ^>"»¬.·t ,º-ªÅj¶x™>áeº…‰i&¦U˜.h&Ʀ|VƒÎ0Ó*LL«€ü·BMjÆëtNgâÐƦñ^Êt~ªaxÏxÉJïnoÒÛéå½)Ïq3Þ<×f·g|üKÅBý‘baï¡XðNìÔ?VTÆ{´s=·€ Ï2eã¢RøÇê«éæ×mÃ-ÚUV%•µ8XñlS$ÓcA«¨fXÎx+{ xë#ëÞkzx¢¦¶/½æ >~ÏÞÚ•ãÊ®í^CnºrùлÞèfg©F€\X´ ^z Ç›ÙÄû”1™²ä ùØ ‡¤úÌ£ÄÑòt±A^(.–å mc»¿·V«sÔ¹k½³„YÊd­ÑÑèžì].,WæiËËÝó¼WáE,3¹©ÂTu¦y7_˜¯.3«ž /ÙAd¸ LÇ02.|—˜Ó"ãðʺÀt P<0 sì¤SwÄ*úJIšƒ¸ßI´| 5™¶ ³•šwìÌ"b>5døe¦r†k™üAn†a^IÅAýüÔtÎ|”a` çÆs=pÉÜR j©_ƒ.[ÊaŠr…p…ÂÓµ‰Vqj•€4dlÿ žÊÿˆÇnùõ±ûš¿Üz2Õu°uëM­ûnÜÚJœ¸ðöõ©?wýËOp[Þxýßýúõ× C[S‹ù<À …ðúíf­·v‰V§ñ5‘d„„#½ÌÑܲœ²Üa¹+#;"ò Ï À¥žK òLó,ϬÀy©y±¶Ü³4ÐyÓõž÷=ÿ›¡Ó®Ó¡S‘tÄåZ"§??HÉ_ªÍÐ>2ý%7¥™ìVΤ.bÑ´šÕWp\Åšª«Mj³ÊG # *=j¢ˆT½™üwY…î‰Ã]¬RZ‹²³?k±³œ”;býkÏpÖ!¬õpk9„ÏýØ!Ì6l@D2‡pxT¥_äÎ:„ì66£«zzƒY¡êÎqj¸Ú¹ØÛúØ »Ý||ɺ“×̸£ýñõž~bíš–Ôbá…m“&mOßûhêü­cuŸç;zäõ·_í” G§s§‡ âúí&’ ÅÞÁ¤Žl4‹595¾:ߎÐîPá¬Ô„F8G¦8§æ:çšBÍ¡·Ä·‹Ÿ™?÷j½H¾9‘SEú›Ç‘æd1y×üGï‡îÏ|¾'6Ì[\þ I²Š® ˆóXËõ#Ú°fÓmM¶fbwˆaÏÆ nÛƒÛÆ n3¸ml!e&³›ÎµÍ8 Õk˜ôXkÿg?bãdfkKÌ֖܆âkø¥rC[Ùÿ‡Ø}¶úŸƒVa{Æß; cV_ä=,)¾gÚ ©/W¼yݯW=Ò÷̆5ï]¿îÑÔb"û`iwêúÇoÿn8÷ìÑ£/½òÖ;¯ÐîF@ÍË€;zU\êÄ£|?œŸÂ/à×ò¢b—Y±8íŠq261–@ªR´CÆr~ĉ$ßþ?[°t½ot{…Fd‚è"Â0bÅJþxǨ#ÿdÄžÖÏ®> “C§†4gÒ^ÝjeG¿WÓ“¤ùž# в¸fæåC† |¹+ÄÇ^5zÐ…£jšVw¿Eg¡&ý)׳Зóè×ðù®üAʥʈ‚éùóó7)·+7<î|ºäE΢xü^Oߺ’w^Êü’^#¦ÜâsSñE蓾}ÊÇ GóëúÚÉÌV© Ý¬:^Œ‹i+ô‰bÊžô5ÅY>àóô¡b?k*¯°¸¢©¬³ŒÔ”5—‘2êŸ.@^Cße$1fD;h(`|‰)°1lcݳE2â;=Âä;šq§åŸÌšµ¾~'40yöø ’Õã3[¼‰ÄªgùÆŽO‚~¢µŠmñR[†É ‰±Ï›ÙæíI/ìŠ ®’¸]shNó-‘RФzCrA6Ï  ü¨Å,÷R¸¨PQÅ@a-—êYô“¿j#b'ãŠ[¶lA=Äõs4^ø§0^؇ô¯PùOG=à¢ç̘§¯¦ÕvË5›6ôýôåû& X|ç”k_˜aOš×,Þ´Äí. Üpøžé‹_¾öØ»ø’àÒÕóG\õÆÊÆl?jcQ81úš…Þɳ&WFƒ¹Nµ |è¦Y3v]ö åÓ‚ôW¤X¸yÐD*=¨§~N}(Í>Œ°Ù¢b¹5%aSaéæL6-åc‹#fÆiI®Uj›¤•R³´CâhN»¥¤Ô)—D‰.ÖTVIÆbÍ€¯Øææ˜o`RÝР Œ®ýÔµ“QÍ ­Rê KhYð##•}\Ú]­¦¾‹ž¡Þ^^®½j|fó[DÔn¯´S¯·‹N=Ñüc«¯XVrà ûöïw&ŠBï҆̄ÌÝŽ¥e©Û¶wÿt\‰ŸÙ÷ ËNÑ¿:Š'D~º·–;‰8Ý6ÚÛr‡«"áIJÓmÆN· „¹¦ •»c^5'üÌVñ0+Åã`îç ‡*aC„7E¶5CŠ‹·Àú Ïfvï 9åv x7;ü0W³éíË ™ÚLö+'Mº}pÛƒm£—O迆ÜÕ½ï¶~£&M¹ãfRuþ`ÇO½Õ€žÙ÷2Re‹*YÀD(`§zKïÕÞ; ¤AW;ÚÕÀsýŒòíU*•ï{•ff…L#’n¤8“ªÔ•¡„ò*PDLïTòcÈ äNè×õ©@ˆlæ^¨H‰«U¨¿:R§ãé¤A®Wàd±¼XÙ€®ÂW‘òå*u+ÞJnân‘n–·)?G÷*wªÏ GÔÐsR‹ú*úµz½­þ}¨žGgÕŽêEnµÅÕJuÒUEÐî H¥"ûM-Œ‡QY·Q4ªˆÉP:´Œ©³tVX)³‰|y/sáhâh•ÖÔ0TôJU’嘢ºEE! ˜¸0†Ž¨ ²È2!X”T…CX(5cs¾¬ëºÒ¬¥öëB³@€t%Btœoúü÷”šºü¾îÆîF¿·ët£±=XuÁ¯h¯ºøè9=#—9wóÃ56äár'='î,Çø—©eÿu:ö&þz0u%ï¾aኩëÉ͆ÏXDHx¨ÃÁçf¿ŸpPÍ”Iã°“˜±1ÞbŸ‰òì…ì³q£³Íj8¿ai¥]gyÕÎadm‹6˜ ‹™}Æf¶c«¼]Íx§ Ag§Ÿ ÕÞ9ª½Å>¥¨¡ÌC¢Ìtáb¾—J.µÏ´ßnçì†?z$&ãP?•Ýt>£+á¼ -˜kø­õç¼hVœb@ñ9ñ¢I1Ye‡†œœK ÊS.X°1©XNX+Pi<Ø:‚%êÒ8¹Î4Ü6Ê~©c¦m²c©4O^èØ(^-­•жޝÅóJ‘É^„Š,…Ö"[¡£Ô5U:®’o’ïåî1?÷=¦ÇÍûѱÃúþñ]åSþSÛ'޳âwJÐ$Ò›Y¬‰Æq4¶¤³Ø‘!Û€jµñd—%9&ÙbVjÆY%΂Í1K{ú½’J) P_1³Õ,ØåU“=®&ìSùÉê,û2û&û6»jWy EŠ1?Lu#SkKgKƒ˜Úiz«?ü è.N@`I‚¢ª2Ø(ªf·ƒ|¯Û' è,côªÍyÉ.ÉÉîp$É%’ð³X]‹Us'¡Ê.x 8,9xÙf7[-¬{ã²,I”u6zÖ]uÓ,¸ÉBÖp–vü„®F&¨x…ºY%j;™¦+ìx…}³ØiΤ ¸‰ù‰9`®'öãsÎs ˜Jäw¶±Ñ z ü£LÖèýøge?ï0¾íf\ggñÖq=îâ¨r«U;"Yµj(LC]2<¥¾Í1GÈ¡ô)ÐiO!kúxêk‹8€FÙ7âì+ÔºdÅöÅÒñ‰~| ySê’åì@Žœ>Õ"EŒRGæë’ƒôE@„wƒ´:Þ*õ¥olEI‡ÑÒ…—_xÎÞ³§OíS#|„~¥ÞÙc o{뀣 •8Ø!ö'uõ7d÷„ñÉûò„ &Oœ*T¢\!‡ëRÏwž³9°-ßÇv1ô‰¾ª¶üNù>ëý¶N¡Sì”^·)6Ý]åçœJŽÅ¯õǃL[ðí&¹Ôqß 5˜ê­÷à{Õ{MÏ‘vóoL¯YßÐNpo+¿³üQûHud™ËdF»ÍkÅB¤ÒÍJ!›ˆˆ©*Ùg%”$@ Ñ$ /EN’‹¢"ð¨|6XÏ-Øf³h&P*ˆÅÄ™5U´›ª½Œ^VˆCŠ !…#–—-Ø3s.³™S…㈖€ÙŒÔ ìc¹Îœ¯ÚæˆÊuº +Ãsº8QlfŸY×­î:’?ærŒ}Ó‘Ì_'`‹¬ÚGÚÙ.ö¥ÜôÌþœF†ZéŸÕ@´û6ÛV™Q©CBI·Z®ÎE›Õ›[eb_¹äV™ó=UšoÍ«ÒØgC9U8?¯JуÙï[ ÌiÊöˆ`Á)÷Ð¥§’îq…؆oHÝ÷çGûKbûþºßúÞ‰A©ÏHN};ªï°òó)s÷oñ¥ ©FW^j÷Јÿ#C#¹ªËÆ™¸ ÏæM¢SwØ"&ÝÉЊ¯4áÏï=ê÷i4aF:[6ûlAl£ƒX¬*rM·íU9Ý¢B"E}+4IfÅá¶x…¦Bs¡e€y€¥¿õ>»©ÈQäínp48r;;çl×[6Ú¯v]s£e›}»c»ó×½êÓ!íy{‡ësõ××–ní[W:ÊR”Ûi xÛÛ 6Îæ»Ð}ÉàÈÈZPl6³²4ŸËéŒ9Tdlf†1“ f°ê¤G£M"} jAR<$ÁvR³ßs¡»ÚÉTÝTãÐd¶ã°ƒ8Úñ°6œj*½ÅfK˜ûš'˜¹‰æ´™˜¡Æ¾RÌ ©i D6`„Éë¦ßÛÑoÞ¼ÚÙÓ>ú7Mºü^­‹AÈK ‡,EÉ=·4)ImeôRÏ ÒÆ ÒæydNŠLéOqOYãJ¿ ²Jͯ¬²—íÏ©²ç_H5P}äã,4NsÀõƒ Cÿ\F4³kpIõh=.˜RË_|/‘N|Ø–Z6´ ï¦é©…OjE¥¶\¾¨û¾u[6­'KÏÿfï°†)TËYŽ“EÜëȄ‡§èVE|ƒžœ!hù²ìßB*íê××Y‘ù w/¿{Ñâ»ï^¼ènòÛÅ?ûÙb€éYúïñkü 2q(¤Ûpz–ž}çÀþ˜MÏ¿µCÏÓóüüÚwÒ¿ošó/®hz^XÂÅx4n†ë ŽãÎüÏÿkáN±øûì%`×-JHù»òwu½i¥i¥ùז࿼XXwÙn×Ù‡9ò¯;_rÝc˱¹gºßõüÍû–o¿ÿžÀr†êÂþH¯üË£+ ü?r½øàzÿ?×®ÿ\ÿ¹þsýçúÏõŸë?×®ÿ\ÿß½Øÿû@2ÿ„ ,*ú÷¸é–’ÀÖ–1c'Œæ¦Ïºlb|Dí¤‘õ“§]ZY8¥|Ôø†ØÌºS‡™Ìš­´ï€ ‡ËžãîWR ¢ÿ_ýxt‹y:?gÊÒiˆ1é1‡x+jAcÐX4†™›Žf¡ËÐDG#P-š„F¢z4MC—¢JTˆ¦ r4 G (†f¢:4MEÃÀ6# ÙP)ÂPrì`•ºQ?T‚ 1¡Ê ûSé"ýÓÃCW/ž³ŒýíW¼ƒæÿÍŸ|qö :“¾¨ ó¿ˆlKÂôÿûŸÞŠf§Ð¦‹Bz Ò¡vdëñkÐIÕ¦ÿ¿ ÂôtJ˜Žv ¯ úq€6gÔß)>…î…òY˜Ž«P= Ïâ?D}ÿí°&ýÐ¥n„O„t$„:hË é0[ñ+èfà>M¯‡ö·Òr#2éh˜“á~ > endobj 197 0 obj <>stream xœí{y|TÕù÷sν³Ý™;sgÍÌ$³ÜÜL2 l$˜!$a‰0‚$HLB ‚Š„E6+à‚hPIÝêú¢µT*â2AZ¨U+Z›ÚÚºÔ ÕˆV¥åWÑV%™ßsî$´í¯ïïûy?Ÿ·÷ܳŸsï¹Ïò=Ïs&"lêcó ‹@½¦$0¹°}e[gª>¹€´¶¯_ÜN®þÞÐþ²£ó’•'7Vè~ ŸtÉå›:Rã]?Œ]¾¬mé i«ÿ ùË–cƒ©NXX=kùÊuSã#&O]¾ª½-U/} ŸÙº²mc§nŒá}É„Á+ÚV.^ßl~窵ë†ç¿Èú;×,ëô¯ú ŽÏÇõ]ÉÀßé˜û¹%àHŽïmÁ>ìL&éë8{ÞpL]ó04ÀQŒ jéY¯c~<ˆ)»È¯¡²°ük¸{–ÁdBÈbÚïÁX Øc!:Ð@Kò¿`)‹àU’ ¬œ‚>w></ò™ÿ8ù$Qî'¼ F˜¿¡X ¬P€ï²Y4À?Œol€VØH®æZ¸$oOž-LÖçÃÓð 1%¿áMpq†ÖÀcðüN‘LÒDvà°Žc¯BvÐ#C×%÷CŠaÜ ½¤„;Å·j y Ûòa:ŽÎ§]Ü"î1^ægòóùþceð½¡'oNîOþ¦âì­p#Üû!A4ÄC&‘È ]ÍU!•L|„¿ékfè€ø†ÛáaxWô*¾e<¹ˆüÃÛô ®…çù)ü ÉéÉK’o×FšA)L„ p}ËáRØ ßƒGáJõqäß{$›DI ™E‘5d-yœª¡µt½—öÐ#üµü~þçCOº“åÉ|ÖD\k>©VÀZ 7ÁýÐ ÏÀ‹¸šwQ*ÜHýÒBZÉÈòy“›ÏusŸñ0Ø=4eh×П’ý@0Påø¬4Â"hCºm†«a'ìÇáWø]D~C$@.&W’»Éëäù Ó ;h}‚þ˜þœ~Ì­ç¾Ç=À=Å}€_z=¹4¹>ù8Pþ³qÀ”Ö°A}úup |îE¿¬¾ãøþgT^ñDK ¸úi¤¥§ßÚB.!Wíäfr€ü„ü”ü…|Ayª¥uÐtº€^BWÐKq¿¦¿§_pyÜ$n2~éƒÜ ܯ¹·ø?‘¯â[ùÍüÝüãüÛÐh5Eš-Z—þƒ}C³‡Z†ÀÕ†’›À>”Á0JáX¤ÉD¸ez9Rö\ç~Ô¨wàRãø3|ŠëÔ #Ï‹H òmYN¶mä:²íd¹iþé!‡Éóäò+òyÃW¸rͤ ͦ%´œN¡Ut:†™t!m¤ËèFº…^‹T½ Ãÿ¢чéú$=LI_¥b8ËqœƒŽÓsN.Ärã¹"®˜›ÊMçfq:¸ÍÜnw×Çý¿_Ê/ãoçDééåßÓÜ¥yNó‘VÒ®V±f,„s.ÒÅ4åq=Í#·!µ3I!X„ x™äàn%;i ùˆû9Œ+Ö’j…*z€ú;Ô°Û¸€fNòZDI ÿ1ÿ±>Ÿ»€«¦õƒˆ·dÞÐUðe’âø#d:jÆdø÷©ç.£UäpüϹœ½{ð§š î6:@î þƒ¯þðÕϸÌäeƒz˜™à¢dvr3·N3Ä•@ߎú}5 "¾½ˆßö9ÊØVĘAP†Šà#r^"ËQ7¬ˆ.!DÖ)G½’_ÓF”<=êÆˆ9QòÝ yÔ¡t™ø)ˆäOž=Û¸r—|ˆ¿{îº~M¸þÕô§ðç¡;èNúúWÅI× ‘Òu i _À¶äõ°5yÊàßváþt7LÕ8yoÕVqß×”ÂTí4€èì¦Æ….h˜?on}lÎŒé‘)“+'M¬(ŸPZR\4~\áØ‚üpޘܜìP–’)~_Fº×ãNs9v›U²˜E“Q0èuZ ÏQù5Jmk0žÝ糕3 X]iƶsZãAlª=L<ت ž?2Š#;¾12šI¤`%Täk”`ü•j%˜ ‹æ6bù–j¥)?¥–g«e>[­ˆX‘eœ¬q/¯ÆIk°&^»~yWMk5>¯Ç(LS¦- ò¡G0bш¥xšÒÙCÒ¦µ@Ój&öPЋ¸ª¸W©®‰{”j¶„8ªi[¯ŸÛXS.ËMùq2­]Y¥*n «C`šúš¸vZ\§¾&¸‚}ì öäíº9!Á’Ö°i©²´mqcœkkbï°†ñ½Õñ´ÍøpÛ´Æçö¦s]5îAVíêÚŒ?8·ñÜ^™¥MMø œKCµ­]µøê›Ý…¸¶|ö)©Z¦Ô°–ÖKƒqƒR¥,ﺴâíŠÃ¼MòA¯7Ú—<Þš`WC£"Ç#éJS[uFºæmêõDƒžó{ ò{$kŠš=fËpÁ$ž[X6Ú§–Ôá¬T7o”œ„­H™‰b¶q% ~H9K–•CW{9ë‰à¬øRdʸaZk—4‘µ³ùqMHR‚]Ÿ²]9õÉù-mÃ-Úô°"ŽQÃþ‘r<Žçå1¹ÐMCF⧨õÒ‚üõ ú€Ò)1CòA}#NkšXˆ4—eÆÕ‰(,ÁJ|ÛÜÆT=KÒB´0ܧ­¬çèHsëÙ6Ò3:½UAñ}˜©íŒë³Go‹ä²×,Ÿ'®ѽ,Õ_7_©›»¨1XÓÕ:LÛº†ój©þòѾáRÜ>­‘K§Ã%šÎ©½(‰‹G³J£)·ðÖª’¼4¡Ó£(ª-$X—Zg¤Ò&A–ÿÍI‰äi6K;ž6¼ÌøÄðùõIçÕÏ[ž©‹ÃóÙ´®aQW—pþÒùׇPÈPÜ —Q<‚X†ÊŠÉ…nµ¢ é·…êHMïgÒe©‰É]\ ¿&ÿo=Eû­§¨Ï`ø7ªO0²Þ¸9·¨©ŠÔ2Þi¡¸Ÿ/UêϦž|â´ªØq’¢Z}ckz[Sv³wĵ*]RÏ7©2«¯Ô;õØT¸x,Œ7ªWÓ5)•’SÓιð \6‘fN*ÈW°j)˜­à-Lš‚­¨?¡®òtEnJ$“­ [CÁ´5dÝ]­XTâóóXov0õ¸5» §qlìœ0SŸ.TË–ŠŽ$Å‚}„¶fwá‚Æ×Òå&$[%‘NÎ8h„e||¼!‘?NPçYÔT:ZY¯ÿ“·ØÔ‚#wªd±ª·mô]•Ò·ÞF¾õºQ¿ùºZÜØººj•`mWkW["¹m‰””®>n1·¸«³¦u2ÉC;Óãµ7ãwµ.')Tõ(䯹=QrãüE}hŽolh<ˆÞÄ´Öª¦ž,ìkì â¶®¶ÒÑVV ²ÔäìAªW»Òû¢ÛÔ^^mPëí j›~¤@{‚¦Ú$µ ¯`è¢Ý;xz½¯$×uh/}¹óŒN6†;€I½ ú#¢'ôÅd}^z°Éf™êç–â¸BL#c9°`ÚŠ±ãQŒ<¸%aÁ¸ #‡¶QG¯Á\Mp-Wõ¡¸åàÒ€å½wqTygi ß×/÷gögõöÇú÷ªïwõûûý¹ýáþéýÏö'HYÔªïW^){¥¾nÿ¼þ†þ:ËÔRn!¾ìB|íB4c/Äå-Ĉb)†¥­wc< ¶r pQ °g.ª•k€NŒç5À8ŒQŒõGz¶aìÆø Æ8×5 áb4Æñ¶Â©“qñŒ-wa<‚Q­t+lÃH±o«Z;ŠñF éØŠ‘‚SV‹aln=€ñFÚîmnÕ Kr-ÝŠa Ýòt¡¯3P†1Ašú;}¤‘¼Íë <[ߘUدt2±#€c¹  HKCfÛ¬úèT3úà.´êEjc)™©¦~5õF½íâñvñçíâOÛÅ[Úņvq~»8¯]Ìk䢨w¡øáBqîBqæB1²P,_(–.‹Š Å©VtA„gÕt–šŽWÓL5 ƃ"ä–ƒÓo&‹a:ÝÏLmV=DÊÀÇ“§ŸN&°BN°âbß%˜‹aYÍrS™ÂŸ,(1”&t?CµH¹nòL´L×ý°®ûb]w…®{‚®{¬®{Œ®;G×­ë麃:‡Þ¦—ôf½I/èõz­ž×S=è‰ä‰h˜©‚C+±L˳”WËÕKWu‰=…Y·su´n~©‹m‡º%Áøçó•p Ó(U$n«ƒº†*w|B¸.¡K΋—‡ëâ†ú‹{¹µ kqz#*pCc‚$YÓötf"ö!¯r¶ß’Îòk¶ßÒÔ®õwÄ6ÅZQ[ý’Öá4üõåŸ{ÕÕo:êAÚÈ~5Ýuéq]àGºÀ]`†.0Y(ÑÆéΙ5gu«³ºÕYÝê¬î¸®ûGºî9ºîºîɺî]÷¹³Ü¾ø]uóãû|Mñ"VHúšêâóƒ‹ñ»¦Óù5Õ}(u˜55öÑZ˜^3Om¯­njªCVªãè‚Ô¸†áqB”.`ãè!zî8ðc{uÈ,SÇ_}žÿã|´ËbYjœOç§WÇõÔË5Õ=²¬ŽqÔ«cê ŽáRcäsÆèN€¬Ž‘u'¾5Æ÷oŒÉú‡cÎáܲªð¿¸jV0É«oìA'¾ Í15wIST)cSîM?¿å>#Z¤º4F¥ "wXª$…ZS\‹M:Œlô$Ù½%ýäQu´ ›Åᮂ©SYêë23ßh¸Ë½e’œ~ˆ<:Ü%a³ßñµ4Ô¬¨NÝk×]¹vÝZ\äa÷ ×6ÁZ‚­k×ááµ8A­Mk<„ûH uMa¶3Qfk0 `ãž%[ekTL8䎞jà+òGQ3ÉãTÇ÷ðàÎõ³èÅAP€8°]0ÚE0:ŒÎœìlÅP˜òû|Š-;;äËÈÀ’Ãép8œ>!Îpføü¡QÊ3àìT¥ýôà1ßã‚ .£7—)A¾ŠŠŽÇ³Ë#yžq¯üÁž#iž}fp"á3• ž©´¦U«-­B¿clØ|µôæîÿ©0~±—•¡¯ãB©‚V§a½VÉÌÉÑaš]Z2a~Oi ö§Ñ/'ë|v³›BB£Éžæz{ƒ!èpZ6¾•î²Ø=¿O:Å€]°r»H ൛ Ö¡?6ií¢-ÃÐô¡Ýh øþô'ŸÃbMÿS“Î":Z†v/'jÍJ°Â¤¨i¬‡Á ˆ¶×aЍ¹Íˆ9qFŲV¨mÑ®ÒrZÏĬTÕGÞtTZ&VáÙƒ'Ã(`ŸcéÌ)­3gÞǾ0DÂáÈ©HåÀ)é”ÕV.üüÔMJÜÜaÀ¦ u’¾Ò\Y©©D–ÔÅMóëâ!ôÀž°ù­Ji"ù‡òòò&²ºV£®>9Þm"¶+šp­N·µÂ‡NGÖŠñ.|¼$V k{i†æææÕ«å Üf29¡¬´$'[É–Ö² #’g…SûœWqQÙ®Éóú«7ܸñÎÓkïöqc§¬m;–&ˆ6ß’Çv}W»~u†C–/_ýÝc›.ji_ùËÍ›îݸè¦%Wn´X&*ã*u6‹Ýï)Øóz_³JN?ù¾Ü\»hê¼mL4JQ’Ž 6ø Ÿ4GW:%%â”ðÃLÛð3M¹XÒæÚsǸîqÝÚÃïÕí7î•ÿîä·ç¯Ë¿>w{þ^ç!—N"…8e†‰ß’E~AÈöü;ȃ$NŽ˜žÉí7½M¾"†7\¥,Ï$šŒ"ˆf«ÃéB {:0|^Ô^&mb`ˆ¢ßér8.’ «¢W,×-s:¿,M NŽ–ˆb®Yp亜’"KZÒ¢=®¥ZïX“sˆ¹\’3ÍY HN‚u§§@š¸×Vág5Špålé³J¼ ˜Õ³O±Fé(Â¢Ò ŠKxT1Q¥$¼C/Uš¯~NϤ£åÃ8,} O~Ò‹ºáguq×hA¡i‚Õkš›™Ü„Ão.ÅdÊ#NéR|/.¶HÑZZ‚l×2¶ÓÒõGßxàå{‡þrÍ;Ë•ç‡WŸìd)Ží¾vÁÞ«—ܼ—ÿ`ð7/¼tßMC~è…=6Éáç§`øê‚™›z–nztûVg/ê/‘ÇÇ-ʈîI¯d1Dʘ°F‘Ó-ιe{J¥=Yz,í9ùyúU¦U" {óÇçUÝ8æÞñ{üy?oðgvÉGä#™¼¬‘€HÄH›£^ó,ºåàS=*z¯E SxcNå('ŠoÍaj,Á|[Ôå-…ɉå´äìÊ9‘£Éñ–gÈ… XÒp J…4VØR¸«ðD!_è™°þm÷0 gŸRùÆ”kö™fT÷S 2íTä̩氪À# ú1ø’Ÿ€?ùð&?Qb6•Œjœn€‡5R;¢uÃýZ^ížB±Ê_f2Z¸“ŒÑùP«(‰6ìÜüÆ ?¼½õ­e«Ùæ˜>ô~÷Þ—–Ýwì;mwqb…ÑÊ6“Ÿ¼6“Ö¬×W¯ÚðÉÚëvOŸë°šÎYWüìþ oÜÕ\ZØŸ<®1©Z&§ûÀ‘|£ æL`ŽÔüï>ãŽ? ¢6¯2‘«—K„KƒÛsôWæuYw›˜“÷fíË{‹‡ÍÏȇB/_‡ß‘„¿‘/CŸçÙN4Ð0¸ z šªöÎøZ{³3œ@Á$+BNÈÉAØ bq ~tÐÛ¦§R:-0]^Fb¹zƒà—MF“ÉäÌ–í:’Ÿ D2bÄ€²k#-QK½…;m!-–U–­–Ý–Å“OÒH¾€>œD‚$Jê‰0i%§ ßÈs‚ˆ·ÀPu5xóéÝæÕkˆ»PUv£5ªÞ“Âðfé#͈l“(Tà ¨8prTKo¶g¨ÛFEÅùÐ_ËÝAÁ<{÷`®#œH~ò¤ì zÀTžºpCXÍd„GLB2‚úÊðaËÂ-â[À(˜Ó*Ü¿uøý»_|ˆè¯yûò¥ÎLyí¾¦~ÏÕ=Cìá?øj%CúG^üŶû=/’·R¸Pwu|ñ–ì½þ†ý¿C\¸ q!€š¸¥Œ(m¨ÜB"•ãŽöFÔÍ´=` ‘Ô’Á’ê4`A`5#«]…«Œ`’ ’Q2… )hJ†/²M¼çÒljËs‡=ùÙ÷8÷ù4Š!h Šœ†GgSà*ݕٳÝu¾ÙÙó¸z¾ÞQïœçºÜ·Éwgh¿ç$ÿ/-È&8”Ë ú™U›¥•íÆ,9]ŒA¾™YrÄówú»ýÇý¿wLN>Éç䘫ÅuÜŹ<¹/6BÏ 0™=pF…ŽAkEa¥4XI[ÏããŠ#Óa8”<ƒ@ÿçƒ!‡¹ù„Çã0ñ‰ä™òòQŽž»%ðÉžT2eSffù¨ÙͤyZcÔ°Ë}³gWö-9¹5q¼õW6ýèÕÍÂÆ;º¿œHH7œ\b±[lÒø–·]õü®Å/=P"v5.Ü· wƒN~#bÍDR[¬G$± ˜YÒ,nÈ‚Œ¼€Ûš%å•ʼå™åŵ†ZïŒÌņïß‹]cŠHŽöMßã5vMHÑLE‹©çt{¼©==/ZuGa›<ÛâÙîþ NÙáËRûú·;†5œ^|ÅÒï7]{‹vU Óí¡?³´e¥æ’e ß¹sõ¹m)·ý¯k¯ïH÷þ©9Ûù)çJ›uqdûà'#-)[µüyäw@cE?1ƒ8û@“üí¸‹|u`ÁÀœÞãüЩ1‰út)®‰&ƒVçxÄgxpጃV¬h4þ”OHI‡Ÿ7*fðZ“(8|¹N‡D“ï¢v‡Œ´w²ƒ!¿951É‘æð Õ¸÷GÔÜš‘ÊSÛÚѨ›­,ê¨pDíàètÄ'¼Ãã“ÑwR¿jî§.fËÍ~ßóÚûÄÍP»y;ã•‘ÊÏ++¿6áRvûûì£Î¥¢1*ŸN™`çäó¸#p‡{xìUc•’YŒôƒnÕ›uÇg s5Ö7ßš{ö¯£LèSIŽ4¿th ÷W•æ~bí‘}ªñ'R¹‰Ñ>€L^ÞkÃ1õñ/ùÞôzb2ê5<ÇÈí ÌðJ³Æäâý~¿hrˆ¢‰Ð.Ew›Ñ¤õä{ý6›Ý,r‘}çßcìl.0}ʯc~—' ¦‰­Cµ'$ÿ ?õ¦ Ânò:ÒWhV ;G˜-!A¿EXip€Ñá’9±š‘Ñn>ÂQïÊp¥»|hÇ“zxó Ó{„ΜV«¥º¦hú;EŠÛ‡DFj:ý9÷ÆöªyCŸ©„7\V³ØÅÈ>x–‘úlû·ømg€TÆ–£¨=‡²îGlsÃQ1×:K3ÃÊé &‹dMIõy–‹Ûi³JŒÌn¬â—¬I²ºÐ"‘<¹V‰¦'ÀJ–ƒ„ôr!½@ŠIÒƒÒ I#ySÊ“ÉlBÜ›Y~ÐZA™w:L¢‘ÿ’ïmßYgôeø2I¯„Lö-#דû}|‡üF7Èýy[À-ÉRV‰¶VÛ•ycñ½>s&j¯‚äuÒÿÝ ÝÉ?Ãvy"hUl·“;ÏAulÉ  ÕO „B„sNHÐ÷ª`>)j‚OÇo+î.þ•ŠäKG‘üÔ™Á3Íƒç‚ø Â89½{´tZƒªì'z²J ÜĘ n¼DÃoB¶òoC¶%ýŠû}çÍêéŒOï²dúJMGË›º:ÏEì;Ô~nÉÖ‹ª®_áðþåkÀNsÎl©½vð¿¾ï¡]örX„ ø}J߇ÈS4®·Vhr­ä*ÓUÒwWfÜKï4ì3î³2²¾h|Ñú‰æc·Ù•>Ó4Sâ‡&£IÓ¨]”v™£#c³cCÆ}Ž;Òa!CÌÒ„Ü›5›Ó¶kv:¶»¯ÏøÒñ™ûó £Y£SÖélrDˆ ¸µ4‚×ï•cÜqŽrߨ¥î£³S¾×7œ.LiÉÀüè Ý•Îì§Œ{© m_f­AhFŒ]Õ+Î)*K‘ÝL™¼xÏk’´]?xí¾¡¶¬ÿ`ëã;Ú¹¦áqùÕ‡®#–~ußCg¾ß°ýíU[öÍÞ´wÑu?Âù½¡-ü³H·Œ'/D;¿×Omoù‰“ä™–¢OðÓS9}GÆêªlÈÙæw;>ŽÚ Lþüœ‚±3}ø/(\ ëáz¸%çæðíp§á¾ð~xD|Ä·Ïÿqž¼\…bÎ ÿþíãô¤pœIÁéòdøü)…p9mfÑ4¬¡Ì€ßÇ”cY`ƸlžnøüŸÏm>jA¶,.³/ÍW$Œ‘uÛZKA·UG;utTç-!æ÷û°õ€ïˆ¯ßÇE|-¾U¾­¾]¾Ý>mL­°âŸÖç)òû¤©ìÙ«b‘ê*¯Nu¤Î9©ƒKêÇ041w†ÁÓv"öÜs©0ó0+ŸñÉ“P<ÙVr½#öì´Æ§.„[‹‡C+v ²sÍ4öƒE?Ôe© z±Â'eX#~v<æ“ÒÌ@ÑíÉ]¢šGÍØk‹ZK¸ŽÑõˆA=;ÕQP2s˜6þCuÄ oÔE"ï[¬{ä…¡Þ·N,»üå4‹£ÎÔ Šê;‘[z‰y϶ޡÿÚÃ-¹§yÑOïyyèÐÓCZ­Up²½ÄÏObŠ8¨'ÚG®Ü=ôåC›BÔíEÔ=Ëm@ÿ<ÂüóÑ“1Úy‡!Ë“Î*Ïj€e°J·J¿JÜÏÛ>Ì2›r˜Å &b±Û‰Ý¾Ì·wωÞh»È¶J¼Ìöq«m§mŸí'¶—m‚ÝnCo–‚#=#©d¥ u:¸IFºÛé°C¬%—@87”¥°º˜AmœUc1kMzA‡>¶Ã¯d9%+C jA=„¹9:ABYsÆÒ3cé‚’Ÿ›¥HbLÒ‘VÑy –•åˆIJš’/HJ½BA9âÉÇÍñdOëygg³ß¯”ÞÿæÁYå) ½¨fDâSçzËh`С‹¬úÇ*î6 »ÌÙe¿:0œ•ÿ Î*øyouõÊ K0YÒ Ýå ,!ë|ŽLype†#(¿|ç})|}í®ù‹½Î Î®x÷•0LµZ>z˜ì ì ÄÔkêÉÞè\>KÊ¢¶{Šö+:Vë#k«îv? 8Œy¸þ øñ˜'êÂKcž¯ÿ2Wü$÷õÔ’ª¿ ÷yß+5šYÓk¦e2¼úì9¦›9mTÎ6–%È´¨¡@pÉstÓd&˽¢K•é^SJ¶{U'EÑBÄô¶˜¡ ô¤STBZÏë½ó¼ò¬(ΙŔáô,2ë¡â1±¢ÊXñ!”í"4?ìRzŠÈª¢E§‹¸"Ï܉o»$gûÈ¡;SMãÔéØpõhôtìc‰êÐŒS>•ër¸œ®)h¡=Qëšá@½üdħEn¡Óʼ£F?sû3ÆaR›HLù†ósüÍXðKèøg¸Y’H9Ó‡óàp^ÅΩÐuŽ‚ø~–ÌZ*ý!ü‹)Ùù‡t_Ÿš;i.>MUŠ´£?ûŒÈîtiÉX‰5òc´¨ÞvÞ¯þDȸéÛzR˜¸*æ]þÎÎö»Kõ&«ÃlçBZ?ŽÐNšsWïá É+«." ûE·²²Rå¾f‡ŠþìÇZÕíaøß¬Ê7üp"©þ7$?vÙ%+Ü0_å¢Ô/¹Š½˜|³‰Æoí½PÝÁ'eŒ²¼óÁyöfµ¡RmXÀ‰Œ…zmßÓSö´ZQÿ–ËùOÑÑ0ô>P…sr'5²6ƒC÷K}†³ú³†SÂMƧO›¾'þÞ7Ç-ßûOøOøOøOøOøOø;°¿HSÿ[Àœú§Ó^ŒZögµ"W»`Fõºy5³çÏ~áM1øÿéâ¡VMyFŸÓödSÂRöW´˜Ú@DŠÕ˜հê`ÔÀl˜sa:\@ÄÔÑ6¤1û[[-–`Úª+׬X¶&8gÙ†ü™ëÚ._ÑΞOºÙŸþ›—þüêi8<¯ À+p‰þÁE ðòÿ…pJáKè‡ß©¡ž‡K1<‡a¼—H@/¼AªI5³ÞSrø/.ö}ôdGOüÀ¡KågzCŠ ×ßµåOÛ'%×ök/ѳ¿q6ŒÐ㿹„. endstream endobj 205 0 obj <>stream xœc`£`Œ\`Qþÿ÷þÿÿäÿÿÿ1ÐnAiÜ endstream endobj 11 0 obj <> endobj 198 0 obj <>stream xœí\y\Gyïî×Ç»ÏyóæØÝ™ÙYíÎÒjwtîêZY’¶dI–ݲ-•ËŒq¸lsÛͰ‰ NbNŠ…â8å$¢RÁGB’* ¨@á¯üER ›¯ß¼™Y«’¢’Ê?©j}þ}¯_Ï{¯¿îþÎÞ*#Œ2Ð{†üÓ¿~O½€žƒžgß>wéü]µƒg×#„ïEˆüûù‹ï8‡Ò|¡ýÂÙ“g^yä/_Aè_}蜿ø»£ˆ½÷#îºçÞÎóO ôÞ/þÚé“ûÚKí}÷^Z#pž¿ ÕKwŸ½ô¥á«ÖÁý‹0æOFÿ«++ÿ3ŽläüÊgTèñôùŒWPµÇ-èËø¯øZF­áêÒrõ¶sÕetÃÑáåÅò2>yìì®ÉeÜz ‘¥úîÛöL.hkK&—5hèY'…¶!;4XÖÉ¡Íe§€†•uêжe§$ë4¡]4rY§ íXv:Ð(e.´Ë²Óƒ†›uúÐödg ë ¡ÊΨµŒóÁÜär®…ýW&—ã’—| Wƒz5Ü]ß}øèÆòð±É夅®ì*´ª»å’,WOÂ7‹»ó»R«º\˜X®Âeh'^ZùGh´ªh#Þ8¹<Øž\j},íú¦ £`¬ Œ5`z˜1Ìf5€ÙM`Î0w 0o˜?,h 'ESÀrÓÀâ`ùµÀ’uÀ ³ÀŠë•怕ç—`+ J[вAвQвIвYвEвUв EY”¢l“¢l—¢ì¢ì”¢ì’¢,IQvKQ®’¢ì‘¢ì•¢ì“¢\-E¹FŠr­å:)Ê~)Ê)ÊA)JD9$E¹^Šrƒå°åˆåF)ÊQ)Ê1)Êq)ÊMR”R”›¥(·HQn•¢Ü&E9)E9%E9-E9#E9+E9'E9/E¹ E¹]Šr‡åN)ÊE_ÚôwV¾ƒºÆ½r¥•Wï’=#¯£¯¡\ÚÑù}¼‚´ tõKhÛÁ£ÀƒƒG_Â+-£‡Ëé?|¬¼Œ:võK]t/ãGŽ}qn=šøâºµÀ¦§€µ&€­ÖV¯«V€  ‰—ÑDKõ¥Ûv½ cVQ%k :ªeí&j Ñ¬=ŽÖ ±¬=‰Zh"kÏ i4•µgÑ:´6kÏ£9´>mñÀ~õÚk€íÛ ìªÝÀví¶}°…­À6o¶¡-Ûˆ6 vö‘-h3Ú”µÑÚšµw íh[Ö^B»Ðά½]…vgí«Ñ>´7k_‡®E×díƒèÚßìÎ;`Ô ç=ìÔI`·ÞìÄMÀŽvä0°ëIÁn@×£CÙGnDGÐá¬}CG³öÍèº)k߆nE·díÓè:™µÏ¡³èLÖ¾]@ç³öEt'º#mw´á ÐÓ@Bí¹ÙøLûL» á qøízy(†—+ 7Å—žþùÖr4áÎI︛Ÿ[ßžŸ]—äãœàõZs'\éÂ4u‘r-^/LaøVÚ‘òùGÒšÔï´ýbX:^{¾=ïáYàIÞÜj±aèŒeÆ QDãØÏ¦a:Ž>‡FÑ· Zdo7Û )T’9bAmÃäœñ9bhŽ¡iœ€÷’•Ã*¿q¦óÞ|{äNò‰hÌ%#I¨ûŽ(•G.o(µçÆð–vgݼ·Þóºï5gÓÉ‹:Ül43©<,$Ü2™ùÕ`ÛúáJ~p¢Ôš*ääûôAôÙÞ¸í¹E<+Ú±GÚ•q–£&NèÞµ {ä*²'ãÊÁB*ÈÍÞÉ‹æ|g–éÒG¶ ®`‚3Îóš¡ÛŒq[¾[…|´‚våsíxiôì 4G¼ôÛÿŒÆàËŠÒßåþŠtìo¾ÙxËûðY„qêeÆL“C‡nê4]›ÒÊ¿ ÀÚèÙX leª¥Á‘†Y*’hûÁ´ÝÞ¼9Ý{}åGè“èoQ ;Ø]ÍF;ÝxI"U%XS_öέo6ê5Ác ×òƒ¼ãRÊ‚Ÿ "߬å !8Ìp„=\ž›reAX®ÎBL#çFâUMQš,ƒ¼ë`/ï‡ñíž¼bÏg·Ã„7F0¦œ>+¶q-àqT;˜ÎÕÙŸF>¬¯µ¡=Ív*8L>ØN5#ýh¾æ‹Lã\¸cžeµk*.,©¦}—2ªi:(^òŠ©Áº]ß·óÛ1hoÛ&X7}ËnäcîZ”a,¸+åð@ާÐàM»+˜Ž&×iÕd²Õœ&ùÎR6–q/jÛ´²W°ÀN±0V\GÀ78Ž.4BixžO³ªñbTˆ=999'¶òÐÏ×P¶×ü›è “N,ßÕL)G2Sl ü2¥Q\©çÊœæê–!tØÖTóS3Ñ‚!ìZ½Rãà"¦[wbìxè-Öʉ%çiÁ<Ÿ†ýŒº#’ùvw¹—*“¥mMì’ 3–¡‹ ð½ Š⟯MŒÒí#c£°/ø«¡gÁ¿Ö!’níùºFÇ®å—SçÕÓŽÚÊ™Ig˜­oC*Mg†²76˜>àÄABÁÚDÔ—èn’$¾Rª¯Uå\ç‚h>è ÕÀFã°ÌûËp½zÎct«¶iÁCnèøž›Õt;Ž"ÚTíf -x†Mרep¹"]›@à ûV×Î,­o5:âgb¯s„ ¾eeM ÂR%† ˜1[؆Û¾Ægئ «ŠcÇ1Aæ¯)9z>Œ‚p0ˆÃð~¢ º¦I4Ëò<+‹a M ‚­d1SzQ/õ¢xQA#¸Ãàc!f^áSWÜ0 5¤“eºÎàŸÐeÛ€)HÎÙ)Æ (ÊGà¦ã{ |$ékj"JÌ'Üc§í#7È2 d­@ ôôNˆFïɘ‹yÑLâÊ…ÖæÙY|á:â}–å°Ó‰yå•ÿ¿.¿õ2šÎŽÂ|q³ˆòñãÎé3üð ΩSr ¾ÿû@›!iÊ´.ÇR¥ê¨XG™$ea¢»}ûjfÎc–÷-=¹Å"±‹!ðø„XV€ýXTsÝØsLÝ͵!H<¬b\.CŒ–<[g‰çiZN7™£iÔ° Ð*þAÝ ‹ÔË*…ŽMˆnxQ¹Á ²¶©q׫“¢ ®Ñ-4+¶ šé˜Æ|±$,ˇØbX¦s¥Q.õIϲyTp½éá&¸?9籺 ŠmC~T˜‡:-¼©1hâAÛ0Üñ‡¦ìa\žÁ¸èøei¿ ø©‚l òâÎ×gSÇÞwí®7¼^¬™ï5ëÜßš8yCçÔäºË qò¼|¤“Àcµ…ç–hhæÀnÿºÐ4xe×Ü4MÏw}Α¦Âõ¥‡è4¸H×gZG/èÅÓ©oé[cèø[vUîb*pNÜ!Ý­x:¬|Ás„9Ve,gÛ˜¬I¶Žé{ 3Žes³”8¢˜+X‹ÇñÀ¶É"¤ƒndxYÌù%ú8äªn/æ$ÙÊtÖ¦Þ_†&·†“8rbŒKQ1HšE=o{†N™M¾®3BìÜØ\cÀà/ˆwj†54K´N~¹ò t½Ú˽  CªC,‹¸bþ¾™î»Ÿx?&ÙÊæZ:ljcbÃ8ÊÞÿ%zÞïæ‹mùv¡„íar#Ý—8Nq\†ùWG­µt„Âwà­CÍ=éÿ=û?Ú[cl Ê¥+,c|ßo÷¬ç4]lR…„ÖV>L¦)áz®X² þ¸b*’ò\… CäèJ¡W0uB7ÖGãÆ`J¨·ŸNõ;[eþ“ s\OcúÜ´»Flä`WÓZÙÁ¯ã3äôҚʎ}Öxö(º^ïyùjg¶@ž5+À^Û:>ºo™Ù ÏSWæ“]«jw|Åü\;É¢VG»&±¥ËTS6D¡.”l‹d¦ùcGðö`|.²Àä!·Êg{"c¬ßý®hKg)?Õg¿v`qãpìDÞ8Ö0{õÚ{ ƒæÜ‚f‡úëò^Ø[ÖÿèÅFý¾Ïüæ³xŠ¿‘=üK0^f.âz·S§Ÿ4ÛÙTv:Ú$cå€̸Óá €‘íÍóøü9ð'ïfºýe|v»«Aú$úßÖ5èë¨üx’.oSV&ލxú¨hÀþŒ·µ Šß{-ÆáÉ»ñÜÔô&üèc{ciOhåÍÔžúùNjºŽ^—U#S¼Ž¡Ÿ®Pž »aä&œ2ì‰"Ò.ÞS§¥êæ…Ä4¼ÑR£‘?1Q×´By¤æºìØï zÖ/@ƒ]É=\ágûù3ÝÎxûʤiL”œÄ±!V¾“«Ð@Pð&üítáÏÀ±{nRŒ"ˆ‘·nƒn l_ëÄ> öý©Ô—Növ¬y…養mUêÐ SðÀSIX®7.˜é”Ã\à BÒ²+q`U!pmÈr°4c0(^,Ç6$ôÖüšÚ¸ ‘k'‘zÖ€¸ž‘ „Çf2{B™?ûÈ÷*ÔCÃ=ùê‹«òÚTG§±˜ïe6S¸éÌàÐÀŠ™%ŒZÝâ†ñØ uƒiPë.,~C Å’Rɵß–Í wóf /° "}]þ0äï3o±µNbÛuá™s©w³]ˆný¤1:®cRê@`t„Æ=·Úô`=ð($á.¤›c°^º“+FÕiêêBè2•V¹X¤,®žsLYßY¦çðˆoÛL×HÐó¥R_DÏ—j`rOâµl‚­'¯ŽÏâϘ3ÝØô&zžìW#¸»ó²äîxlja“kö½H†B›°^N<>`ž±WŒCFˆ£[æÚýâR®à \² ºöGПÂÚy«r¯:ØÝ4Ø  HïÎ7ZziJˆQ£œ8äöÛõoÜqÇÉ äm—0ö;ß9ÄïOêÆ™6| ÙEìTÐO ŽLniÕk“Ïs-¼}'¾x]në¶|Q­£;?C¿«3 y~·ÒoöJi:éd»I’šo»_*òdÉ)†ºçæLXL'¡ÊŽ<Ãtó†% PÔ¬„Ci É'Õ¡e÷XyOãDótÇ÷’ÐטoæC/gOŒ˜ëÚÈŸ G§ÜÖiv®ô.°½°WÁAzЯ¥ Xƒ´ùxu± # "SßèAÎ0­êr›ë …"áÃK|)Ô´’GL  `¦ÃÓ$ûSOЙûÁ;ös”¹U¹g÷€â­ÑóF¨?t]×d–¢:$CA`æB·<$´c&‡€ÊÙ>5uæ{>d—P&û•µ£º®¢ŽOý%äÛ¯Mw+ôΈÍö[GåYaž ^öÔ(×4­I ê·mȇì¢íèf r~% –¦¡á5¶©Y¿ ‡ÑÀç–Ç& "Ö &¬¢aóз Öñ/låç #Ÿ›èZð+òï~!öÑìŠ3A ‚µáøAÎ BYýhzÎõ?Ç«VËÃßl†²ùAQ0 Ê£ öƪ7·…‰+l‡"Ìò€·£oöjÜö,äSuÈeHglq¥F[|W9sl߈ð°ö¶A|ðjÑÔC­Cv¢ £4÷þäO ¡~M˜d`½Sœ·»¥z¶Ð $}äÜ(x!Ç­ø5êYPF ÏŽ­œF ²Ø¶i´½Ø´ŠP‹ú¸É…Õ Ž-DG_èÊ÷Ññô,¸üßž³ôŽÉº6;îŽ$RG›¤Öu¼7’ͯKK°››ø 9X6ð7°aM_§]?çz1éMȽßè '²Ðì¥jó3΄¾»°~¦f»¾‹ñü–=ÕöÕI–¸ŸmÛãÝ=HU#áv,¿Fw”Ê7t÷êÁwŽôòÛv*½ô“ ­ÞÝ0ȧÚi·Ƙæ ß0hÑ÷¡:jABF£Ø²)ìå“Ô¥gÌÍÙB¿wSU7Á)êÆQ|S%&÷2…Y–»2ÖåØt+–;×I÷Sš^ã5p’‹AS™¢hè5™†/k~h˜–-!HÁå9b®³‡?Gûèçt«Î“2{Ïw3º4ªOµ,i¿N.ôÀå@ݹ®!B±î™žö8&cq! "Óƒp´–?Ci`}×:1®ws™ï£ÇaÜÕº«Zï­j§:«5{‡LÙ$×NäLjo»ž©;w£’ ‚AmLlB·Êá0€²R˜$=æ–ЂH@¨Û¡°aWoÿ r™'V+§‡û¢¾Úç‚ ™Ïí,œƒˆF'©çø£jB~ä[-®ËåÝ §ÍU9Zÿü0½vÐËÛ>{²êïOòœ+IÓ·Z®^¬ NÖÇÂd|êì°;1•ß´§1Š÷ts¾ï£BuoöþþÐvÂ\;6P^ã¹®=8ü€yøoMMdyúÛa~¹ÞÚ‹N¶H¤kJw}Û 95§¦_ÜŠ·Fz¤iΰÖ¦gxIÍmqHû]/ýð­H‘"EŠ)R¤H‘"EŠ)R¤H‘"EŠ)R¤H‘"EŠ)R¤H‘"EŠ)R¤H‘"EŠ)R¤H‘"EŠ)R¤H‘"EŠ)úB»Òÿ_x‡ÞƒžA_ú{ôLñ8Þ߇?HÆÈQòùkòí¸ö¼öc 7ižÎ=O¿Åjì»Ì^dßây¾‰âïâÏñ/ó Sì÷‰¿Òuý¸~Ièú¯$cƒqñ¢ñ3sÁøÞ#€áÝwfc>×%ÀIhÿ\·¾ øàè{®ÂU¾ÿà^x÷!€üíe¸~ ®ò;÷Ão°àˆ ô…åÏ¿|«·åßPÚÐòÂΟu¯+++ßE+è{p;ýŒþç‹ endstream endobj 2 0 obj <>endobj xref 0 206 0000000000 65535 f 0000148389 00000 n 0000295395 00000 n 0000148071 00000 n 0000142186 00000 n 0000000015 00000 n 0000000649 00000 n 0000148437 00000 n 0000148478 00000 n 0000202404 00000 n 0000201640 00000 n 0000288964 00000 n 0000148556 00000 n 0000148586 00000 n 0000142353 00000 n 0000000668 00000 n 0000003032 00000 n 0000204390 00000 n 0000260189 00000 n 0000148616 00000 n 0000148646 00000 n 0000142522 00000 n 0000003053 00000 n 0000003552 00000 n 0000148687 00000 n 0000148717 00000 n 0000142691 00000 n 0000003572 00000 n 0000005324 00000 n 0000148747 00000 n 0000148777 00000 n 0000142860 00000 n 0000005345 00000 n 0000007059 00000 n 0000148818 00000 n 0000148848 00000 n 0000143029 00000 n 0000007080 00000 n 0000018602 00000 n 0000148889 00000 n 0000148919 00000 n 0000143198 00000 n 0000018624 00000 n 0000028990 00000 n 0000148960 00000 n 0000148990 00000 n 0000143367 00000 n 0000029012 00000 n 0000030985 00000 n 0000149031 00000 n 0000149061 00000 n 0000143536 00000 n 0000031006 00000 n 0000037795 00000 n 0000149102 00000 n 0000149132 00000 n 0000143705 00000 n 0000037816 00000 n 0000039319 00000 n 0000149173 00000 n 0000149203 00000 n 0000143874 00000 n 0000039340 00000 n 0000040957 00000 n 0000149244 00000 n 0000149274 00000 n 0000144043 00000 n 0000040978 00000 n 0000043411 00000 n 0000202582 00000 n 0000205361 00000 n 0000203968 00000 n 0000245548 00000 n 0000204994 00000 n 0000278555 00000 n 0000149315 00000 n 0000149345 00000 n 0000144212 00000 n 0000043432 00000 n 0000043898 00000 n 0000149408 00000 n 0000149438 00000 n 0000144381 00000 n 0000043918 00000 n 0000045487 00000 n 0000149468 00000 n 0000149498 00000 n 0000144550 00000 n 0000045508 00000 n 0000047170 00000 n 0000149539 00000 n 0000149569 00000 n 0000144719 00000 n 0000047191 00000 n 0000065287 00000 n 0000203518 00000 n 0000233240 00000 n 0000149610 00000 n 0000149640 00000 n 0000144896 00000 n 0000065309 00000 n 0000086797 00000 n 0000149692 00000 n 0000149723 00000 n 0000145076 00000 n 0000086820 00000 n 0000091114 00000 n 0000149776 00000 n 0000149807 00000 n 0000145249 00000 n 0000091136 00000 n 0000098144 00000 n 0000149849 00000 n 0000149880 00000 n 0000145430 00000 n 0000098166 00000 n 0000110318 00000 n 0000149933 00000 n 0000149964 00000 n 0000145611 00000 n 0000110341 00000 n 0000112396 00000 n 0000150017 00000 n 0000150048 00000 n 0000145784 00000 n 0000112418 00000 n 0000112992 00000 n 0000150112 00000 n 0000150143 00000 n 0000145957 00000 n 0000113013 00000 n 0000114671 00000 n 0000150174 00000 n 0000150205 00000 n 0000146130 00000 n 0000114693 00000 n 0000116153 00000 n 0000150247 00000 n 0000150278 00000 n 0000146303 00000 n 0000116175 00000 n 0000118259 00000 n 0000150320 00000 n 0000150351 00000 n 0000146476 00000 n 0000118281 00000 n 0000120494 00000 n 0000150415 00000 n 0000150446 00000 n 0000146649 00000 n 0000120516 00000 n 0000133426 00000 n 0000150488 00000 n 0000150519 00000 n 0000146830 00000 n 0000133449 00000 n 0000135938 00000 n 0000150561 00000 n 0000150592 00000 n 0000147003 00000 n 0000135960 00000 n 0000136492 00000 n 0000150656 00000 n 0000150687 00000 n 0000147176 00000 n 0000136513 00000 n 0000138239 00000 n 0000150718 00000 n 0000150749 00000 n 0000147349 00000 n 0000138261 00000 n 0000139836 00000 n 0000150791 00000 n 0000150822 00000 n 0000147522 00000 n 0000139858 00000 n 0000141441 00000 n 0000150930 00000 n 0000150864 00000 n 0000150895 00000 n 0000169992 00000 n 0000147720 00000 n 0000141463 00000 n 0000141937 00000 n 0000170034 00000 n 0000170065 00000 n 0000147893 00000 n 0000141958 00000 n 0000142165 00000 n 0000188741 00000 n 0000170175 00000 n 0000170096 00000 n 0000170127 00000 n 0000205579 00000 n 0000233459 00000 n 0000245772 00000 n 0000260404 00000 n 0000278781 00000 n 0000289198 00000 n 0000202120 00000 n 0000203071 00000 n 0000203787 00000 n 0000204230 00000 n 0000204734 00000 n 0000205226 00000 n 0000288862 00000 n trailer << /Size 206 /Root 1 0 R /Info 2 0 R /ID [<652AD7A773DD6BC62980FF224BED427C><652AD7A773DD6BC62980FF224BED427C>] >> startxref 295618 %%EOF tokyocabinet-1.4.48/doc/logo.png0000644000175000017500000000435310705303710015521 0ustar mikiomikio‰PNG  IHDR,no´3•PLTE “m6š{J ‚_«”‡¼ª½ÖÍäêæüþû÷ªbË pHYs íÀ,tIME× 74ÒD´fIDATxÚíšÍW£:‡ùt¶ôªíÖ¹ãL·;Ú-Ž­lQ‹lëha«-Ðÿæ“$ Ië™Þsšã©”xHÞüÞ0Œc;¶c;¶cû6Ó?L¬btˆXö&Ó9Ì™ŠM2æp÷X›WþlÄöQïƒÝž.–ö«¸88,žôíà°NÐY«ð ‹œ?÷t"€G6 ÄNXr»¯ºCÝ({;a é4<Ö„bé8¡*Öéìy9ÿYÁ²Ç õà†<[£zw±¥ÕæÞ®XßSt¢' ÞxnnJÙŽé–Ø½ KüD,G´‚e¥hü¶@ûS‚ë´™OÌ­r',3í`õáÝþ î¶ààIº7c);!«ÏN´â°à¿?ø×ŒŽi&é.¶”Ål,î ¯ÄrÉíºdÐÆ‡¤{ –ªⱩó:aXbÁÉ[ã ›Ôÿ\ë.§¢QtB<Ö ±^‡N ÂBßFôþ}²0G’îB³*¾MM%x¬ ݦkB /y:ˆ²îÕàTloºX1 dMdÇ?l‰M¥ØÆjÝkQ Ø4±ÊÙBvq/|Ã:¸xÜ,2|µîX*NˆÃ²ØM‡ÿRfÏ^Š–Õz÷zpªm^–M ´"ª ¶¸ |ÁPÖ½ KÁ qXÛŠX¼ñ>ŒdÝ%Á©®Õ˱"5œ š¬O^½{Ö›¡‹µf×YpX!ë¼€{3i÷Ö˜¼!ÎØ kÍìeõaÕ5¬®ÃGCKb[[qÐÊ3›0tØV«¸5vÀª¯ÄÜæ&0PMâö•ˆX¾¶«–ëVˆ>WìZ}â°;t«‚êG¢lh1dÃ7cú­Ö½ k¥%õ‰žÁBª‘™¼{cpÊîK+àC‚¼Ä2Ór¡9|^ëÞ†¥˜ØuÇ[ÜõilÊ»·§ª6/‰N§•è_ÿ‚ƒ‘¼{+Ö˾cy|ý¥ÊEC÷–˜Y5ÉhÊ|þðék\ ú o¾Õîm1s}Mle¥Ò<QÁ–⺡{-f.®uK̪]iVwûåÕ Ý«Ái>âú…R…3Š"εœý üˆüBƒÏ~dÝ+Xož`~ë]Šü§³$‰ZÜj †íÝ]DŪIªÆ¥Ø‚-Ïï>ÕBçÏ|Öo¹ÔÍz¶~•%¯ý6·TÛ[«´ªwh{ë­›9Ÿi P%QÙ;–ÆŠr>߸L çf¦ºÞZk¡¬(º%A,Õjv¿µd¸/¬¥ª(º:ÞÚžFÓ+)B¯Çß[¯×ŸßÆ_µ”NÕ[ãÀcy!5‰µxnÝu+×usá)Nbï€5TõÖNKáIË™Nï:k¤8¼þÎXƒ&ï¢ê­ùÛX–ª·pBI%EIJ’$ÑÆb¡`¾Å$"·œ³¼à¢&ÊÍX¥·~ó¶5x„# ¢öˆå¨P!¯°.-m™¿¢h~˰΢è7Ùñx|žº^Ö,šÓÉÛ¤G –‘ªÔ(¡š‡ÆŽÙñË)¿“V’¡‡ˆ$/ÄÀJ­Àk'¯ÍFü¤ ¾^y\Î FH°âò‡uS)%á,Ñn“q0/Åo…(›Žú9˜;N0r’á°o +O™0ºìÙj+–£P9µjj5áÅ8¤–añ^7fñ]+–Ê{ovmùÁqXFäÆJI‘­ŽµÆ;‹é=P+ŒEÔ`@ q–S 4,ìÉ~„u‹´#°À|\ãq9Á#àáì'µ©aÑÂ6¡wVÄ49¬‡Ò,'ÄÁ¿í+¬î ŬwVjï#XË+WnL|\ŠîoOXnËÄ($ X^IÏ­DBå‡âñXŽqœ)ŠoËOÑÂ'ü—X9µ¸¯[¥¢ÃÈþÜJ—%x¬{<×'X ‹ýÔ±-Ð.Ÿñ2{ç|"25–-F9,úVÉ Ç“ò‚.ö²5Ö»¸ëž^…ÇŠ¡Btaå– E¸Ð ‡ó€cú@›³­uKŒÊÎ7{Â:ÙøàÏœÂAá…1)zñv ÂäDùˆòˆhótg¬Aæ"´ñrÍ¥d€LÙ'¢z}õÎc±…m=q»°†ø‘`¥+0§#€5(ÌéÔ·î.;ÞGX&‚z¹DNca%š+±’;rX_à0¾ÁyK?ÖÊøRØ0VÊ‚Ž¬€=2EUáÃåÂT^¦[ ËhÄr77Ðä‡t´|4„ð$—YÐQs.ß{BJþAÏk19õ êø¤XTN¿Ç?¥1¤¶…&!·s3¾ý„çY×RDªgá aˆGËeXë2X”bQóKk*à§-C²!–µÉìÜ¡“ug¯Oÿ^Ƹ€8@ãb2•‡Â|^ 8,òšÆyéª î† ‚5º±ú›ÄšgAè®·­<Ñ8¥˜?3Ý_ŸIÎ)ÅBVp÷+ÅUQy÷“èÖíˆ2$/äàÇÞ#;ï¥Ó,HÒÅÖõÃPH²?š£S‹ŸÑlOϪGˆ±ÏœÁ?•&n[¨Ë¹'€8 jŠå9,ㆯ`¸ ¨”W²Çç`ª¼œ¡@<Ýw¹¡XxqÐ:@9´çT–ùpXôÁÝ[¹‚VúޱLŒã…™B-¦#€:]nˆ$†$/±STꂞÈ7î“äõ¼¼¦…ö7I‚¾ö“d ÿŸáªWÊ3Á‚“HF é;°0?ØÐÔ–ÝéÍ_ÏÉrN‹ös2 ƒ×`GëñgQ²|" „9K’G’B\j[ülîÆcŠå|ÖãŒÎ2l|&kœ›¬eœý,‡Ó-›Ö§=6ئŽðB°À²À hSXÀ œùu4÷þ–=C&yz…Ìï¹ÍñØ8¶c;¶cShÿ(‡øÚj¯gsIEND®B`‚tokyocabinet-1.4.48/doc/logo-ja.png0000644000175000017500000000405610705301304016106 0ustar mikiomikio‰PNG  IHDR,no´3•PLTE “m5™zI ‚j°™¿®¹ÔËæêçúüù•K)ÑIDATxÚí›ËwÚ:‡edÓ­Sr“-÷¤…­Ó$õ–ˆ·nh›´müпGòÛ–eÌ«ôžhA8ÆÈŸGóøiLúc¿ã iW'G¥Ùó N JŸ3ÆVOßN ËX==¼œ¤oϧ‰õÞîüÕjemrÞ|>ï+>Æ “èëvX”±Ñ†ç©®¬¦òëØ¿ÏŽe°ßj,ÆBfý,öSùýñ74|>:V¬Ñd ãmX›Â€óÆ…V«1‹Û®ÚÜÊá%NÜp^qE>ñC¿—íMM^p¬¡8¦p‡…èaõ©­÷oƒãËŽújqt,'>ªX(ì.ö€…çêáJ±Âz÷ÒŸ'ª|;) ˜ê©pàÇV¥˜$w¥(B†»ÿtº)–´i×üåû²l,šy¤é ÆZÅ=aåœSR„°‡ôÕÓt}ìEÔòAS-Bš¿‰æºËL“¯‚ÞOjG¹€‚¹ê^Al3uͬOf5uËFX•SùQ±ÿJ°ûÇÆ*åÞ°r"‘jÁƪ!‡Ï{¥.ÖÀª”Ž’Æq :/Xƒ°>VɽlÀ"3b6a²H”`²rXÅêV°ŠEh8ânï4b½Iò–`Éä·¾ VqÁ.¡’oôØX—’…Ρ®ÇgkN{Æ’yáïB^³6éKDÅdðeñ“¬ÏâX÷ñÛޱŸí k‹±!–S¦ºG'ˆZè$°Hq–­¨€Eÿ ¬çÓÀÒj6u­±¼\'f¿½ÝV°-Ö~Ó).½`,™‚X4c¹ÆÚLoUú柳œ–§ËAîss;,_Ò™nÅUà«i¨¶dûÍ[c½ÓÄ˱zõfTjèÎJ•Ñ? ÖÆ}ù‹eë†ÇÒG9¬ÎOEStGçj‡e‹­{Œå¨B¿³[µ¾Ëö…|ðòÈ:6ɵ^S¬Ú t×j-<‚»&Ì4Uú ª%Kopf{×jÍÍÀïÍå[ ÊêŸo‰³F1nðšîÎR¡ï"ó,?õ~r*=HÑQÛïZ­ã:ö"Š®ë#ôOr…Ëhxâ”s®õVT‰L$j¢]Ó!F˜&.ŽD¢^FgGçÒœø¾®ÙE ö¨ÄXÂ\¥Š¢¤ öô#aK¹"ªH $YÞQ> ÓwJ¨¢w9I„%‹…Å+˜ä|7ÁÂÊýéÕZ»šÒ¬3ë­X+-ÇÙc¿[š/ºiMŒÎ gf“sµ«Öi‡£¼ $åª1(6S,¨®:ÜV âb[,ÁÂIl¿S㨢 â3åÕeëjÝ)vÅRѬӢµŒbK/'l¢3ß÷[­c¯œU´|&Ó‹½³¼Þ:§õͤà«Ù+‹¹Ü£ó/¸Ø¥*È@\ÿËV=WT?[ŽMù²/ šbiliÕªS¸ƒš‘8׬mÒº9S<¸Ó®ÒË“XùUWêˆzD» Ø.ôÑ>ݶGyØÎ^Ÿä»À:=*¤}tŠÃDÿ³q#[ýÏémâøô7–½Ãù3• mú–ª?7.?2,Û;QaáñÛ`áèg9çüõ|b¥¯­ÆŠŠ?–‹ÍC­~gUÁ"QzbS$¯í“°|‡™."ñ^µzÙTÆÒcíåxÑc$”ZÛ]¢èû‘@ü3u«X¿EMІÃL¤YH ý£ŠuÉ–”· ¨Ç%ó t˜ÍèVm7䤊—f²‰&Ëé8¿D°>ñÌ-RƲCÓf&žr“Q_¿$A‡nvŠE<Éò†ù%záeÎ2Äýó³í°ø3,˜«Ëú_I N²Dßñ·Â Ê=·Ëæl âÄb—¶¯`yƒcV$맯€ícðýþv2ÞË«vFõh'ABØÀoü»´ \`®.,âÖ Ã4 pþê®I«ÎÍ0ÆêL÷&¡5iõùN‡DûÔ Þ>ù.÷-¾­}̹üµF~'¬%ŠÇ V‹Æ0Ì4®%¦6eXwg:°]XÜ%\ž· ^% BøŸJ£žáwØç>±†-ž³\†™Æuãp«bq«¼g˜ä­;ø¤Ÿ…ŠG)…0äXàŒ†‡ÈÕ5±ºëm°4nd,¬„¥Áfß'ùmUähQ‚øJÇ7â¶ç-Že³`ÑÁ¬õ¼U–'ïq¸±´[Þ=yƒaj~±,Ë÷’ c‡}žrÓ,ï;_ ¿Ç.ÖdµáÞºŒ%l 2?mFÞøŽ0Hä‹&Q&'~ä NT€uf#-"qEÞS\|D®øîV°[^#À‚«ûi³Ë-ÔDÍLÔæ&áXß@‚€/Pœ ƤE2²{èòš‡*X·\ìq,Ô%à@Zª!§‹¸áXg&rÌ}Í&}À²º-ÊPNIe„ÕArG¾&R¢‚åÁt±µ ‘:<»%¸<âXÆvXzÖ*ò’Ÿ™¬»´Á\Æ"¡9H|‹x± nn®¬vÖeŸçj0¸e¬i–Á* ‚-Y‰xü³hÚø1Ö’¶H§8¶é÷Éòj«›Ë[—BÒV+Aˆs¿¶_$5\ùg™ ëbV«N;¹,¯‹v¼ÀÒáÆãtþ´ÿ-¦fnrÏ…x¹â±ûenEÊöÔþEòc|ŒñŒÿ17“vXŒIEND®B`‚tokyocabinet-1.4.48/doc/icon16.png0000644000175000017500000000057710673774356015712 0ustar mikiomikio‰PNG  IHDR(-ScPLTEhÇ)+(!=ÍGIF [Æ(LÓ6e›dfcSk†MgØlnkiqx=Ó\uÞy{x‡‰†]”Û‚á”–“v£Þœ¦é°²¯ž¶èªÂéÀÈÐÏÑÎÀÕñÊÑòêìèæìøüþû ß+‡tRNS@æØfbKGDˆH pHYs  d_‘tIME×  ~sªM•IDATÓMÎÙƒ @QSÁR)uŒˆIÿÿ+ËbkïÌ–IÓ]ChJŠkêX ±¤ø©ÁÜë”÷^ä;j¹mÙ‰wjÈ€áÅñ˨‡ú©ÇLˆáyBDÆìhî'@ÇFóô({h'9¥Qðû$IÌ›þƒ=­tÊ•y·°•ÖÚúDøPÚ¶D¯o¿Òè¸Û›û´K«IEND®B`‚tokyocabinet-1.4.48/doc/spex-ja.html0000644000175000017500000202160112013574114016307 0ustar mikiomikio 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. ユーティリティAPI
    5. ãƒãƒƒã‚·ãƒ¥ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹API
    6. B+木データベースAPI
    7. 固定長データベースAPI
    8. テーブルデータベースAPI
    9. 抽象データベースAPI
    10. ã¡ã‚‡ã£ã¨ã—ãŸã‚³ãƒ„
    11. ファイルフォーマット
    12. よãèžã‹ã‚Œã‚‹è³ªå•
    13. ライセンス

    ã¯ã˜ã‚ã«

    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ã¯ãã®ï¼’ã¤ã«åŠ ãˆã¦ã€Œé€£çµã€ãƒ¢ãƒ¼ãƒ‰ãŒã‚りã¾ã™ã€‚既存ã®å€¤ã®æœ«å°¾ã«æŒ‡å®šã•れãŸå€¤ã‚’連çµã—ã¦æ ¼ç´ã™ã‚‹æ“作ã§ã™ã€‚レコードã®å€¤ã‚’é…列ã¨ã—ã¦æ‰±ã†å ´åˆã€è¦ç´ ã‚’追加ã™ã‚‹ã«ã¯é€£çµãƒ¢ãƒ¼ãƒ‰ãŒå½¹ã«ç«‹ã¡ã¾ã™ã€‚

    一般的ã«ã€ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã®æ›´æ–°å‡¦ç†ã‚’ç¶šã‘ã‚‹ã¨ãƒ•ァイル内ã®åˆ©ç”¨å¯èƒ½é ˜åŸŸã®æ–­ç‰‡åŒ–(フラグメンテーション)ãŒèµ·ãã€ãƒ•ァイルã®ã‚µã‚¤ã‚ºãŒè‚¥å¤§åŒ–ã—ã¦ã—ã¾ã„ã¾ã™ã€‚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ãƒã‚¤ãƒˆã®å›ºå®šé•·ã§ã¨ã‚‰ã‚Œã€ä»¥ä¸‹ã®æƒ…å ±ãŒè¨˜éŒ²ã•れã¾ã™ã€‚

    åå‰ ã‚ªãƒ•ã‚»ãƒƒãƒˆ データ長 機能
    マジックナンム0 32 データベースファイルã§ã‚ã‚‹ã“ã¨ã®åˆ¤åˆ¥ã€‚「ToKyO CaBiNeTã€ã§å§‹ã¾ã‚‹
    データベースタイプ 32 1 ãƒãƒƒã‚·ãƒ¥è¡¨ï¼ˆ0x01)ã‹B+木(0x02)ã‹å›ºå®šé•·ï¼ˆ0x03)ã‹ãƒ†ãƒ¼ãƒ–ル(0x04)
    追加フラグ 33 1 é–‹ãã£ã±ãªã—(1<<0)ã€è‡´å‘½çš„エラー(1<<1)ã®è«–ç†å’Œ
    アラインメント力 34 1 アラインメントã«å¯¾ã™ã‚‹2ã®å†ªä¹—
    フリーブロックプール力 35 1 フリーブロックプールã®è¦ç´ æ•°ã«å¯¾ã™ã‚‹2ã®å†ªä¹—
    オプション 36 1 ラージモード(1<<0)ã€Deflate圧縮モード(1<<1)ã€BZIP2圧縮モード(1<<2)ã€TCBS圧縮モード(1<<3)ã€å¤–部圧縮モード(1<<4)ã®è«–ç†å’Œ
    ãƒã‚±ãƒƒãƒˆæ•° 40 8 ãƒã‚±ãƒƒãƒˆé…列ã®è¦ç´ æ•°
    レコード数 48 8 æ ¼ç´ã—ã¦ã„ã‚‹ãƒ¬ã‚³ãƒ¼ãƒ‰ã®æ•°
    ファイルサイズ 56 8 データベースファイルã®ã‚µã‚¤ã‚º
    先頭レコード 64 8 最åˆã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã®ã‚ªãƒ•セット
    ä¸é€æ˜Žé ˜åŸŸ 128 128 ユーザãŒè‡ªç”±ã«ä½¿ãˆã‚‹é ˜åŸŸ

    ãƒã‚±ãƒƒãƒˆéƒ¨ã¯ãƒ˜ãƒƒãƒ€éƒ¨ã®ç›´å¾Œã«ãƒã‚±ãƒƒãƒˆé…列ã®è¦ç´ æ•°ã«å¿œã˜ãŸå¤§ãã•ã§ã¨ã‚‰ã‚Œã€ãƒãƒƒã‚·ãƒ¥ãƒã‚§ãƒ¼ãƒ³ã®å…ˆé ­è¦ç´ ã®ã‚ªãƒ•セットãŒå„è¦ç´ ã«è¨˜éŒ²ã•れã¾ã™ã€‚å„è¦ç´ ã¯å›ºå®šé•·æ•°å€¤ã§ã€ãã®ã‚µã‚¤ã‚ºã¯ãƒŽãƒ¼ãƒžãƒ«ãƒ¢ãƒ¼ãƒ‰ã§ã¯4ãƒã‚¤ãƒˆã€ãƒ©ãƒ¼ã‚¸ãƒ¢ãƒ¼ãƒ‰ã§ã¯8ãƒã‚¤ãƒˆã§ã™ã€‚ã¾ãŸã€ã‚ªãƒ•セットã¯ã‚¢ãƒ©ã‚¤ãƒ³ãƒ¡ãƒ³ãƒˆã§å‰²ã£ãŸå•†ã¨ã—ã¦è¨˜éŒ²ã•れã¾ã™ã€‚

    フリーブロックプール部ã¯ãƒã‚±ãƒƒãƒˆéƒ¨ã®ç›´å¾Œã«ãƒ•リーブロックプールã®è¦ç´ æ•°ã«å¿œã˜ãŸå¤§ãã•ã§ã¨ã‚‰ã‚Œã€æœªä½¿ç”¨é ˜åŸŸã®ã‚ªãƒ•セットã¨é•·ã•ãŒå„è¦ç´ ã«è¨˜éŒ²ã•れã¾ã™ã€‚オフセットã¯ã‚¢ãƒ©ã‚¤ãƒ³ãƒ¡ãƒ³ãƒˆã§å‰²ã£ãŸå•†ã«å¤‰æ›ã—ãŸä¸Šã§ã€ç›´å‰ã®è¦ç´ ã®å€¤ã¨ã®å·®åˆ†ã¨ã—ã¦è¨˜éŒ²ã•れã¾ã™ã€‚オフセットã¨ã‚µã‚¤ã‚ºã¯å¯å¤‰é•·æ•°å€¤ã¨ã—ã¦æ‰±ã‚れã¾ã™ã€‚

    レコード部ã¯ãƒã‚±ãƒƒãƒˆéƒ¨ã®ç›´å¾Œã‹ã‚‰ãƒ•ã‚¡ã‚¤ãƒ«ã®æœ«å°¾ã¾ã§ã‚’å ã‚ã€å„レコードã®ä»¥ä¸‹ã®æƒ…報をæŒã¤è¦ç´ ãŒè¨˜éŒ²ã•れã¾ã™ã€‚å„レコードã®é ˜åŸŸã¯å¸¸ã«ã‚¢ãƒ©ã‚¤ãƒ³ãƒ¡ãƒ³ãƒˆã•れãŸä½ç½®ã‹ã‚‰å§‹ã¾ã‚Šã¾ã™ã€‚

    åå‰ ã‚ªãƒ•ã‚»ãƒƒãƒˆ データ長 機能
    マジックナンム0 1 データã®è­˜åˆ¥ã¨æ•´åˆæ€§ç¢ºèªã«ç”¨ã„る。0xC8固定
    ãƒãƒƒã‚·ãƒ¥å€¤ 1 1 ãƒã‚§ãƒ¼ãƒ³ã®é€²è·¯æ±ºå®šã«ç”¨ã„ã‚‹ãƒãƒƒã‚·ãƒ¥å€¤
    å·¦ãƒã‚§ãƒ¼ãƒ³ 2 4 å·¦ãƒã‚§ãƒ¼ãƒ³æŽ¥ç¶šå…ˆã®ã‚ªãƒ•セットã®ã‚¢ãƒ©ã‚¤ãƒ³ãƒ¡ãƒ³ãƒˆå•†
    å³ãƒã‚§ãƒ¼ãƒ³ 6 4 å³ãƒã‚§ãƒ¼ãƒ³æŽ¥ç¶šå…ˆã®ã‚ªãƒ•セットã®ã‚¢ãƒ©ã‚¤ãƒ³ãƒ¡ãƒ³ãƒˆå•†
    パディングサイズ 10 2 パディングã®ã‚µã‚¤ã‚º
    キーサイズ 12 å¯å¤‰ キーã®ã‚µã‚¤ã‚º
    値サイズ å¯å¤‰ å¯å¤‰ 値ã®ã‚µã‚¤ã‚º
    キー å¯å¤‰ å¯å¤‰ キーã®ãƒ‡ãƒ¼ã‚¿
    値 å¯å¤‰ å¯å¤‰ 値ã®ãƒ‡ãƒ¼ã‚¿
    パディング å¯å¤‰ å¯å¤‰ æ„味をæŒãŸãªã„データ

    ãŸã ã—ã€ãƒ•リーブロックã¨ãªã£ãŸé ˜åŸŸã«ã¯ã€å„レコードã®ä»¥ä¸‹ã®æƒ…報をæŒã¤è¦ç´ ãŒè¨˜éŒ²ã•れã¾ã™ã€‚

    åå‰ ã‚ªãƒ•ã‚»ãƒƒãƒˆ データ長 機能
    マジックナンム0 1 データã®è­˜åˆ¥ã¨æ•´åˆæ€§ç¢ºèªã«ç”¨ã„る。0xB0固定
    ブロックサイズ 1 4 ブロックã®ã‚µã‚¤ã‚º

    トランザクションログã¯ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹åã« ".wal" を後置ã—ãŸåå‰ã®ãƒ•ァイルã¨ã—ã¦è¨˜éŒ²ã•れã¾ã™ã€‚ファイルã®å…ˆé ­8ãƒã‚¤ãƒˆã«ãƒˆãƒ©ãƒ³ã‚¶ã‚¯ã‚·ãƒ§ãƒ³é–‹å§‹æ™‚ã®ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãƒ•ァイルã®ã‚µã‚¤ã‚ºã‚’記録ã—ã€ãã®å¾Œã«æ›´æ–°æ“作ã«ã‚ˆã‚‹å·®åˆ†æƒ…報をæŒã¤ä»¥ä¸‹ã®è¦ç´ ã‚’連çµã—ã¾ã™ã€‚

    åå‰ ã‚ªãƒ•ã‚»ãƒƒãƒˆ データ長 機能
    オフセット 0 8 æ›´æ–°ã•れãŸé ˜åŸŸã®å…ˆé ­ã®ã‚ªãƒ•セット
    サイズ 8 4 æ›´æ–°ã•れãŸé ˜åŸŸã®ã‚µã‚¤ã‚º
    データ 12 å¯å¤‰ æ›´æ–°ã•ã‚Œã‚‹é ˜åŸŸã®æ›´æ–°å‰ã®ãƒ‡ãƒ¼ã‚¿

    B+木データベースã®ãƒ•ァイルフォーマット

    B+æœ¨ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãŒæ‰±ã†å…¨ã¦ã®ãƒ‡ãƒ¼ã‚¿ã¯ãƒãƒƒã‚·ãƒ¥ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã«è¨˜éŒ²ã•れã¾ã™ã€‚記録ã•れるデータã¯ã€ãƒ¡ã‚¿ãƒ‡ãƒ¼ã‚¿ã¨è«–ç†ãƒšãƒ¼ã‚¸ã«åˆ†é¡žã•れã¾ã™ã€‚è«–ç†ãƒšãƒ¼ã‚¸ã¯ãƒªãƒ¼ãƒ•ノードã¨éžãƒªãƒ¼ãƒ•ノードã«åˆ†é¡žã•れã¾ã™ã€‚固定長数値ã¨å¯å¤‰é•·æ•°å€¤ã®å½¢å¼ã¯ãƒãƒƒã‚·ãƒ¥ãƒ¼ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã¨åŒã˜ã§ã™ã€‚

    メタデータã¯ãƒãƒƒã‚·ãƒ¥ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã®ãƒ˜ãƒƒãƒ€ã«ãŠã‘ã‚‹ä¸é€æ˜Žé ˜åŸŸã«ã¨ã‚‰ã‚Œã€ä»¥ä¸‹ã®æƒ…å ±ãŒè¨˜éŒ²ã•れã¾ã™ã€‚

    åå‰ ã‚ªãƒ•ã‚»ãƒƒãƒˆ データ長 機能
    比較関数 0 1 比較関数ãŒtccmplexical(デフォルト)ãªã‚‰0x00ã€tccmpdecimalãªã‚‰0x01ã€tccmpint32ãªã‚‰0x02ã€tccmpint64ãªã‚‰0x03ã€ãれ以外ãªã‚‰0xff
    予約領域 1 7 ç¾çжã§ã¯åˆ©ç”¨ã—ã¦ã„ãªã„。
    リーフ内レコード数 8 4 個々ã®ãƒªãƒ¼ãƒ•ノードã«å…¥ã‚Œã‚‹ãƒ¬ã‚³ãƒ¼ãƒ‰ã®æœ€å¤§æ•°
    éžãƒªãƒ¼ãƒ•内インデックス数 12 4 個々ã®éžãƒªãƒ¼ãƒ•ノードã«å…¥ã‚Œã‚‹ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã®æœ€å¤§æ•°
    ルートノードID 16 8 B+木ã®ãƒ«ãƒ¼ãƒˆãƒŽãƒ¼ãƒ‰ã®ãƒšãƒ¼ã‚¸ID
    先頭リーフID 24 8 先頭ã®ãƒªãƒ¼ãƒ•ノードã®ID
    末尾リーフID 32 8 末尾ã®ãƒªãƒ¼ãƒ•ノードã®ID
    リーフ数 40 8 ãƒªãƒ¼ãƒ•ãƒŽãƒ¼ãƒ‰ã®æ•°
    éžãƒªãƒ¼ãƒ•æ•° 48 8 éžãƒªãƒ¼ãƒ•ãƒŽãƒ¼ãƒ‰ã®æ•°
    レコード数 56 8 æ ¼ç´ã—ã¦ã„ã‚‹ãƒ¬ã‚³ãƒ¼ãƒ‰ã®æ•°

    リーフノードã¯ãƒ¬ã‚³ãƒ¼ãƒ‰ã®ãƒªã‚¹ãƒˆã‚’ä¿æŒã—ã€éžãƒªãƒ¼ãƒ•ノードã¯ãƒšãƒ¼ã‚¸ã‚’å‚ç…§ã™ã‚‹ç–Žã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã‚’ä¿æŒã—ã¾ã™ã€‚レコードã¯ãƒ¦ãƒ¼ã‚¶ãƒ‡ãƒ¼ã‚¿ã®è«–ç†çš„ãªå˜ä½ã§ã™ã€‚キーãŒé‡è¤‡ã™ã‚‹è«–ç†ãƒ¬ã‚³ãƒ¼ãƒ‰ã¯ç‰©ç†çš„ã«ã¯å˜ä¸€ã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã«ã¾ã¨ã‚られã¾ã™ã€‚物ç†ãƒ¬ã‚³ãƒ¼ãƒ‰ã¯ä»¥ä¸‹ã®å½¢å¼ã§ç›´åˆ—化ã•れã¾ã™ã€‚

    åå‰ ã‚ªãƒ•ã‚»ãƒƒãƒˆ データ長 機能
    キーサイズ 0 å¯å¤‰ キーã®ã‚µã‚¤ã‚º
    値サイズ å¯å¤‰ å¯å¤‰ 最åˆã®å€¤ã®ã‚µã‚¤ã‚º
    é‡è¤‡æ•° å¯å¤‰ å¯å¤‰ キーãŒé‡è¤‡ã—ãŸå€¤ã®æ•°
    キー å¯å¤‰ å¯å¤‰ キーã®ãƒ‡ãƒ¼ã‚¿
    値 å¯å¤‰ å¯å¤‰ 最åˆã®å€¤ã®ãƒ‡ãƒ¼ã‚¿
    é‡è¤‡ãƒ¬ã‚³ãƒ¼ãƒ‰ å¯å¤‰ å¯å¤‰ 値ã®ã‚µã‚¤ã‚ºã¨å€¤ã®ãƒ‡ãƒ¼ã‚¿ã®ãƒªã‚¹ãƒˆ

    リーフノードã¯ãƒ¬ã‚³ãƒ¼ãƒ‰ã®é›†åˆã‚’æ ¼ç´ã™ã‚‹ãŸã‚ã®ç‰©ç†çš„ãªå˜ä½ã§ã™ã€‚リーフノードã¯1ã‹ã‚‰ã‚¤ãƒ³ã‚¯ãƒªãƒ¡ãƒ³ãƒˆã—ã¦æŒ¯ã‚‰ã‚Œã‚‹ID番å·ã§è­˜åˆ¥ã•れã¾ã™ã€‚リーフノードã¯ID番å·ã‚’16é€²æ•°ã®æ–‡å­—列ã¨ã—ã¦è¡¨ç¾ã—ãŸãƒ‡ãƒ¼ã‚¿ã‚’キーã¨ã—ã€ä»¥ä¸‹ã®å€¤ã‚’æŒã¤ãƒ¬ã‚³ãƒ¼ãƒ‰ã¨ã—ã¦ãƒãƒƒã‚·ãƒ¥ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã«æ ¼ç´ã•れã¾ã™ã€‚レコードã¯å¸¸ã«ã‚­ãƒ¼ã®æ˜‡é †ã«æ•´åˆ—ã—ãŸçŠ¶æ…‹ã§ä¿æŒã•れã¾ã™ã€‚

    åå‰ ã‚ªãƒ•ã‚»ãƒƒãƒˆ データ長 機能
    å‰ãƒªãƒ¼ãƒ• 0 å¯å¤‰ ç›´å‰ã®ãƒªãƒ¼ãƒ•ノードã®ID
    後リーフ å¯å¤‰ å¯å¤‰ 直後ã®ãƒªãƒ¼ãƒ•ノードã®ID
    レコードリスト å¯å¤‰ å¯å¤‰ ページã®ãƒ¬ã‚³ãƒ¼ãƒ‰ã‚’直列化ã—ã¦é€£çµã—ãŸãƒ‡ãƒ¼ã‚¿

    インデックスã¯å­ãƒšãƒ¼ã‚¸ã‚’探索ã™ã‚‹ãŸã‚ã®ãƒã‚¤ãƒ³ã‚¿ã®è«–ç†çš„ãªå˜ä½ã§ã™ã€‚インデックスã¯ä»¥ä¸‹ã®å½¢å¼ã§ç›´åˆ—化ã•れã¾ã™ã€‚

    åå‰ ã‚ªãƒ•ã‚»ãƒƒãƒˆ データ長 機能
    ページID 0 å¯å¤‰ å‚ç…§å…ˆã®ãƒšãƒ¼ã‚¸ã®ID
    キーサイズ å¯å¤‰ å¯å¤‰ キーã®ã‚µã‚¤ã‚º
    キー å¯å¤‰ å¯å¤‰ キーã®ãƒ‡ãƒ¼ã‚¿

    éžãƒªãƒ¼ãƒ•ノードã¯ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã®é›†åˆã‚’æ ¼ç´ã™ã‚‹ãŸã‚ã®ç‰©ç†çš„ãªå˜ä½ã§ã™ã€‚éžãƒªãƒ¼ãƒ•ノードã¯281474976710657ã‹ã‚‰ã‚¤ãƒ³ã‚¯ãƒªãƒ¡ãƒ³ãƒˆã—ã¦æŒ¯ã‚‰ã‚Œã‚‹ID番å·ã§è­˜åˆ¥ã•れã¾ã™ã€‚éžãƒªãƒ¼ãƒ•ノードã¯ID番å·ã‹ã‚‰281474976710657を引ã„ãŸå€¤ã‚’16é€²æ•°ã®æ–‡å­—列ã¨ã«ã—ãŸä¸Šã§ã€Œ#ã€ã‚’接頭ã•ã›ãŸæ–‡å­—列をキーã¨ã—ã€ä»¥ä¸‹ã®å€¤ã‚’æŒã¤ãƒ¬ã‚³ãƒ¼ãƒ‰ã¨ã—ã¦ãƒãƒƒã‚·ãƒ¥ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã«æ ¼ç´ã•れã¾ã™ã€‚インデックスã¯å¸¸ã«æ˜‡é †ã«æ•´åˆ—ã—ãŸçŠ¶æ…‹ã§ä¿æŒã•れã¾ã™ã€‚

    åå‰ ã‚ªãƒ•ã‚»ãƒƒãƒˆ データ長 機能
    継承ID 0 å¯å¤‰ 最åˆã®å­ãƒŽãƒ¼ãƒ‰ã®ID
    インデックスリスト å¯å¤‰ å¯å¤‰ ページ内ã®ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã‚’直列化ã—ã¦é€£çµã—ãŸãƒ‡ãƒ¼ã‚¿

    固定長データベースã®ãƒ•ァイルフォーマット

    固定長データベースãŒç®¡ç†ã™ã‚‹ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ãƒ•ァイルã®å†…容ã¯ã€ãƒ˜ãƒƒãƒ€éƒ¨ã¨ãƒ¬ã‚³ãƒ¼ãƒ‰éƒ¨ã®2ã¤ã«å¤§åˆ¥ã•れã¾ã™ã€‚ファイルã«è¨˜éŒ²ã•れる数値ã¯ãƒªãƒˆãƒ«ã‚¨ãƒ³ãƒ‡ã‚£ã‚¢ãƒ³ã®å›ºå®šé•·æ•°å€¤ã¨ã—ã¦è¨˜éŒ²ã•れã¾ã™ã€‚

    ヘッダ部ã¯ãƒ•ァイルã®å…ˆé ­ã‹ã‚‰256ãƒã‚¤ãƒˆã®å›ºå®šé•·ã§ã¨ã‚‰ã‚Œã€ä»¥ä¸‹ã®æƒ…å ±ãŒè¨˜éŒ²ã•れã¾ã™ã€‚

    åå‰ ã‚ªãƒ•ã‚»ãƒƒãƒˆ データ長 機能
    マジックナンム0 32 データベースファイルã§ã‚ã‚‹ã“ã¨ã®åˆ¤åˆ¥ã€‚「ToKyO CaBiNeTã€ã§å§‹ã¾ã‚‹
    データベースタイプ 32 1 0x03固定
    追加フラグ 33 1 é–‹ãã£ã±ãªã—(1<<0)ã€è‡´å‘½çš„エラー(1<<1)ã®è«–ç†å’Œ
    レコード数 48 8 æ ¼ç´ã—ã¦ã„ã‚‹ãƒ¬ã‚³ãƒ¼ãƒ‰ã®æ•°
    ファイルサイズ 56 8 データベースファイルã®ã‚µã‚¤ã‚º
    レコード幅 64 8 å„レコードã®å€¤ã®å¹…
    制é™ã‚µã‚¤ã‚º 72 8 データベースファイルã®åˆ¶é™ã‚µã‚¤ã‚º
    最å°ID 80 8 ç¾åœ¨ã®ãƒ¬ã‚³ãƒ¼ãƒ‰IDã®æœ€å°å€¤
    最大ID 88 8 ç¾åœ¨ã®ãƒ¬ã‚³ãƒ¼ãƒ‰IDã®æœ€å¤§å€¤
    ä¸é€æ˜Žé ˜åŸŸ 128 128 ユーザãŒè‡ªç”±ã«ä½¿ãˆã‚‹é ˜åŸŸ

    レコード部ã¯ãƒ˜ãƒƒãƒ€éƒ¨ã®ç›´å¾Œã‹ã‚‰ãƒ•ã‚¡ã‚¤ãƒ«ã®æœ«å°¾ã¾ã§ã‚’å ã‚ã€å„レコードã®ä»¥ä¸‹ã®æƒ…報をæŒã¤è¦ç´ ãŒè¨˜éŒ²ã•れã¾ã™ã€‚値サイズã«å¿…è¦ãªé ˜åŸŸã¯ã€ãƒ¬ã‚³ãƒ¼ãƒ‰å¹…ãŒ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' å®›ã«é›»å­ãƒ¡ãƒ¼ãƒ«ã‚’é€ã£ã¦ãã ã•ã„。


    tokyocabinet-1.4.48/doc/index.html0000644000175000017500000001442312013574114016051 0ustar mikiomikio 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.


    tokyocabinet-1.4.48/doc/tokyoproducts.ppt0000644000175000017500000064300011375376046017547 0ustar mikiomikioÐÏࡱá>þÿ  þÿÿÿþÿÿÿœžŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿnð‚º‡^‹ åÌÒÄ9û–ò26zëÿ‰PNG  IHDR1ñcsRGB®Îé pHYsÃĈ.>£ºIDATx^íý ÐfE}(êÃFF¹ ruÁŒ"¢˜$P^HŒHT,ö–h©ej(븭܌/‰Ç½=ÙÙ&–q—ÿá˜#–Š å¨Ù#$DD¨@DQE@Aä¦BLÎO_óåó»½½n½z­õXSÔ½úòt¿ï»~«{uﲋÿ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ @€ КÀ®[¶mm-3 @€ @€RþS©S/ @€ Ц€¸MMy @€ @€@±àb»FÅ @€ @ Mp›šò"@€ @€bÀÅvŠ @€ @€@›à65åE€ @€Å €‹í#@€ @€6ÀmjÊ‹ @€ŠÛ5*F€ @€m €ÛÔ” @€+ .¶kTŒ @€Ú·©)/ @€(V@\lר @€´) nSS^ @€ P¬€¸Ø®Q1 @€hS@ܦ¦¼ @€ @ Xp±]£b @€ Ц€¸MMy @€ @€@±àb»FÅ @€ @ Mp›šò"@€ @€bÀÅvŠ @€ @€@›à65åE€ @€Å €‹í#@€ @€6ÀmjÊ‹ @€ŠÛ5*F€ @€m €ÛÔ” @€+ .¶kTŒ @€Ú·©)/ @€(V@\lר @€´) nSS^ @€ P¬€¸Ø®Q1 @€hS@ܦ¦¼ @€ @ Xp±]£b @€ Ц€¸MMy @€ @€@±àb»FÅ @€ @ Mp›šò"@€ @€bÀÅvŠ @€ @€@›à65åE€ @€Å €‹í#@€ @€6ÀmjÊ‹ @€ŠÛ5*F€ @€m €ÛÔ” @€+ .¶kTŒ @€Ú·©)/ @€(V@\lר @€´) nSS^ @€ P¬€¸Ø®Q1 @€hS@ܦ¦¼ @€ @ Xp±]£b @€ Ц€¸MMy @€ @€@±àb»FÅ @€ @ Mp›šò"@€ @€bÀÅvŠ @€ @€@›à65åE€ @€Å €‹í#@€ @€6ÀmjÊ‹ @€ŠÛ5*F€ @€m €ÛÔ” @€+ .¶kTŒ @€Ú·©)/ @€(V@\lר @€´) nSS^ @€ P¬€¸Ø®Q1 @€hS@ܦ¦¼ @€ @ Xp±]£b @€ Ц€¸MMy @€ @€@±àb»FÅ @€ @ Mp›šò"@€ @€bÀÅvŠ @€ @€@›à65åE€ @€Å €‹í#@€ @€6ÀmjÊ‹ @€ŠÛ5*F€ @€m €ÛÔ” @€+ .¶kTŒ @€Ú·©)/ @€(V@\lר @€´) nSS^ @€ P¬€¸Ø®Q1 @€hS@ܦ¦¼ @€ @ Xp±]£b @€ Ц€¸MMy @€ @€@±àb»FÅ @€ @ Mp›šò"@€ @€bÀÅvŠ @€ @€@›à65åE€ @€Å €‹í#@€ @€6ÀmjÊ‹ @€ŠÛ5*F€ @€m €ÛÔ” @€+ .¶kTŒ @€Ú·©)/ @€(V@\lר @€´) nSS^ @€ P¬€¸Ø®Q1 @€hS@ܦ¦¼ @€ @ Xp±]£b @€ Ц€¸MMy @€ @€@±àb»FÅ @€ @ Mp›šò"@€ @€bÀÅvŠ @€ @€@›à65åE€ @€Å €‹í#@€ @€6ÀmjÊ‹ @€ŠÛ5*F€ @€m €ÛÔ” @€+ .¶kTŒ @€Ú·©)/ @€(V@\lר @€´) nSS^ @€ P¬€¸Ø®Q1 @€hS@ܦ¦¼ @€ @ Xp±]£b @€ Ц€¸MMy @€ @€@±àb»FÅ @€ @ Mp›šò"@€ @€bÀÅvŠ @€ @€@›à65åE€ @€Å €‹í#@€ @€6ÀmjÊ‹ @€ŠÛ5*F€ @€m €ÛÔ” @€+ .¶kTŒ @€Ú·©)/ @€(V@\lר @€´) nSS^ @€ P¬€¸Ø®Q1 @€hS@ܦ¦¼ @€ @ Xp±]£b @€ Ц€¸MMy @€ @€@±àb»FÅ @€ @ Mp›šò"@€ @€bÀÅvŠ @€ @€@›à65åE€ @€Å €‹í#@€ @€6ÀmjÊ‹ @€ŠÛ5*F€ @€m €ÛÔ” @€+ .¶kTŒ @€Ú·©)/ @€(V@\lר @€´) nSS^ @€ P¬€¸Ø®Q1 @€hS@ܦ¦¼ @€ @ Xp±]£b @€ Ц€¸MMy @€ @€@±àb»FÅ @€ @ Mp›šò"@€ @€bÀÅvŠ @€ @€@›à65åE€ @€Å €‹í#@€ @€6ÀmjÊ‹ @€ŠÛ5*F€ @€m €ÛÔ” @€+ .¶kTŒ @€Ú·©)/ @€(V@\lר @€´) nSS^ @€ P¬€¸Ø®Q1 @€hS@ܦ¦¼ @€ @ Xp±]£b @€ Ц€¸MMy @€ @€@±àb»FÅ @€ @ Mp›šò"@€ @€bÀÅvŠ @€ @€@›à65åE€ @€Å €‹í#@€ @€6ÀmjÊ‹ @€ŠÛ5*F€ @€m €ÛÔ” @€+ .¶kTŒ @€Ú·©)/ @€(V@\lר @€´)°ë–m[ÛÌO^–\±u[ãÎ:3C)Š @€ @`èà¡÷`ÑõÝ=*G€Àd2|{9™Ñ¤¡¶€%ÐÃî?µ'@€ @€Dp"”d @€ 0lð°ûOí  @€ @ Q@œ% @€L {î“©$Ř˜€xb®¹ @€Å œtø±ï~îkßïàâkª‚& X‡©. @€ÀèöØ÷˜8ç´7œºùÄÑ7V äçÔV @€ù‡lÜ?mØm÷7žô’·?çUVDÏ'“‚@š€8ÍI* @€¹bx±¨7}Îóÿ0þ™«på³€x̽«m @€C8à§7ÁŠà˜ŽÙà˜bsÔ™@9àrúBM @€üHà +!â}àx+8Þ fD€@mpm: @€h_ ¦y7nØsÕ|c_èØzë–SÛ/Uަ! žF?k% @€À@–­^Yë­[NqHÒ@:S5‹×%*D€ 0eU×?/qHÒ”Gˆ¶77Ñs- @€––n½NÖIjÙ]vÓO£Ÿµ’ @` s—@/m‡C’Ò«ªYŠ€¸”žP @€!pðÞ«l½ŽŒC’ éàt+)  @€t.Pix±6Iê¼c0 ð(ºQ# @€Æ"øðÊæ:$i,C@;:wˆ+k @€URv^'O‡$U—~RàIu·Æ @€”.Po ôÒV9$©ô>V¿þÀýÙ+™ @€ÀO lܰgœoÔ\eñ¤È°ynr 0ðhºRC @€/Ðpýó²öÇ!I<ýMñÏÁ»h–À-Aʆ @€@cæëŸ—UavHÒkŸú›­L,7nŸ ô, î¹O€ @`Q öÐëžö„§ŸsÚâÝ`Ô&. žøÐ| @€‚Ú]½´a³C’^zì¯ÔZU!]@œ\ @€ÖXØcŸNm^ùsÏ‹08‚áNK‘9bÀÅvŠ @€LN »àEÊX1ð©›Oœ®Øe°Q@€ @ Ö7ÁZµaq6ÒOzIlŽå¤R:^=r €sI+‡ @€À<Ž6ÁZµX‡$Íë ÿ}„»nÙ¶u„ÍÒ¤2®Øº-CEŽ;ëÌ ¥(‚ÀDò|l'‚©™†.à¶—ìå{xûµ—¼õ²søáƒ½4Y¡r ˜Ω­, @€k ºq¡‡$õ®Ð^Š˜îåAW/Ü %@ ’€É‡J\­$ö…Ü £LŒCÀ—pþ~œmO•¿ÜÅßñÙŸ}ÕÇz¬€¢ t-`¸kaù @€HȰôúõpHRR?I4dð{OÝ  @€F$g èõÁ’4¢¥)«€  @€Eì¿Ç>%ÔÃ!I%ô‚:t$ îV¶ @€ª ,ìYD<«´C’ªužÔ¤£T“ @`ì‡î}@QMŒ€üíÏyÕkŸú›vÛ½¨Š© ÚàÚt.$@€ Ц@Q3À‹ sHR›},¯¾À}÷€ò  @€üX ¯s€çò¾ßÁÛNýÝ—û+sSJ@ ppá¤z @€“ˆ­§J^iu›’Tl”>‰Q¢‘À e@€ @ ±@™ëŸ—5+Izÿio8uó‰›+ý€ûqW* @€¥ƒ€£Â³C’þç³_у' \—©0 @€ÀJÛz}â“?öƒ§¿)ŽJaOhÒ¨À£î^#@€ˆÀ!RÓŸTsvHÒïžpzɯ.‹Tm3€3 +‚ @€À…=ö¢ÑÇ<óœÓÞï±òêyÀyœ•B€ @`u‰l½~÷Ç!I±3Ö©›O4Jt*°ë–m[;- %ó+¶nKI& S8î¬3§Ödí%@€ ÄàØj‚ _µÉÞpù[>yÎ=Ü„@f€»P•' @€Tý÷Ø'5éÒ9$iÜgÀ}ê+› @€À{ €j8$ɇ¢;pw¶r&@€ 0_à nŽC’æ)ª ñpõj»byÞîöšè0FƒZ @€küï¾yÊ'!­?.øáƒg]yþÙW}Ìð!Њ€àVeB€ @ ¦€èw8‡$ÕU.[C@lh @€èMÀá·)ôIJQ’&E@œ¢$  @€NöØ·“|G—éÆ {¾ñ¤—¼ù™¿]ã4(Ÿ€8Ÿµ’ @€,°t¥!á¤J\¯ @€z°tUz‡$U“~©€Øx @€ Л€%Ðõèg‡$¾ßÁõ.wÕdÀ“íz '@€è_àû÷_‰aÖ ¢ßˆ#fõÕºp?îJ%@€ f€› ƒÙ!IÛNù=›i7aœÔµàIu·Æ @€”%`¬æý±åÐÍ<ýM§n>±yVr½€xô]¬ @€å Ø«•¾™’’ÔŠçˆ3¸s5 @ hXÁ+`k±‡b8¦‚cB¸Åòø*£ÀA€ @`¹€-  ±ýÍÏü­7žô’øK!URîÀÝÙÊ™ @€Àš–@58f‡$sàEÕJeZ·N*C @€óöØg~")2 Ä!Iï~îk^ùsϳ9YFõÜE €s‹+ @€@˜.sÄ!Ig?÷5±Mt™ÕS«†à†€.'@€ PGÀ&XuÔ²\³yaÓ9§½!"á,¥)$«À®[¶mÍZ`ÆÂ®Øº-ciŠ"0iãÎ:sÒí×x P]à‚ýY¬¹­~+ò \zÓ5oúÄÙ;ï¿;_‘JêX@Ü1°ì LC@<~®ßJO$ëÛ¹’@²€¯âdªRún,¥'Ö­Ç=Üÿ–Ožsá —¢¶*9WÀè¹D @€hYàÐ -ç(»n’Ôko¹ €{£W0 @€Àd<¬®wHÒ°úkÚ €GÓ•B€ 0[@¦«þ½¢I\—­Zað8úQ+ @€†$` è!õÖ’º:$i ·XmðÐ{Pý  @€†'°ÿö^¯Íj줡öÜë-t÷©< @€À €4Ènû÷JoØm÷WþÜóÞþœWéÇÁõ£xp]¦Â @€ƒ8tï߆É7àÄMGðô7=ûÈã'/1$ðzK]  @€Æ!`æpýè¤Áõ£xp]¦Â @€ƒpðà»pI’4 Þ¨³T• @` 1m/‘Ž¡%ÚðïIÊX¥§Ô“ @`$Ö?¤#W4#IÚvêï¾ßÁcmàÚ%A'j @€ÀÀCê­Šu=æÀ#Î9í góÌŠ×IžI@œ Z1 @€f¶€÷Hˆõí¿{ÂéI*³—Àeö‹Z @€ŒVà £m›†ý»À줓?IQࢺCe @€Æ/°°Ç>ão¤î²Kìvö?ŸýŠ7žô’ø BÀ…t„j @€LEÀ;ÀSéé·3Izÿioˆwƒ'Õêb+.¶kTŒ @`œàqöëÚ­ŠcŸßýÜ×¼òçžçø«Þ»^Ü{¨ @€À´öØwZ ÖÚ 8$©„ .¡Ô @`B18¡Öjê‡$õ>À½w  @€LH@ô;¡Î^­©Iêw€ûõW: @€À´¬žV¯ÑZ‡$õ5 À}É+— @`ŠvÀšb¯¯Öf‡$õ2À½°+” @`¢à‰vüÍvHRæñ Î ®8 @€I X=éî_­ñIÊ9$À9µ•E€ 0uC6î?uí_MÀ!IyÆ…8³R @€üHàнA`U‡$eà ÈŠ @€ ðï ë8$©ëá!îZXþ @€þC@l4̘’ÿœ›R‚ªàªbÒ @€¨)ó{qøMÍ‹]6%'oΫÞxÒK ˜v»]Ü®§Ü @€¬)ûýÒ!.‡$½û¹¯‰wƒÓ/‘r}°B€ @ “€õÏ™ GTÌáû1ð+îy#jSŸM÷©¯l @€I ØzRÝÝbc㤃#n1Ïifµë–m[ÇÚò+¶nËдãÎ:3C)-B”Ðqz¡„^P‡ ãз±aV²@†@4ß§ ä1°X·cLå ¢§Ê¬ä?|ð­—»ýÚKÊ¬Þ jexݤ’ @€cØ}ÆÐ mèI 6Q{íS36Dz–¾v€kÓ¹ @€@5öW“z¥€C’šŒ p=× @€¨ pˆ] +hIº¦€C’jpm: @€¨&°°Ç¾Õ.šÀÚIª1:À5Ð\B€ @ Ž€s€ë¨¹fm‡$UàªbÒ @€¨#`ã¢:j®IpHRÒO’€Ó­¤$@€ P_Àúçúv®œ'pÌGœsÚN{ÂÓç%œúO}h? @€@[@çqžl)IJézpŠ’4 @€š غ© ë’´>’8aIB€ @ ±€%Ð e$à¤u˜ÀIcH" @€ Ù¸Ã\N ]À!I«Z €Ó‡” @€ê ˜®oçÊZ³C’¶n9µÖÕã¼H<Î~Õ* @€Òl‚UZL¤>[·œapÃiïúÍ @€rØ+‡²2V˜’‹¢ñ€ @€ Ä5±5QçÅ(€À1ßxÒKÞþœW-ì¹Ï”‘ÀSî}m'@€È$`ýs&hŬ+‡$óü?ŒNÖI<Ù®×p @€|Ö?ç³VÒº1óÀ1s¤O°Ó5™ @ ·€- s‹+o]x8Þ Žwƒ§æ$žZk/ @€@–@÷€®Èu¦yH’ØÇ‚ @€@çï½Ðy PQà⯺ì¦k*^4ìäàa÷ŸÚ @€ BÀ ð ºi"•|à‡n¿ö’_ÿÀë~ÿÂw^}ûW'ÒêY3À“ên%@€èGÀ;Àý¸+õ§vÞ÷YWžê^÷–O½ÿ–{vNG<ÁN×d @€ÜvÎ-®¼Ÿˆp7‚Þ}Ϻò¼ƒ'Ë#žl×k8 @€@>K óY+é§b‘óë.zW,xŽeϱøyâ<à‰Í'@€è\`ã†=§yæjç² XW ö¸:óü·¾ì#zá —£š € @€Ý XÿÜ­¯ÜZ ¦yÏÛqél«+oÙg©€Øx @€ Э€õÏÝúÊýßîyàþÙWoºø=ÓÜãjîXÏ%’€ @€@#[@7âsq‚@„»~Ù¹'¿÷&¾ÇÕ\*ð\"  @€4°ºŸ‹×رó¦ÙWç\}‘=®æð\"  @€4XØcŸF×»˜Àj³=®ÎØþÇö¸J »nÙ¶5=õ°R^±uÛ°*¬¶†+pÜYg·òjžA òA˜¡Q[ ÃG êæSP»ƒ2\øöç¼êÄMGg(Hˆ=®ÞsÕ7Þõ­‰´·ÅfšnSV @€V° –aÑŠ@ìquöU;ù}¯Ž=®D¿õHÀõÜ\E€ @ UÀ&X©RÒ­!0Ûãê×Ïyý;>ûá÷ßÍ©¶€¸6  @€$ ,ìéà$(‰V ÄW1ß;Ûã*f€57t9 @€õݸˆ@ Koºf¶ÇU¼ñ[ãr—¬* 60 @€t(`ýs‡¸#Í:"Þˆ{_õ÷o¿ò–#mboÍ÷F¯` @€)8x ½ÜJc…s¬sžíq+Ÿ[ÉS&ËÀ† @€lÝ!îX²Ž}­f{\Å?íqÕi¯ €;å•9 @€ÀÔößÃXSë´?N3ŠùÞ˜õµÇUžQ"Î㬠@€‰ Øz¢?¯Ùñ~o¼åû‚sßh«yTmþwp›šò"@€ °Làн`B`©Àl«Øá9öy&“Y@œ\q @€Ó0<­þ^»µ³=®âD_{\õ8$À=â+š @`üÎÏkaìkõŽÏ~x¶ÇÕ-÷윗ÜïP@Ü!®¬  @€&.°qÞvÛ}âSnþl«S?𺳯úXÌO™¢¶ € éÕ @€¡€õÏ#ìÔ´&ÅW¿á;g{\=ðÃÓ.’ªspçÄ @€˜¬€x‚]á —Ïö¸ºøÆ«&Øü›,.¼ƒT @`À¶€pçU¬zLón¿ö’Øãêu½kÇΛ*^-y&p&hÅ @€LPÀ ð:=ö¸:ëÊóO~ï¼åSï·ÇUá=..¼ƒT @`À‡ì½0àÚ«ú<Ûåßþä’÷ÅWg]yž=®æiñßÀEtƒJ @€ŒRÀ ð(»u±Q»î²ë»o°ÇÕ€zY< ÎRU @€ €ÖaÕ«û’'ÿŠ“®ª³õv…¸7z @€Œ^`a}G߯‰70žqœtø±GPóÀê,U%@€˜À¡½<°.«QÝ—{r«\Ò‹€¸v… @€Œ_@ô;þ>þq 7/l2 <”¾¥§Ô“ @``Ö?¬ÃT÷ùG=½ÁÕ.Í' Îg­$ @€I Øk:Ý}⦣cx:ínKÀÃí;5'@€(Z@\t÷´]¹­[Ni;Kùµ/ nßTŽ @€BÀèI ƒ˜öÈ£ü—ßGjH€ 0HC6î?Èz«t-8 8ήu©‹ò €óY+‰ @`R‡î}À¤Ú«±/xÂÓ7nØ“CÉà’{GÝ @€,`Aì€;¯VÕcøÔÇPëReg‚V  @€ÀÔÀSëñho¬‚Ž0x‚ J“ÀCé)õ$@€’@DAVéÃZªk<õ0 Ü’e'Ù€;a•) @€ÀÄØsŸ‰ L¶ù§õôɶ½ü† €Ëï#5$@€žÀ!†Wi5nC`ó¦“?¶œäѾÀ®[¶mm?×2r¼bë¶ 9î¬33”2Ð"tA §JèuÈ0}f% døDó} J§n>ñ'½¤´Z©O+oÙqæùoÍS–R* ˜®Ä%1 @€$;`%14Ñ–C7Ç<ðH7ìf €‡ÝjO€ P¦Àþ{x¸ÌžÉT«­[NÉT’bª€«hIK€ @ MÀ&XiN£M¯[P`ï € ìU"@€¼€M°ß…g7ÎC- €[• @€XØc_xÁžn¸´1 .­GÔ‡ @` ‡:i ÝØ¨ vÛ=6o”…‹Û·-*? @€É ˜÷›üø À ŸøÌƒi”# .§/Ô„ @`$Ö?¤#7#…ÄBèÆÙÈ 5pk”2"@€ 0°´‘°(pÊæh”# .§/Ô„ @`$¶€IG¶ÑŒÍ ›âH¤6r’G àeA€ @`©€%ÐÆÃR—{2Bvݲmk!Ui½WlÝÖzž+3<î¬33”2Ð"tA §JèuÈ39˜¸€{’¢ÀOz‰í‹ê‘Þ+sÆö?Þ±ó¦Þ«¡f€ @€- ˜ntøÙmÝrÊð1†€ÇЋÚ@€ P”€M°ŠêŽ*¯;º„Ž—Ð ê@€ 0*3À£êΖó_žøÌ–r’M}p};W @€X)°a·ÝãôW2– ÄÀFï£BÜ{¨ @€À¨¬Uw¶×˜x2rÚQÏh/?9Õ×Qs  @€µll¬%pÚQO0˜OàñM€ 0B/°S[jR,Ž…Ð-e&›:à:j®!@€ °–€%ÐÆÆ:Ï?JÜç÷©¯l @€ñ ¼÷Âø¥Em ¾ßÁq$R[¹É§ª€¸ª˜ô @€Ö0l|¬/°uË)ˆú÷%¯\ @€q xxœýÚ^«6/lŠ?íå'§ à X’ @€˜+`è¹D˜îk €û’W. @€À8,g¿¶Úªx Ø$p«¢©™ €S¥¤#@€ 0W`ã†=ô:WI‚8eó ò €ó›+‘ @`´Ö?¶kÛnXÇ·«üæ€  @€­ XÿÜåØ3Š•/|â3ÇÞÊâÚ'.®KTˆ @`¸¶€nßå¯ù©›O´`>3»83¸â @€Æ,0Ü%Ð÷>ð½1wL‘m‹%бºÈª¶RàÑv­† @€äXØcxou^zÓ5gžÿÖ­ç½5?—ÿ‹UÐy8¯·Ò @€F-0¬mÎÛqéÛÿøUÿö+oÙ±cçMßxÕ¨;§Äƺq!ŽD*±f#­“x¤«Y @€}D<ÓG±ÕʼçûϹú¢“ß÷ê7]üžˆ{/>ÇeÕ2’º ­[Ni#y$ €“˜$"@€ "Pø&X;ï¿ûÏ/;÷×Ïy}ü3þ¾¬E1¼4Ni¯4Í6/lÚrèææùÈ!E@œ¢$  @€$b—@ßx×·b¾7f}cî7f€×jÌÙW]ÔN‰ZpR«œëe¶ë–m[³–¹ +¶nË\¢âLVำΜlÛ5ÿ±Øé*%Ï8•ç¼¾¹Ø0>¥ M/c›~ÏÐwf€3 +‚ @`¥­žíq;<'F¿ÑIüðÁˆ–'Ñ[…5ò´£œ‡”£KÀ9”•A€ 0Bžíqõëxݲ=®»àƒ×^apbbÉÚ8õq'˜xo s|ÀA€ 0 öìùàØ×êŸýðl«[îÙY=¢ßˆë]ëªÚ±øü%Oþ•Ú—»0Q@œ% @€9ûïÑ[<ÛãêÔ¼îì«>¶ÎW‰]« M'Zµ˜ìä#0¸Å eµR@lT @€hG —%¬±ÇÕï_øÎœûÆxã·­¨5f’/¸áòvPä’,ãçOð&p²W­„àZl."@€ °BàнÈ©rá —Ïö¸Šó{[/÷WÿSëyÊp®@¬‚6 ŒÐY]%'ð|qð;Z±/_ð„§oܰçk^TÀEu‡Ê @€@¹éÁRbôUnS^³ôžxCUŸ@o±:B÷VüT ŽIàSg;è¦Ý/n*èz˜‚@˜ªÆ%Sì´5=è¦N{Dæ#øÐuàº7VAGÜCÁ#*R<¢ÎÔèF vŒTûÂnÚ1ò\kk×¾pä šG`]Øæ:ŽD‚”Y öÁ:ùÈã3:²âÀ#ëPÍ!@€–FG5æ$[nÀ²kŽÜ°—'`¬‰V8ëÊó¹äxá1¿”¿Ð1•(Soj ´,ÐV\ÔV>-7oÙµeÛV>£@ÕI;vÞ’’JÔžÀæ…M'~l{ùM.'ðäº\ƒ  @ ñUëìÍ'~—UIµÞG2½€Ià^ºø…O|f/厣Ð]·lÛ:Ž–hEi¿âÇufž;‰((Úž§¬õ‡Ù¬&þG€Àh:úbÉÿ]ÑQC–vôh•¿!£ù¼hÈd>xú›ßïàÉ6¿¯†Ÿ±ýM¿×Ã7\Ï­ô«2Üë” –pËÒ;{éãRý J »Otw9 ¸~e[Ÿø]Z½S¿c\9UÛA÷Òó[·œÒK¹#(T<‚N\Þ„Þ¼ûŠEû*×Ó?Eš4y®¿H; áÆÝ{]wMèe(bÜ}¤uSˆãXੵº÷öÆkÀ±#tïÕbÀCìµõê<ñŸm1ðØ´öèC Ûi¶‚úPl¿ÌœO tMûý'Çñ <ðÃ?ðÅ‹ÆÛ¾r[g—[¹‚k&.¸sªW­„ìBÐêr-_QBG´Ü$Ù˜Œ@æÏoæâ†Ûù¡ò—8ÜÞQsçí¸4Â`™^ð„§›®a.®Vè%%üT—ý–P‡"%tG¡#Uµ,ÐË'7çÄfÁökV­GŸ^ÆÃûH ÄèXÍ!³À†Ýv?ùÈã3:‚âÀ#èÄ5¡„éB"ÏÐ(¤&%tÊHÆ·fÈ"Ðïg¶ßÒ³×)¤w–Þ+PGÍ5úøk« û`UÐ÷Qò€Ë¸ó«îçye/axii”ðEZB¦ÑÛÕZ©_ªyI=U[îÙyñWMµõ½µ;–@ÇBèÞŠfÁàaöÛ’ZòÃ,à\u$Ò;ƒå@ cB>ª¾H;îçúÙ2Bê7À•²œuåùYÊQÈO œ²ù"•À•¸ŠK\ÈOr™7m…Ôª>*nìªb ùò•UL·ü¤"å°2NJë õ!°T`ÇΛ®¼e“Ì›6Å‘H™ tqàw_!?ÆåܬìËBêVHO x¬«:Î ùxòeÕs£ŒËÁ)d´4Òt1Žœ‡Ô1ðêÙ¿äØ“{)w … €ÚqEìz5»BîœzÜÅtݤ’z($ž)äkª—.H,´¢BÆL¢›dò ÄkÀ1œ¿Ü‰—xÌGÄ<ðÄÒ›/N·*(e9?ÀåÜ”Ô=kW¥œŽ—JèTÀç±SÞgn䌸s5­í×9©Èj™lÝrJµ &œZ<¼Î/ç§w(ÑoQõ,§û†7ôÕ˜@{å|‹ú‚j¸ýœŠ‚*gü´-Gλþ²8¸q62¨&¯ºq¡Ú5SM-RϵŒ¶¨{‘¹½XTmÝ9Íí/ t'à‹´;Û®söMÞµ°ü ´"ðÀ|Ïç?ÖJV2©$ð_žøÌJé'›X<˜®55ì*wN ]N`E}‘õ¥4”Î- ­¨á4”Tω\pÃåO¤±å43ŽcË©O±5Û5?U±Ò~e‹ºI]ZŸ¦3JI` >tí¸eÕ.í›Ü¸ǸҊvb ô¯õ&p»¨ósÛ°Ûî§õŒùé&ŸB<€!PÚkQ7迵«XZÏSå ¬/PÚÇÍé˜Fli£kL¶Ú2\Xm8÷vÔÓ# Î_î°J—Þ_¥ý¬ý¦­´úõ:béõ#PK ÀOYi_Dµ\û¼¨@ÀÒ~¬ûìeø±@L_zÓ502 ÄèX¹ÐÁ'.·ËÜ´uÔ7îœ:‚•- K ü *°ãæV©@ÆÛ\F t*pÖ•çwš¿ÌWxþQà9CC\èg§ÀßÑï6jw^m)°Çkóº@!~¬ üò)¤³jT£@ÌŸ\×€u ¶vì¼É$p[˜éù¾ßÁq$Rzú ¦—ØénÚ2ôJ™wN®ðE:…Ž.ð›<Ø {S ÚX¦À‡®³V=³uË)=”:œ"ÀÅõ•Îl]Rà“Ùƒl½¯ q øEZàÎ8Æ@™°ŽÀqt·V Nà⯊yàÁU{èÞ¼°)þ ½ÝÕ_Üm國 ~ʼ½¨ì»Úe6ÍS++“i ûE:ÍîÈÓjßäyœ•B žÀ®þ§zºª‰€IàuôÀM†V›×ó”ycÑ"}™ ,v<´(/+­ ûÁ)ó{¦uÿ3,SØã˜‡„¢Ë¸à†ËcGèrê3‘šÄkÀñ2ðD[µ™àªb¤wÓÖ ëÀ3uç4ðTý¬%^ʌͲvÏ´ +ö'~ÚÝ¢õùâ4à88_yJúwÛA¯5À=JÜ´õÜ?.¾äÛSwN%Œu(\ äIÉ_/…wkÕê•L]òo}Ugé Ôøàµ—D\ãB—4ˆãXà&9ŒõZpŸ=릭OýŸ.»ð;§r Ô„@Q…Ç%±ÕmU¦pð’ôÛêùXU ¢ßˆádذÛî/|â33:ˆâÀ½uSÉ?„…ßCtÔg%·ºð»üŽzD¶Ö(ù[4j^òWʈ‡Váì¾ÌG<ö4m}Xm8ÿ 9uó‰ç/·ðÀ=tPá¿…ß=tÚa…·½ðÛýN»Fæ– þ-*úíw¸þM8¾Ìû!JïE öÁŠ#‘z)zÊ…ÆèX=eUÛ.Î=$ ÿÙ+ÿ¾¡ë+\ üûþ®;Hþ ÿý–0D ÿ&ŸÅÀåäºRÆ$pöUŒ©9Ci˱ zEW €óÞòíÊ¿cÈÓ[å;¸mÊ3”Rš@ùߢ¢ßrÆLùß䦂Ë-j’G`ÇΛLç¡^ZÊ¡âH¤üå–\¢8Gï¸iË¡Üjåß9 bPµÚ'2›ºÀ žû”ÿÕ1©a4ˆîðe>©1©±ºÎVX=Œ‚­[Né¡Ô‚‹wÞ9nÚ:'î¦wNݸʕ@e¡DƒøÒ¨¬?ð †Ò)ƒ¸UøXPý".½éš˜.¢*SªÄæ…MÇxÄ”Z<§­àƒ›¶q³díÎ) ³B¬)0”oÑhÀP¾.&8Ú†Ò5íEšÜ¢ÀYWžßbn²Jxɱ''¦œB2p'½< Ÿ±¡ÜtÒO ™Åg@C.A]?МØP¾(&;°¢ƒ†ÒG¾Ì';J§Ó𘎡§ÓÞBZ¯ÇîµÃê¯Á}zí[…Iàƒ×^rÏ÷©Æ£¨ë©;!ŽESš6BÜTpvýà~¥†uÐN'5ËepbÃzÓ¬s\=Á}‹zŒ8Ða7Ä/sßçlª½–@LŸwýe|2 lØm÷—<ùW2Zfqà¦ý⦭©àp®wÛ4œ¾RÓ! ñ[Tô;¤¶¢®ƒû2Ÿ=g´¹ÊX&« # Æ’Yàä#08s¡'®ß)nÚêÛ öÊÞ6 t¬v˜¨xªÀpGæ¿ R{e醨ƒÃý¼LcLie5ØË$p5²6RÇè<Á›À»€+¦Ù/Ð@ŸÅñ'¿ru|Áp ‡;n;îRÙ÷#0ÐoQs¿ý —Jè—¹oòÆ‚,ûØ~Ý%ý<íR­‚Žþßu˶­ÓZ?ÜÛµY#óÿØçËß®ÀÌÓ´ £³bÒ^Ð*ÖQòq ú³3ÜNöâdéô“ ÿùìWÄñ<ªð8ªúû¾óâ¯G[êµBœä6ôÈ^¢ßlQb?ÿFÒçG"?úç¥Ç¯šæ#(>ŸæÝT;‡Aã×nµ G °åÐÍÛNù½4dXMرó¦3¶ÿñ°êÜnm-^ÏsЫ—6ÌOc»›ÅÜFk5]GcC¶KF0ÌFða7&×zÿŽà#f|NSàÊ[vD06Ͷ÷ØêÍ ›NÜttè½h3À«wA†‡åyú¾ßõ<Œý¶qóZcŠçó|¬”’(ç ±2M’õþ%Ó¤ò³k3ô¥æÝÔV#è‹¶(äS¾@,Ž…Ðå×sd5Œ%бzdJoŽø§¬2Ü"¤÷Mó”½ÿæñì½™yn.›‡J9” Z©Â%糟§Éãø,dèPydz)ãè‘ôöJ9\ ^ôg±;ñpë?КÇ*èÉN¿ €s<ïåƒQÂ/_†[®°-¡¥£Œgã¶Þ^>D ­*ç#_µVµÓiðgèšÑpe°ª=&k\8š~©Ñv— EàŒcžù»'œ>”ÚŽ¦žçí¸ôM¿g4Í©ÔéÀ#û…[Öë…üàåA.¤±#ŽE•¾U§™8χ=§m9_,­´:Ck¥§ºËddÔ”œó lØm÷ó^øf“À™åøáƒ§~àuq særK(nrp†›€Þûµœ¹<Úå´wÖõyZÝã0+ ¼G EuÀogø^‚6ˆ/„ñuÓ ØUr®À+îy/=öWæ&“ ]s®¾èÏ/;·Ý<‘Ûøà ¿úåôti?lyðKkõXC‚µÆyþå|$ÇZ“<í¾ôF9¤3t·¾FlírGÙeµ5\د@LÿÆ$pL÷[©•~Ï÷ŸüÞ?ˆ©à©5|„p†Ÿù2GI¿dyú¢À†O-^ú‰(³;ÊüÌ®Vy>Ñ=²Œxôfè»±êe ëqÌÏŠkßõ«•â5àx¸Ò%7ˆà˜nžÏ°r|<…_¦”!Uæ¯WžÞ)³í³^Ë#2BzLSrõÈ2 ¢'2ŒÇ=P3t"À}¨×ªê¸;q4î&Äá´çœö†q·±ÀÖÅ;Àñ&ðÔ&gøñ.pP¦T©äŸ«<½V²€x1\xÇ¥|úFœ&χ·ÀÑÆ ʰœñÜJMFß¡­(ɤ]88Žn7O¹ÍxÝEïºð†Ëç&S‚‚à ?Ïcê¹Y[ ÿ}ÊÓ§…#ˆë}îÊïÖzí*üª<ŸÙ¢&2Ò2ô,É¢v‹•™H϶(&«ÚÇxÄ»ŸûšÚ—»°ž@œg×»v Wý§Ö[µË~õÑ¢@Ü=¸0Jˆèhö§äJvQ7Ì.TÇçÇÌd¿Æ=’ËlÝÕ·5‚±2ë6âZÅâó©M¼ €9žTCì¶ Þ6 ±›Ôy"¾E'ÒÑ]4ÓàéBUžfg]y>Šü§l>!¡=–(î¿fÑ⨚p\¦ï èU PúË#zh¾ÏÑM*98‹o¼êÆ»¾5¸j½Â1óÀCoEzýÀéVý§ôÔ¹ÿ>h\ؘPí LçVÞ°ýÑ3á§óÁ™p'kzºî’J|‘/=öäé€Ó×~hÓU Õ› H’hYÀç®ePÙýx+ʉŒ«‰4Ó .Aàƒ×^gó”P“IÕ!&öÜg"M £§óû:€Îh¯Šºµ=K9˜#àãfˆt* 8ì”WæSˆ3i·›ÎÞëvÛý%Oþ•ìÅöS ¸÷ôRý¬¦[ 1¥ûò!öš:KÀ·è°úk µõe>ÐŽSí2¶_÷‰ƒË¬Ûˆkõ‚'<}ã†=GÜÀŦ €Ëíe¿¦åöMÛ5sƒÞ¶¨üüHÀ·¨qYÀ—yfpÅU –@ÇBè±¶®ØvÅ$ð©›ÄvÐàB¡ÑB;¦³j¹SïŒVÆIãûÎ_‹’:R¢¾FùeîÓÔ÷°šbùýÅ‹¦Øì¾Û« # î»—/j£üí¬Š0Ùôn2&ÛõÞ¢€oÑ1eUOÀ ¬çæ*‹·Ü³3ŽD’Y öÁ:ùÈã3š¿8p~ó5Kô{YPgôWà?{%^ÀÇgð]8®ãêO­É-pÖ•çç.Ry»ìòÂc~iô à"ºØodÝPR%&>$Ì„—4‡Q—‰d†ÑIS­¥Á9Õž×î¦;vÞtõí_mš‹ë+ l^ØG"U¼h`É €§yËëwq`Ÿ˜¼Õ5<òzO½´~ û˜L}à¤ýý|…îpk>¡¡šë ¼çª åxþQOÏ_hÎ €s6»²ü¨Ò…WÃý}á¤z= øí_ÑU|™W“ž@¼óÀ2 œ¸éè˜Î\hÎâÀ9µÿ£,¿‚ý¸¹T7úCî=uo_À·hû¦rÌ"`èfaVÈxÎßqÙx3œ–lÝrÊp*[¹¦àÊd /ðË×pÊ—£z|}ªEsŠö¦¦çö—Ù¬‚ÎF=+è¼—¾àÜ7Æ.Ü™ËÍSœ¸MgOpÛÔ”Wu#°º™+J(*(HÆ.à#0öÖ¾Ê1 <Ö`¬²E—ÜóÀýç\}ÑÉï{õ›.~Ïw}«Ë¢úÌ[ÜŽ¾ßªvåÒ’€H¸%È)fÓãœq;ŧÍk ¸µ0:,8ëÊót'þü²sýœ×Ç?G¿í¶¸Ñ@r»ÖˆÏÅÝ ¸êÞX -¨- Êb¤î4FÚ±šUY`ÇΛ®¼eGåË\0O `c¾7f}cî7f€ç%Ã×éE¿FuÔ\ÓŸ€ÛŸ½’ç} ‰™?,=.I‘l‚øâEluwMŽ ¯úû·Ÿ±ýãßîJ)0ç]·lÛZNµ®Øº­œÊ¬¬‰ƒª½“§CõKÕ~‰ôyº¦FÅâZÏ­Å«2 ½Üb¥d¥OS””F‡¨³Tµ]sN{Ãæ…Míæ9ÁÜ"âýÀÕÿs¿l{4¹¬¸Ìûr7jµ?~¡ÅKµ{gvaž>ªTIŸ¸J\%în`èߎºlýl»ëÐÅrõìÈzV‡öÒ¡ +pêæßxÒKæ&“`U8N9”úë/^tË=;§Ld ôš½oÕè”?Ói»q>¾î½¥™pöÞ^ AÀÇ*²"Џà†ËG¿ESàöŽÏ~øä÷þAìq5ñè7xÍÿÔó¼³Ý\†9‡¨°^k·×"·<·VµuhëZ#ÃÇ€­áßú%-v¨Onë½Ób†mu´m‹"«ÖÎ8晿{Âé­g;Ö ã4£÷\uA<8ˆéß±¶±j»¦ûН:b*¥oë—xýBub¥N©š8O'.­•­ÚG]¤oØï:±‹Ni’gÃM)Z§§(eKÓ°Çõf¶žRP ö¼àÅÿcÃn»×¸vR—ÄW±m˜ó“Wvú`_ëÙ>ü €ë©C¡ZI–¡Ouh+=Õ0“z­ï²wwy½­T½_‰+[âz]¯7³u‚ê Ä pÌ×»v W]xÃå±ÇÕÕ·u ­ÑÆIÀ¾ÇkŒŒV.©÷»[µhý[U¬­ôõ¯m«ƒæ“Þ¿º¬!u†ËÓ{³ve ƒÚtÙ.Lz3[§(¨žÀžûœ÷Â7›^¦‹œÏ»þ²÷|þcÞò]\*ö}]ïK¤»«Òk›ÔA¿7ÑëâÚ†ý®C»è”yÎíG=UCµ¯KæöfóŠÍ ³å°þxЕÙ:BAMþç³_qÒáÇ6ÉaLׯWÛ¯»dûuŸ°CXJ·/ö½œÒ¯…¤ÉpË-5$ éî”j¤ š"™!ͪ¥w2ÈwQDÊG¯a¹ÆFCÀ^.÷1ï…]¡­ÄiÀq&p+Y :“˜ì)ߘøµÇUz?ûMïÂ’Sf¸å—<*Õmq´øøWrë.ñÒϯNéÎ9Oξ ’<]ÙQ)>ïÁʶS‰OÇ+¾±½³=®jŒ±âàmpI±n¹ÀÅö¾Š PŽ@†ocp9Ý­&&"K #žHc—63‚ÞØÞ96yž`Û[iòj%™ @€ @ ›@Ä;vÞ”­¸Þ úÑW;.ýõ¼î÷/|§è·Iw€›è¹– @€~ξê‚~ Î[ê=ÜöU;õ¯{ÓÅï±Ãss{K ›ÊaM ‹î¢lëî A @€Àâ$¤8)NEkÛ#Üýë/^ôÁk/±ÇU‹]l¸ELY @€ I ÂÂØ9Say‹‰Õݯ»è]±àùœ«/ý¶ko¸]O¹ @€ I &/xñÿظaÏLåu_Ì¥7]Q½·|»“ÞíÐS·t—»œ  @€ БÀÿí_öØ÷˜ƒŽè(ÿœÙÆW¯¿è]±Ãó­÷ìÌYîÔÊ2<µ×^ @€ãˆw€ãMà˜ h“b«ó®¿,f}wÞ÷@›0¬j›V©- @€ÿ!ð½ðÈ}ñ¸…MƒC‰ˆ÷ÿÅ߯ÞΟøúUÑŠÁÕ ¶ Ö@;Nµ  @€ø‘À®þ§aAÄW÷žü¾WÇW1<¬Ê½¶f€‡ÞƒêO€ @`Ò;¿w÷æ…M‡ïwpù ±»Õÿõ©÷¿íÓÛ#.¿¶£¬¡àQv«F @€˜À‡®»¤ðÖÆWglÿã3Ïkìó\xUÇ]=ð¸ûWë @€Œ_ ¢Ê2çTãßXç'úÆšç2k8þÁñÓ-´zj=®½ @€F( ¡Ÿ}äñå4,ö¸ú>÷Ñ×]ô®Øãêž¾WNÅ&^Ç M|h> @€‘\ð¢?‹S‘zoÌw}ë=W]pÁ —Çôoï•Qef€  @€Æ ð¯ÿö¯'l:ºÇ–ÄW~Ù¹ÿãÿýëXíüÃû×k¢èµÌ @€ŒA`Ãn»Ÿ÷Â7÷2 |á —ÇiLWßþÕ18Žº f€GݽG€ @`21ézà^?æ #²µ89äKÿïÿï·Å?o¿ï;ÙÊUPm3Àµé\H€ @€@Y1ý“À1Üuµb«í×]²ýºOÄ_º.Kþ- ˜nSV @€ô)ð½“ÀOxÄ£»«Ä-÷ì|Çåþ?/~Ïgo¾.Šë® 9w!àà.TåI€ @€@?11ÛQÁñŠïï_øÎ8Ôwûµ—Øá¹#䮳5ܵ°ü  @€È'o^Øtø~·XäÅ7^õ–O½ÿ—ÿï8â¨Åle•_À p~s% @€ С@ÃÛJî1Í{ÞŽKcÊ7&~㈣Vò”I¿à~ý•N€ @€@˱V9Nâm’é=ÜöU;õ¯{ÓÅ~›dåÚ¢,.ª;T† @€b!ô³<¾FFîþßÿ|þë.z×eß¼ÆW5 ¿Ä1H…wê @€ PGà¿ð͇n\H¿2&Ï¾ê‚ o¸<ý)'`xp]¦Â @€ÌØu×]NØtôüt»ìréM×üñ'ßûŽÏ~ø†ïÜ’’^šá ˜nß©9 @€k lØm÷ó^øæ…=÷YÇ(ö¸Š³ìílïc:"š{\½ã³ùÃÿ?Wßþµ8àwœ ÖªyàyBþ; @€ƒ¸õÞG>ü°ÿëSïÛ§·7<x°*þ–@  @€ 0 ÿ4‰Vj$ @€L^@<ù!€ @€ÓO£Ÿµ’ @€“O~ @€ @€À4ÀÓèg­$@€ @€ÀäÀ“ @€ 0 ð4úY+  @€ 0yçO~ @€Àº¿ý /Øk÷‡ÍEºïÁï¿íÓœ›L @€@àñM€\øâ?Û}æVôÎïÝýì÷¾zn2  @€= XÝ#¾¢  @€ @ Ÿ€àTë×?íE³¤ì¹ï~Û{ö÷Ãö9`ö—½7ì±a·Ýã/÷>ð½“ÎþíÔL¥#@`°/ʯ>ñÀÇüÎÿk°-H­¸àT)é @€âÀI]ÑïóŽzZRÒ]v9î¬3SJF SwÿÆkŽ9舔" Ú¥¥iÞùk¿süaóɯaô1°¸êð¾LÄ‘|õm_}Ùßþi™MP+h.` tsC9 0!xvñKß6‹~ãO{ô“b¨ µ_S  @€C¹÷ÔŒ§õôsNûÃX ¯<,-öô£OŠÿ”±"Š"@€¨) N‚ûÖ½w&¥ûq"·ÂéVRŠÀŸüÒo½öi¿¹yá‘++ïÿ¿ì)ÏJCÔ“ 0epRïÿÕç>š”îljÀ£ÒKI€@á±ÙU¼:xòc_§žï½¼\xCT @@l  @`=§>ꘔSpã­`/I @€ÂÀ…wê г@ì›øįþÌÏ÷\WÅ @€¬+ N qÀobÒ=wXbJÉ(Mà/Nþ?VNä¾ûsÿÀœ[Õ˜(ŽËç&“€ @ /pª|Êíï,¯C7.¤f*ÅÄ6W—¾üq¬ÑsÿÔe[Ùm¿î’s¯¹8¥¦q¹mðR ¤!@€ô" î…]¡$Gûζ¹Šýœ£ZqÊÑÊ]ßöé^}ÛWS*}Æ1¿œ’L @€üàTó~ø/©I¥#@` ±ÃóöÓÿ[í»l›«UwuŽ—ïüÞÝs[öèýŠˆzn2  @€ò €SÍ¿}ß]‰I÷yè^‰)%#@ /X¨üîßxÍ+ŽÿˆWW­Ãª»:ÿÍ?žRágy\J2i @€2 €ÛßøÐ=ÚÏTŽ´'¿¯}ÚosÐëgyúÑ'-{¡7Žÿä׿0·"±ˆÚnXs•$ @€äç7W"= D»cç7çV"^ >í ÏX–ìw.ø_)§"ýü#š›¿ @€™vݲmkæ"—«û­@bé‡ísÀ²·׺0ö‹¾þŽ›³-*Y¼âXT}T¦¡@|¸æNrΊ8î¬3–5ÄË/~éÛbªvnÍ?|Ý'ÿä“ï[š,&cít W½$^Ž—çæ&A»ï¼üoã9H»yÊ­pØë.åw<¶»ókXxWªšô_±u[“¸¶EiFA-––•xý‰ó~_ô¤gÍíµx¤uâ_½rY²Û¯ßuÛiçþÑÜüÀs‰ºHàë® ÕÂóÞAªG€<–@çqV Å ÄÉF—ßü¥¹ÕŠ…Ð+ªÄÑܳÁc{-gÏåí%AÊnÞ½TL¡ @€@×f€»Rþ¦D†Ô[ uM™¥œe3å®OœZ¹b6eæ6ìWüÝ_¬ßW)ù$ô¶$¬q­€5¢¤‰vÃcD}¾zSâ[÷‘û<âÀ½»–Æ#ÎeïÂÄÃÍ{øÞ=?øÞí÷}ç›w{Ù+0ÅâÄš¦½vØúÕ‹ , yõ#jû¸…Mû>lïöÜgeD+â1eœ?'°|åΛ‹í‚HÝÿ°Gìµß†Ý²òõŠÙ“Ö;î¿û¶{ïüúwo‹gîÅž VL<ÁN_³ÉSŽ‚F9À)ÝšøBoü’=û½¯^–áùg¼%N ^§”U—O/K/Né¦vÓÄVÞ±™Y»yÊ­|pù}Ô] c=ÎSuÌÏ,RC:ˆ05^Žš[ÌѾ,M¼Ì³t;è˜ïuÑqlR¼ªTòÛbs[:â7ß}Lj[§iÌ&~c’­SŠã{|QÑKëAfC½Øæê÷N<½Þšó”¢#çÿúóÏïzEzŒ¥(eóÂ#SªT#M¼]JaUãZ—47Ñs-ã8ÇesO6Šßª•ïô~þ[7„BÄϱSô)ç¼¶Øý*ÇÓUÍZòùÛ~Ô_þG€À(fK¿+¹â!C –ÒM¥m6‹{_ô¤gu“ǤwLÅw=ÎÆR×Së¡Vݵ"eüL0x‚®É¬"¶ÿÚUsibËeiþñ«WÆêq2°ÕÎsõzOÏ8¼Ü{/¨ŽòD,K+ßu –‡ñ$¦Ì,ŸÇ‚á ÍŠˆè±‹Uęǒ8Û€™$Î ®8ʈõÌs_‰…ek®,u.·GWÔìÆ»ìÿ< îRUÕ2Ì×­Z¡ç>þ©]Ä`éÌÃIOÜiʘÉìzñùÊúG¿·îÿ²§<§ë¹ße 9ýè“ZoE§}=èÌÀƒî>•'@ e~ù3ssŒÃ$禑 LÏÞ|]™S+ ÄNE™#–Å G¹gwjÃú×¾<žÉöÕðeuŽø-f2k7¤ö…Ñü3ŽùåÚ—¯¼0&±»{{y­zÆZ舺[l…¬ÖØu˶­ýÅ¡^ýV@鋱Œ³Gxjøè}zÔ¾ýà‡ÆÞ¹=Öd4EÇu±eJsúíú”æL»§¬ÿþؽ|盧;g•”µŽÀÜþZ¼6Ö?Çþd0'+[ÅK›s›ïóÇ s“IP”@¼tšsÙíªm#åãP½ü,éß]íÄXGD|åòXÆÕ<óx ¯7ϧ^mµ¢^éÓ¹ªÿx@Ö±Ç@áRáXEùì÷¾:%åÄÓDÐû¸…M›ö=pÙ“6ñX+C\qOzDPŸùæu±Ãs½œ]Õº@L8ÄI'‰Ùv}ó—X ÉúH¼;7Núê ÚåÆ÷@l¥Ûõ–K)Õ‹­3oQ)ZëtlǹP±3vŠRwiâôÁæ/%¥?Pè¢!Ôv¡º2Opçô82­%Œ±[Ãaû°Î“ø÷}álTSah®‘T\Ûðâ—¾mÙŠ²xªGþ¶òt¹v­\¸R }ǵùïM3tYÜþþêÏüÂ7¾{[ÉÏeÒ·¨ítup†ÙK•¾–Õ0ήûö}w}åΛgÿ>îOöyè^µ7‘þú]·vîåDˆãpÓëvWzÙOóVz ÐQCúZJÐQsÊÌV\¡_À°VKšþýèÃßzvyú=ç5ËÀãýŸÅm<"ôýÄŸïô¾¼•îžf&—¾ü‰Ó>ùoLc…K§òâõ”Í',¾æPòsÃôw:ý.ò["ýÖbióç.ç‰_§ò35Þ­Íù méOUJç6×*%ý~cYñ {÷\¿ó¦¥ß–q¿}ô9|¿ƒ¿Þ—æÙпöôo¼ƒêž|ï¡Ù}Ãn‰‘S£ò³†˜NÌ ÓØ«! Ë+ĺ”¹ç¬Î²‹EÑò•”@۳ɴ™"¨ˆ×D¿m·“_Üü¥ßaüó­;Ú)5!—¨XDæ±̲ Ã.MM·›±ö{éKþvMµ“n\5v%Œïö·^zîú‹&â¿Æ^1VÕzæ[ª^R/}/û-¯ZÕx‘¸áÈÒË#ôýðuŸŒ_ØxqzÙ³ÂøÍ½`bˆO~ý UqšøGCªNþÇmmT2–^Çh‰¶Äüÿ)ç¼6þ•ÿ)ñ¾wi3ãw-Þi¯Úpé+ €+p}éŽoTH-éjw~ý÷ؘ’LÝ ÄLTü’u:ƒ×]å'’sÌÏ$¶4žÍçyŠ1 }cùÀ,2ñ“ŸXÃôd³"VÞnF‰í^%) ô(pô‡W*}¶$ñMÑØ­j ¶yá‘•êS/qÌ‘ö²ßòªµ¥(U[OR.Çcˆ#çO¸´ôð¯}˜Ð/W|x­ˆ@7*¹êpŠÿ)ÔxŒò䃬J*}%p®Ä¯ËY޵?~*4À¤7}÷ö”ZÇ\w3')†Ââþ/}uâçnýr×ÍYúΊ‹ÆTm[EG“ãíôÅèze¶1wQæ¼A¥û×¶¸ä3¸×JÙÙ{‘¢Æ«ÃT `Ò_z¯×Gñ­’¸'k½ü«^õ¸6Uº$ž)¤o´÷Þ*GÇ¥Q5Ž]Ìù¨G<:½”ôVÔxŒ’þ^wz…¥\* îj<<þ€Gu•õóýæÝßN¬þ|LbJɘ À ›ŽNlu¬@ënƒ¨¸ÿ޽O—Îú.«ULÕÆJÅĪ®•,î§ãýÕ¸åóÇI0>=L_©ÞÊåSøÙC6§79¾ ιúÓÓ/¦Œ&V‘¤_;i¥'NOíÙ·MõÆé¥TMÝ*}Àwìüf/䘴Oï‚zÓ§ñe>÷ v§j+jé5oòUïÓ£—ôÇU++¿×î‹ËWþ©4ËšnÒý¿Þ«ªƒøÆw+좴j뮹ýké­®´08=ÛÒRVºO»æöÖ?¾ wê±—jâ¼}„Ö1]»!7VÙÍ«À j7¼´ À]õÈ~Û»«¬žoúiœ^xW«>ö*½àú7_üxû5ø÷ßý¹¿O_9»èeOyN½úÄq\ékÿfEÄk·1aU¯¸v¯Jß·ªg»õ\+·`Œ×°Ý‰æÑî®”;îÿnÃÌ…¹ð ½+¼­ö©o\ݰ âòô^8îÐÇ¥—þtòÖ{ª=ú\V‡¯}çÖôZ¥ou‘ž§”3°‘[ à%ÞåTz±$w3”G€@vXüœø>ªMžÓÏm\|•ýÝŽOÏM¶4AÜÍÔ~;76P©úªB,×L>+5d:‰0ã|©Ø(Â`žEu}¥Žš/©·¤¹(±Ö+“þžj|}µ˜þ"ɦ}LooúŒýÝ?¸/=Û•)Ó'âÚGîóˆ&e¹vpµáqÇýwW»@êÕŸŸÕXÁ›± ÄÍnúâç@øè—«E§5Üâ–:vC©talâZ{.qÛçU*+Çnaé3U3o=ý?ü—Öól’aÐ-Ýn-ÂàØŽ;^7QmñÚØ/ªÅÜdUC ý‰äÍwßQ#ÿU/I|¸ÿÛ*qi>_¹óæ&ÙVz ð°‡lhR–kÀ­üË­å5áŒÒ_Å9âá‡NØIÓ ø‰@„"q˜P:Çå7©ÓéߌđUß\}ñ“ŸÞ¥)ãÎ)vÒªtmà ¨+•µVâôIŒoßwW+%¶•Iìݽrﱸã·%,Ÿé¤ïÿ2×$q.êGß~G=}nn³é_S‰®“,1€o^Ö0ÜÕðxÄ^ûu•õðóýzò^é»Ò_E XS ÞžMß9n/^ñw‘Mó#_úT¥²âíÜXË]é’Åıü¯êœsï ¡ÜëáõÛïU³ÅÏkÕa_üÒ·ýÅÉÿG¿õœré•V5×^y1eáõÛ^i úý~¿-Éô¹¨ô…Íés­é/!7oï>Ý«y&rXU@ÜÕÀذÛCºÊzøùƆ.‰8tãBbJÉ«@ìBTi/N÷¾Z‰\c‡ªXË]éÞqi¡5æœû]½|Rë]ß¿·1¼lñóZµŠgO{ô“â\ap!·N5*}‡¬šO¥Ïì}íÅ{ÅÚVZ‚ÞbܘþEñØýk]¯Ò憥o|è spùZή66bÿ’xƒ+åšæÇ¥”RNš¸]HÒÕ~Ö‘ÇÅ­CJýãDå”dCLǨtºJ³Òq©Cì½ÎÎèËÐZDŒ‘^PíãvÓ‹X5eú’³Ëcáô_~æCõî¥âFüEOzV¥ ǼqDΕ.i+qúI­]Ÿ]œØG±áöC²!ýÄ×Tlîø™o^÷;ü¯¶Üä“"“ð‰÷ÍÀ­ô]Tû×a@çWªjm•à ½ÜôßÓï—š7äü3Þ’ø8fj¡DÊG¾­4àj’韺©Út™jâ£Nýɯ¡Ó»¥ô/ôQ3wظæ?„VnYWôâ¡Ò)ç¼¶—¦ÇËØ(¸RÑM‚Òô§±‹UЇ‰•–ŒVjËZ‰+ý4týJ €ãç;}kŸe 0øú;n:Çeõm´b>©LbkîØœ,¥ÉñÈ餳;%åZiÒã–&eUNˆÚÆû«‰s3ÍŸ,ÅéëÓ^nú­xúýRó¯©ôA›^ÿ&{š×ZÝU¿§¿®ÖU ä[¼@óc ‹o¢ ¨/P5úØ#Îæ­_^³+c5G<Òª”G“·sã%çˆö+×ËBèöÜ7±’U÷K̶F²ÚÑo”?ý±¾ýµOû͸ŸN߀§F%]2H_XÅM^ŽÞLœµ‹Z5Ùð)ăªJ¾y÷·‡UWdÖ³°93øªÅ €»ê…2?u]µV¾hU n7«®òSyûv‹UƒÒØH©v˜T5ÚïeGèƒöÞ?q\”'VxýdÂàVçfRéTÕ_ý™_˜›áZ NÙ|BúµŸ½ùºôÄRv'>õ©o\kdRþtWÛ•9'îw³J£)K<š®ÔŒD bÂÿúóϯԘXÚ—}ïÊV J#‡ØàºRK×8©Éœs½J¼Vig Õkﲫfap¬xl2÷ØJMÆšI| â=ÏÄÖÅbézg8G÷¥BKQÒwúL¬ù’µøH_Wn‰Oc Oâ¬{ó¾H_c’¾ßuóZM-pµ/á«Z¥.X` »D̯j… Ì¢ßJ‹hbòðeû§%´'nÇ«îÛë*kï!§"¥ßýÏ|2/„N¿ÕûÊ7—Ѓ]Ô!â®x?\Ü…mäùÑ/:=çã?¾‘*Ý{¡¥×gÐ)ÓןG3ÓÏ5Ùïa{ÏM³˜ Òþ¬éÙÖNYi/ñôý®k×g²Ú«r×§ohÙüEùÊ•ëï‚ô= ú«cq%w=BÒ7uˆÙ³ât†P¡B‚®!P¥Ö±FôY¿å“ïïwñó²æÅ¹MU÷~çå[oOøû½OO_éUm²ùVjGþ8]¥Áj ¤W)q¬˜Á«ä™^H,"`«×ו šTâJŸ¸x^öÞÏ_˜ØU¿‘bðœøW¯ÌŒŸ~Öî&XÑÌô[â÷çOßD*jØËæë €Jû–VùÌ»ÓâÀ•yÓ?í~Î+×¾³ Ò¿;«Âð2.'îº&Ãë5îC ⥘i©4÷ÕìúìœUoš£ˆ&»}ÖøúÍs_õ'¿ô['?öøDÀ ßB‰p ñB`¼/š¸½pb—&‹îþÄŸ·¦¬ݪ—Ôx ”Å'+fŒ+= ÉóÉZ†þ Ðz‡`'ú´Xtây¦”ÒÑmÔ|ÒÅ"· ߊ)ueK ;ìÖ×{tXKY @ XËD«F¿±ñr¯ÛÅtôG¾ô©J¨±T8–lTºd1qÄQ1©[éÚ¸­¯”¾^âÇ<üÄ # LL™'YLžvîÅSìŽVÇDwÇþgq_ï•Ô<*%>qç^sq¥ Ç£™ð·V¾#ÿ&žÝÄ>JŒîfE²A%‡†‰Ó÷®;lŸ–µxyú‹qÉ¡Ú*·y>ñyOQ¥}+6o~Q9€+wG¬o©| ¬ðÕfPX¨zâÑâíf§'i7é Ë«ÆN±ËN¥×ÖVoûµŸH¿ ã&¬v¼Î’¾¼ÌÍN# Ž×b}Õ®L$Z ƒ#ÜJ¼D²µâWõ²ðÚ£Ÿ»”]üÒ·E¸;ûáq¥(+j‹Û'øRÌÍwß‘8&Ã3q?ªõ3ô‡å‡?9‘+’¥Û¦ç)墀%ЕCúÒ‹^VÂTnOK¤¯Ài©ÀÁg?–1ÃÐi3Ò߶̦ӎùú1 ·¡U•2|‚ªVieúôߋٵÄþåg>Tï}æ:}O§Òúç]2<ËHìŽU×jƽû/±åÉ™>SiüijõØ<)B¥Z .q½/“æÍì÷ë(ý¬ÅuÈ3´üóJ/G [oríÑRÉ*Jiñíž!Ýÿ°ª5ÿþ¿n1M7Á•Ï‹C% ¥çÄN×ÞOÙ|Â@Gi<Ù¬´xäºo½–ΞÍEØYÈ7ÒìÍ…¨OÓ¥À… 0Õ˜œ@‹Ïö&g§ÁÈõºqØoýÎz)â¥oÝ{g¥«qTébþU7ߊ;‰.nk*ÅÕU2®„ÙQâY“]Z³;Îx5^cé¢w:2)'Ûxðôì÷¾úò›¿Ô]•b@<Û:éìßöS^)T{Ö‘ÇÕî”xb°êå-n¾UµèÅô±ø9}C„ÙUÿøÕ+k×Å…vÆ7Rí]*Z¯RÔ'fƒ›ÔÇ;À•;%ý½Êr^<¨ÜÈŒ¤{–ÿNuúQ„qC|Ê9¯íš9ÝÖ;À]÷…ü— T}jñÚ&¯ÈöØÃÄ­C¥ 4ùŠHÿàϪÔÅOUÌí§?ÝÈvNI“w€×龘Ԋ½mªî™”>¢ƒÎßqY½7ÃÓKeÊøèÅÌ[ÕØc.ED¿õÏmë]ĹÅÍMÐã;ÀQ·JÇ}GúÚçÇþdU ˆâšœ07W>%AÝZÿN®ú£°N»bSÀU¿‹V„«6¤ÅÊÄ'ñ­—ž[ï»Ñ pÊ蕦CxE>1÷ò—”¾ßA‰m¹é»·'¦”ŒÀÈâ9QúÙ°KÛó„µ7ˆê×0~ž«¾—ÑcíÍN«.„ŽI•†kÉ–ñFÔ‘ýƵ¯ÿ§wõÛA K5·1ßKa;šÊŽŠ(ñÁi·›¶ºüËcÆŠÙÖ£ßhxLÑÇìSôˆùùЈ•ÖADTZ!2i±÷Uè·÷Q#ä¹jÕjįª—dKæq§f+knAñI|ÙSž37Ùª ÀõÜ’®zèC6$¥›v¢/ÝñD€Ö±¬SÕxÈ—þ‚‡€;]²1 Äg$á×»­|®÷ ·ÃGõÆc‚ÚOÕ…Ðír/Ðuº £ 1§ÇRØÙÑÁµ(>8tEP{Tä7é±ÄØ]/žÔX1›^çè‘(¢öƒªô‚ÊOyùÍ×Wªd¬ðlj—D k7*í)˜s×É¢æñ¦OÕ¸=–î—üK×éÎ5z$¶ÖûJ×ÐN½ä€=÷IM:áté;(t·À¬þŸ;ì¨Ä|b͆·†­$@¬zŠý$«Þ ÌšKæF°ÉjÕ£z£áG<üÐz êŽÐÍ7§YZÏŸYxdzµÓŸ¦çÙcʘ ›…Á1h»¨F„Âàõaãn8⥻ëÕë¯xPO%&>‹8ªnæn/ªñÃO ¿ý[käÄädÕŸ¼xÍ'v›¯7«^óö±DyÕ?ñoýùG¦Þî¦W)JŒuRký‰‡ëÔçWæÒ ZLéàÊhé§ÌõþâAå¶õtAú«³%¿_å‰3Àµ_€©Ú?é/Zx¸ª­ôéq?Œµï`òœ›Þœ&)Ó_kåµÏô/¥hT[»‹U}!0ç—OGï¯5$fKp»›„Œ.‹Ch´v“ÏNâµéoÃ&f˜˜¬ßí Ò[ÝúË¥‹>µwvˆÀæÖ{îüÆwo[zÚ#÷yĦ}¬ô&ÅZ=Õ×­xÕóŠgõoñìߥ «Þ Î ëÄ;+7nˆ¯»•GßÅf+_•¯W™ÈÿŒc~y­…5~GÌ'~¹ýG²ò+¬Ü¤¾/øÚwnM¬B±¯WZÿüÙ›¯Kl¯d†. cÂJô;ëÇ”‰Ù¸)‰]Fâ=Þæ«àþnǧSÆOÄQq×ÕÖ{¥‡ñ­Niu†4уÑћѧUçÇRª÷‚Ï;êiÕ7îf{lÆ“…î&]«NÿÆNƒîÄ&•ŸÜQß'Ǥkú|C,4ãÄÓoäâ';f‰+u±/ô”_Ž' •Ôû¥??úÁ¿÷ü`•à9 €k ¹¤}¯'À±1`ûÅ7ȱÒôï5·ßØ (—(T ¦þâ­§XÙ佸¡‰€p|/ý®Õg1U’íqXL9Æ]i§ÛWþîî´>…~T~ºZ³08f#+ÅW‰M[ ƒcÏÕÇcñÜ-}ÅiÜÇÇBÓSmññ©ÔG•n ;t@ÉâC§1ðâ šBLâÛ¯Æy¿³ÊG[Êÿ&\5æ\6éñU³ìOûóm|è+ûý+wÞ\u0€«ŠI߉@úÓ¯¾]¬ÕìÇ:#“0Ó¹¹ïd”È´|{yó~U¹xÝ1}å•í¯ }ªVY`¶ÓUóÐ7î)c1ðÔmVæ.û‚ø¥«´áYtzù“™ÉãéOwap´e”apÄéïã_»ªvô€qíG¾ô©ôQñËGlIO<¾”ÁuÊ9¯m}O¬GÜõý{»ãM?Ú}e†ýFÍ7ìö•õÿ~–µåËÊ]õc?(5žž €ë|.ÒŸÖÉ}ª×ÜôÝÛ›þÄ“˜²ëd?ÿÈ£Ò‹øÇ¯^™žXJÅ Dè³I wºšµ.ö»zïç/Œ#gÿW\l§¯_±ôY¸Y>Ÿ»õËmi×Õž…Á³£ƒ»(kǪrž#7ifú„±ä¾ùÛï1WŸ>KÿäƒlÒ´q\/zDÈšþ¾î:­ŽLâs±¸Z0}ÆõŽû¿ÛæD¢ßÐ[õ1Ó7ïþvG°keà‹w KÓÔûA×é¾.Î0¨Sq]“þYzÔ¾•ÐôJÓ¿ñ«ÙäÙs íU‹¡oúÆok¡ÅcÄØùcûµŸxñ“Ÿ½,0xX#­êô¯—AæöïìèàîÂàxa!ÖnŒ Nóâò›Û9á£_þôÜî›%ˆï±­H¬|‹É"dÝcóùÚSGñ—/; }ñ]‹mYšÕt¢ßµ†qú²ÍVº ~hâlÅ•YŸª÷v¡¸•~‘I é+âÙ¸ å5Ë""J»Ý¦ÿj6«—« t"0{×7掚‡¾Q¿X«ÆGþeOyÎZògað…/þ3/’tÒ£-eß„±ùY¥Ì>óÍë*¥ŸlâY|ÜYgvttðÐÃàôÓ†"‚j>ý;‡kCºê(-íЊ?JáßùñÐ3&Òç"ÄŠÁ{ø-ë¾JïW§ß[¦û¬Œ¥ä0 •ϳ欵˜¿Æªãu|Û瀕{h-þ›8`b­šXD–¾2¸ž[êU{îþ°Ô¤Òí²Kâ:™¸3î}ùÖiOxFzÅ7~»ßéEKI ¡@ÜeFÚü]ßY5â³óZ±..þ¯ÏÝÓ.V^ÅÏž0¸a'vwy<¨”¹éßJ\³Ä³£ƒ; ƒ‡ø{ÌÃIļõž;S¦$»îÛ_OIiJ;´"±ÚÝ%‹X4VøÇ`ŽuÑñ4^‰»¾¥7~³ÿëÿc´Ç/E¼E¼êäÞãxTw•œ›sD¿UŸú-æ9¸è7–:®ú3þ.À\ÏY‚ø­_¹‡Öâ¿YëAy ¤Úw×àÄ®ù©düð_/;tãBbJÉBàæ»ïHt8âá‡&¦ì"Y|ým^xdzΦÓ­¤,D ±Ç/ߥ/Gœi™¾ÍÌ:•Ûšø­Š»ŸÙ»‘yú‡H\ȨXVx82÷ƲKLÿÖîÊYkAŸW*hˆ±öÜ'±ßH>g1%ÃëwÞ”’ìÇ÷ôSN-Y¬‹ŽÇ qä{,lŽ?±ÌaögöcáCŒöuÞKŸ[jýÃëkG¿5vðêw`ÄìZK{?Å}ö0=ý™•’à:£ëÛ÷ÝUç2×ÌH?È+ýÑï¼2ëü÷gy\úe¦Ó­¤,A –WÄÜl¬vŽ_¾VÞ³ŠI¿xì·5‹¿UqQé ‚ËïÑKèÐŽêHâáH¥ÌMÿVâZ5q¬R,%mýÎ~éG,}uqóÕÎ!ýÁ\»Û ¥ßs·òýYÛgĦÏ-­z€mm™&ïýFô›>rj×°Ò…qŠg,ýXëÏ[·­õÆS|“w±°<½òñ¸ø0=ýªe)Àµé\ؾÀ—îøFb¦é_‰¦'‹GbéÛF¶qx`zæRèK`6å;[íœ>7»~mg›—Ä´ÕÒelQЪ[Y$6|1 îý=ˆÄ 5Ù™ÇZµi¦«Š­•>î>gapë gap<ÚˆÕñ…ÐV…ûͧǛu[tÑõØk¿ÄlïþÁ}‰)ç&«ýÆ.M ç*çÖ­^‚x@öµþ¬“çßíHÝ ®^Åæ^ÕÊ‹Qà¹Îäˆ/‰ûFZi„¶Ú÷Ü•6ŠWYlþܾ|:ˆ[´ÙW1+›>©2·21øßzé¹+7/ù¯?ÿü¹×ÎMõÇ6¶s[Zf‚wþÚïT*¦[ïÊÙ•ï¼üo»ƒ ߌ½ßÀ²‹é÷ևLj3L_[ž¾®p}®¸á|îãŸZƒ4îiÿò3Ó}`ìLÖã¥Å.˜= Õj5:ev‰¸6]Ò…ûiP&fóZÍŽÎ÷òй¹Æ'¾¶<}]áúJ±à¥ÒÒ¿Ynñd*ö¾Sôñü»?÷÷匨X­ïëÕG\Ç-=HÛøÐ=ê0ák¾ö[[Ÿÿ5àXViÒ#Þ{Ó_b¿HV¸Àҥαœ!ýN¢•vÅ'¢õ›uap+]“žIÕ瀑sl÷Z¤Az—rwttðl68‰Ä#3apŒJwƒK…W8ýõXuÒÊ=X„X5z<¢ßøHŽY©zÎfÇ’±Å½ÐVþ%ðE‚Uk7ñ½T©!³Äàh»Üÿà÷ë\暯~ç–„T?J’¾db†ë'«zð¯õ~­°Ë¤-˜µ‹e«q˜^ëKkÔpv³ÞîF>ÂàQã’÷‚?º ½ö5ÊrIUÙÑÁ…ÁQ™xd6Ä0¸ÇõÒéûWíëɦOŸühåø«˜ù¨±B*æ?FýÆÊç×rÇ=Ãì›mÕ×$ã{©Æž »nÙ¶µßOQ½À½ß:ÇÂæÄÏCüð_GêŽùý6*JáÕ{¢±&3qV*že«plTé`ìý³ìíÇlU],(>\‰o,ç”Ìï0åcߎ'tdÜ1ÔX¾ÕÄ-–@Ç&=)9Ľé3r¥×ÜlãQq¼}Ðʳÿ¹eM*AÜ ÖØ¾;žt2ý›ø5ã§_Ã&£+Ú>õQÇüü#Jü=­ZVÌê_ôÕ+kÂYµ¸eé+ÅÚîá«•6CÊ3øÓ5F0¶ã1nâÏYó~¯Ô׋C´y¹ ?+/O¿\ym„¾—ÝtÍÜïðUáªãmÕÊ$ŽÌøZû½O_ù_GqªV%·þàØh»R%îN ((fŸ/äùi ðªßñ}¸w×S‰9§W»®Ol—dsâ§èèsø~utï;·éð,«XÕö‹9¶ÝÚ ƒçvS¥ÑGUÏ=ŠükÜ—TªU¥Ä“ €eâ±Ewap¬óŒ“ò?lJùZ„±”&ýø$¦¬,>Qñµ^éòXü\àWF¥&H<YЋ&âÅx.ËSÞ{ÿ¡T~e=ïÔ[l‚0¸9fí£›}6Ço+‡øpÍŽn7 Ž.nfÔk`¥Bãæ¸­×€ã+·ÒwìÛ>ýÁz ÐUé;-7VòäƒL”¹îÛ_OL¹j²ªg^.úm‚Óûµ«~‰Uú`FÀ½÷£ ,HÿÁh÷½Á•=·}/~ò³+õP<–îýÕßJ–xp1,c P«³fAoLÂä™ìåŽ-ÎЮ%wê­ïâ3 ƒãaÁົ÷ Çx«wt³/ÃÞûnebJgWšA]«!ývñªÛá¬UÕØh •îxæ[Òó©TÃôlKK™þÄÿ{íפòñžþKwÅ-×7)«êÌǰæ~›È”píª+·«® —Еê°\ ý·9æuÇwÆ1¿œ¸×Âb,~î®;¦œól¦wôF ‹Qc…sÕÁÙ0Þ‹÷©b[Ë<·tmf bª\\i$¼ì)Ï©7Ò|VrΙ8ÂàØ¥¢ùÑÁývq¥ÅãqyóIàøŽ/ÞôžJ?23=Ï2S&>­:G·¬±ñ*{b󮎡R©ª¢ßÄ~)*™¸¨îP™ŸÜôÝÛ-·°)1eÕdé{q-æl½_UdéטMóÆ;H±mO¼Ó;›éÍôFÝâ¶&~Úc´xM¨ÒšÃVzv7¿M_Z™Ù‰¦ÂàÄНÁJ7‚¾ aKH6;¬öç«÷ß»ôíBfÚ¿¶ùb\uEØ×¾sk ¡w~ïžÄRj?Œ½ÇÒ§?wë—ë³j²JëbŸ¿XµÔ¤8×VhåýGpUvés|óîo'Si;ŠÄ<#Y“ßbu¨ÅÏéÂR®ˆ§Îñ»XEx6›æ…»]¯ó_«#bF¬“Œ½szÿiox›¾j…Á)ÀgÙÆz_†)Â%¤™}¾ª¾tPÂïݧ¾qu%ÀøÔÇr†J—,M‡ªK!¦ó)øö}w%¦Ïâ.Í0Öú¥ï¼VKËŠâôŸÝxµêé;‰P’­#Põ“¸jV`c¬D¹Ž-VºÞìÄúm®qÖeL”ÅQ%RªÓ@"è—Žâ7>†túsî.ϳcR(VH¦ »¨Æ²5 è%Ÿ¿í†ÄšÇ·n¼Ë“˜x–,Vž¿èIÏJ¿$À¥¿–¼2ÛŸ=dsbYñAØvÅy‰‰%kK ÆÃª÷HUß8pp[=2†|Š: 6ñÀÆp{âô}³Rú)½èÅÜòœõ—Rù¥iœ\U¬Çô1ñÛÅÓœôÅ­äç¿uÃÜ)‹ÄOGÕs€Óë)ã },QKHŸ’yTø7~¾¨˜?¥Ú¥©q»?«I¼z÷—ŸùP“»ÏŽZ4Ë6qôvqVj§íj7óX'ÇÕ¬stp9¿wõΦŽQúÞÏ_˜øNGhļq/çæ‡Ð¦wk¿çÏêÏpÓݦÄbî·Rô5‰å µ¿‚¢»cùU¢|À]oŠqÇýíÌ0¯z7˜Þ ) ÙÎ^ëç©j,`8¥[¥éA`ã¹—ÕæÑû¶yÄ_dô*}©]~ó—Ü7÷0DÆUdú²vÛ_¼åw 1å;7úm·èÚ¹-îd[ésº~qNÇ |ÃWk·¨¨ kG¿ÑŠ’£ß¢K®L‹’­ÜÜ(Vˆ”ó{_Y‰Û/-õ“±«B„ëÞã¿Fš‡jD¿ñ<11À.y$TªÛUf¼ãížxµþÁ­3ÿªÑoÃéß_®²ËwüñÃÑéŸöܧR/Œ>ñ:ï'V ~´ µé‹Õv?ÿq—¸Ò2–9½âïþb¨Êê]ŒÀWî¼9g]"tŒ71Gg¢ÄÍníçå9뼬¬¸ ?éìßŽè½ÆðªÕî}SŸ1‹nýÆø!¤ØË¬Ãb¼økŸµí×~¢¨Ú~æ›×Õ«Ïìh´Xz;/ÄäÕ,‹Æßã&;þ}ü×HS/ó˜$¯wáp¯ºè«WVª|ÄñbÑö0¢¡ŒÏów\V©ËõˆG7¹ÜµÝ ÄØˆŸ§ØÃrÕ"j¼q äÜHàŸoÝ‘xý!÷OL™ž,nã"0˜û‰Š("ÞKLÏVJk T}™­ždÜÄN*:ƃ›ÌQ¬3UU‰¨ßM+Uµ£Äqs2q¦t½üãIDÕðõ rUføˆ-|î5—öŒ#ª×d%HÌîÆÎ ±ú#‚±Ù~ûñ÷¸É®1ë»Ø/1 9‚¯ÖªÃ,š<÷–iež‹þñ¸¡¹ÿÇ¿vU“ñß­ì®T•NúxÜ›âh­?qd|6×ùyúè—?]•Q\ULúLñ-–ø««PÖ_HS»ÆÜ®ó…±DL×ÎÜ…– tzÃ4‹{g»:Ç–•ã Tâ&ø­—ž÷µU¿'šÖ®v[Æßýùç×~­ÚA muD±ùÌŽ.ó«ã#_úT9nqß2ÙMàj!-v\ó‡˜éÛ_µXmYÍÖ_O¾þƒ‰øªqÕÿ&Xíûô-¢Em.5 ðØ*0ñÐùNO!oë…ûüò6ÁÊoÞ¤ÄÄzÒ‹ˆ‚8RûŠ[®¯ñÛ°V)‰•ìt¬u";eó U.–³©Oz綘2 Ƨµ§>úê뉣·Ý½ajÔÓ%Ubs¥crªæŸž>çÞW‹µJ¿#ízl§ßu¤“¦¤Œ‡¼ñ ´Éôo”Òä”JÖHÓÖ·kù›`ÕÀ™]R{çE3ÀµÍ]عÀm÷Þ™XFë¯/-wÕy`ëý»F²tôßÖÉs6Ù¯³Æs·˜±Ç:çtÃÅÓ\Ò㵩OzKÛJ›¬ÆÒ²ÚÑoÜ|8¤­¾Omø¢KÿÈ×.eî…ñvI‹OçW`‚x7qí^»•oeqþƇîÑn­äÖµÀlG÷z>À5{§œ]k6`—}ñö¯%Vs¡ãò–ÅÀñ 5–\&ÖM2‰é¿­Ì0&{g›ZÍ9e?çD™ªÉâç0>³¡1÷ž¸ÀM}ª6¶IúØì§ê&«K‹«ýè½I]K`UøÈçÙIa-ÿª§°Œ²ã»7ÿŠôxàÛÊâüÚ²+ËoTLÇ{ˆµ9 €st±£5ê)ǰNÜß5¦/ºFŽטõ†ÄOìdßð©×®Jøöýw%¦œ%‹oÿÙdï죩MöεŠ/Y¼ÎŽî·ãÓõÏ-½üë쨙RyÑoŠ’49âMþ¹Ï¼:ªèw6bÑøUêÈye¶qcÖÊßX “­Î j(¡Aô{lÎ×äç[ܰ’.üJJ'Ñ [ïI]a÷‚˜õ¯õ‰o–cv'ò {6Óï¬Æ çøöŸMö6ù è®9…äaðlÛ•aðd?7Üð9zVô[ÈðV¥³¥ñ¹ÎÉŸÑï2ðøUšMtý¿÷Ùk÷‡u][ù7ˆÏZ<áŠûðXéÖ|¦M°êwGìØžxñÄwXITZ5YúÎñ›Á@“²Æwmúvöi+¤÷WnÒßø±4:Þ‡7j/õi±u‰ÛµµuG‹5¬b«˜gþäÙ:·V6Mi·zyr‹‰ŽÓ>)¶Ü¬]Üp£ßÄÑÛõFAµå]˜(ð'¿ô['?öøÄÄM’rãQÎ&XK1ÓkU£ â <ÞûMyjœ˜y¼²Ö³‰9t‘¬Ì_Ò.Zš?O3ÀõÍ×YVW?SWþ´Àõ;oJ$9tãBbJÉ+pÇýwGÝbš7nÁgÓ¼q`o<Ù‰‡%D¿Åº%V,önˆÙà˜4˜ÝÿŒ·4œâîÜofmÅõ.¾^ÕªÎÞ?ŒG“¾¢çÒÅ×l{óuS[³“è#Ympm:Y}ºJW¦ï£àž[\ihI\C 1„ða¬aÛú%±ßÕsÿÔJ÷ú«Ö¡~šû$Ž^psêòsˆ[‹Gîóˆ‡=dÃ#öÚoÃnYZá{~ð½»p_L ~ý»·‰»:êÊøvzÄžûÅÈ}º×Ƈî±Òÿ+wÞl~Ñ‘¿lÀõÇ@úcu¿¦õ•|eú†Ûf2—R§Qn ‡èt.O !À½‰ô-ôׯj‹ôn’8zýd÷ÞS*@€N¼Ü)¯ÌÛHßEÐkÀíˆË…Á ÄÔJDzÇöøæ-ˆýf𷨼r @€- €[Ä”UW7ß}GbÖ±¢)1¥d™@lõë>â £ÙYÇMþ¯ÞÅ&í7Ó¤b®%@€ Àõãý„Ä‹Ûç€Ä”’­*pË=;e6í{`bJÉ 0&ÙVÏÇtDóFņ«­³Ù¼Vr @€­€[a”I·ÿ|ëŽÄžó‘XŠd(G &~·ŸþßžwÔÓ6ì¶{óZ}ý®Ûþò3ú†ÏÍä@€cµgGÕ®¸‹‰Ä&Åûo‰)%#@€ÀÐb³«×>í7ÓY¿½±åÕiçþ‘èwè£Bý  @`°á1 ôUÐ[Ø4Œ&©%ĉš—¾ü­lvµˆ‡ŒñÒ¯-¯tˆK  @`àúýdwúvÕ¯¼íÞ;/òp"ÔÒd/ʯָÊ%ô"¯ûÆ>Ï'?öøVÖ3jišÇîXÕK¤'@ §À숣Wÿm½î•eÏï¼üo_ÿOïÊÙe @€~Àýú+½‚À­÷¤®‚~⩯¤»ìG§xj` (S`úÆNW­q´ØÆ«oûêIgÿv<[,³ÕjE€:7‚½ó{w7ºÞÅUn¿ï;‰ÉµïA‰)%[xñ“Ÿ/!@ ŽBßÙ~W/ûÛ?-§¥jB€² ìºeÛÖl…¯ Ø†$ñE¬ãÎ:s|ÍÏÜ¢8ßèEOzVJ¡±XúÄ¿zeJÊq§‰Y£SFëÔÏ~ï«k_;” ¿ôbMA£»^‹Ïi×èFÖ?¾øŠ­Û3yË'ßoO—zp¢°d+¦ðœ+ñKO\û 1~öÍ-¾è»X“蔿ùâǧ¼æ9qô €k^ @`–@¢›Tò'ßJ> )î © @`@q®oDh±ÃsÑïå7)æä§ýh$¨*t* nÄ{óÝw4ºÞž}ß]‰W<æá‡$¦q²»¾ïˆ[§iÆ!0{Ñ÷Ò—¿#ÎõM|§¦RÃã¹a¬ˆyÅßýE¥«$&@€cµgÇÙ®¯ÜysbÃݸ˜rÄÉî¸ÿ»#n¦º@LùÆ¡¾³íãü¶Ö››]ÅÛ7§œóZn+C®€x¸}7Åš§¿G½÷†=b^eŠFKÚ|߃ߟ¸€æ(P öó;ç´?œMùv±ÚyÖäO~ý qÊQúwfPªD€ºw¡ºJžÝÿ°L%½˜ô=ŠÀ£ÆŽ1§}oûôÓ¹&n¥ù² ý‰û#öÚ¯QI.^"¾ ÚkÀ3¶mWœ'ö"Pš@¼¡°cç7kÔJè[Í% @`& Î46ìöL%M ˜Ûî½3±•^žAÅ´y¬¼üæ/%ºIF€@3¶ÿ÷ô%-Q¥Ø=+¶¹²à9Oï(…F) e·Ž¼Q_¼ýk‰-ôðR¨Ø{6G¹à+—w´mb§HF€ÀR÷~þÂ8Ü(BßÓÎý#Û\¥pIC€ÖØu˶­tš\±u[Êå±b-žÙ§¤”&EàÒ—¿#ñÍ÷}áâ]»”<§™ÆkÒ-ö{ú;-š9« _üg)Ï•|ãUê—ø>﨧­zIÌîÖ/êW;Ù¨骉Go|"ŸÁ'‚O„àÁ'Bð‰|"ø|´Dÿˆ/®%ŒU°;+[Ë.úFJõÅvþëµ›vR{ë»\ÑøoK ÿÙ—ß4|Þ„Ï‘Ïaw~u7rÞ|î.ç³Üþa;SnÂ÷>È’æçB|Îý¶Àg˜Èg€Ï啿ÇQ-±§§Ýó/ÝöÌæeéÎoJ5•å?‡¦¹ÛÕìu|žŽve‚i;¢úª¢ÎÔ5Çv»¾í­-º³°ó*=ÿß”ò¯ÚÝÑΧÏ;]¤½në¼<ŸÒýÖ´÷Ýè_d*›ß$è}eÒB±º‹ôÈ´GÏŸ^û£k%wÊ~E¼u¼h„ÔEFjk+ ªÜ.hcÞ^qœíÕt? ¿Ùø¼ècÞ øŒùìm¤â3D: mS.i©ðœý4´X>Ã%D¥]Á§>)µûŸ!{U;DÒ·™þ Ÿ#|6†n€Oe Ï¿/Æç.2QÓˆŠMÚÕ|žÛYs™ö½+åõ2|Úóàs–ý ^û®ìgtâ¢öSOÍL¶x£|¶=Â1>m —Æ~ú\>Û?Íùæ¸}øL8Ó  ñ©úŸõ?gð¹sùL,Þn´}é†|m¤}Ïó¹Ë~+vƒ|ª>*|Nçs`ü¢>@^;p¹Ð~zã÷þQß»ñôñ{;TéË»|mfPmÇùÁãÓ¿÷WÕ½ŠîEu©à´­‡ÏEˆ¾^»pûŠvË^i‚Ï[ày>'¬-Â'Bð‰à!øD>|"Ÿ>‚O„àÁ'Bð‰|"øD>|"ŸÁ'‚O„àÁ'Bð‰|"øD>|"Ÿ-Ëç?Å[ˆàÁ'|"øD>|Â'‚OŸð‰à!øDð Ÿhc|¾uùÅß–*4pv¯ùõzçë*ÞpdX5Ÿ³> ;ð™¯bz½ š)¡ð¹i>G?³á—¸ÛñÙâóæß1ø„ϱ³gwà>oÅç@#Ÿ[ãóø³J¼õOšÑS<Êx‹_ŽÏ{3Ïì ñ5B\™)æâ¡«i~ö÷äÕà´êújoѹ!®ø¦ÐCøTL…èXH~¦|šóü4×!);ÐOtnaàÀÑ´@ÇöП‚VÀgpøŒ?£ÈbY>•!˾ðæP˜^ÂãÓTã`íÔ`*ºõ·\Eð¹>s¿\ÂgBBÖ2|*»ªŒ²Ï§ê'¸|ø„Ï¥ø|›Åç›ÛuN³½Sô,|޵ïKñ© hŽO3¿4ܾÕÐfù|{ŸêH¾=ººOìçVø¿ûãþâ0Èçåãw;ÇàÖàÎ?øãwÿŽáóá|z“™Ý´gT&ÿl:‚ÎTa|z2q™2äÎ^š¢ÙjÜ› !çoÁÿTÛ?=Î~ný}øOŸ3àOøD>|Â'‚O„àÁ'Bð‰àSñùÿù_þó#4A÷ç¡5ÛO„àÁ'Bð‰|"øD>‚OŸÁ'z>>>N?¾”>Ñmþ ¨À'ZŸ PO´2>µ…O´>>?୘ϟ>‚OôT|š &øDkâÓLÐÃ'Zžzýˆñ;zÁ'‚O„àÁ'zjÉ…Çáuú˜ùZ˘´?Æ~F¢`4ÇÏ处\‰jw*†OøóÅLÿ“DÑ+£ò)ô’À­+†Ï×E3-ðáãñ%úQžÊ¼ZúÄàWei…Oø\†O§}W|êæ=$|6¯Ãç“x^9-Å4k2ýÁócóôL`îóõô#)ÝU0]ͧÁN·ï’ö!àó)ð -œíjöG¼¨Ý½Ü¾h|†>ÜÒÞ‚¸Ç§øã"õ(Á!Eg0>z.<5_Êe­5š!Xrk†A3õw³|ž0ºšOIb?Ÿ—϶Eÿˆé1>U鮇$ŧ!0åSÂ(Ÿ¶³)ô?ŸšÏä—)|6í mirŸ2§ÀçÓú¡šå¾§™ö? ŸýðÉá:$ôyóŸ|>EŸ7<> ŒžÐ5æé‡à ±vüÁߣҦÆt]Çþ:ÂgT> Å¡âL Â'ºR«[y‡O´~Á'‚O„à]-ü?‘§~Î(M£© Íy­e ÿOt%ŸC¨áÿ‰Èçfø"øÄÿ?>ÚUÈÈɳi×ÏÆ•3àÿ‰îÆçG´Tú%uÓÿŒÝAðÿD÷ãÓ0CØÿKkü?Ñ ùÄÿ>WÌ'þŸðù0>'ö?ñÿDá3¿Çãü?á%œM>~'Á'Z³àÁ'Bð‰à¡¬Ä›À‡OŸMDtQÁ'‚O´E[o€h¡+´)J?: EZ=Ñ«¦¬¡åñ Ö+à#>ÔcÚòÙÐÙƒé6Á'Z Ï–ÆfÍßõêù ÆÝ¤±¤ø/¡ÛòÙÿ]ðÉÇ(ŸÇyÀ'ºŸºHŽÏŸèÆ€:žÎ6EižOÝ …O´, *G®‰ 6²É᳋ºcüŽž\ð‰à!øDð‰|>¿dÆ+w¹ü¥÷!·½mø„Oø|!}L+&Ó~yà—Ffœ³üW >á>_‡Î~_×èwÉ£Ë'QF÷>ï†D‰Þƒ~ì>ö.lsb[í¹bUYïi<5£ ˆ~lኗÑãËçvuQ}ð¹*BC°)¿œLH"&VôDçy5Ž¿¡/у¡ˆÄTÖygOÍÔOÓæNrªWîsT5’OéŸkâ38|ŧ—[3¤Ÿ¬ä“ŠM—ì_ Ãÿ<ðu}1®núyÑ7“x~ÂçJùüˆZô»iLdzâíTâvÑÍn›Þk2Ÿqesù”tÛQÙÂ$çèôð¹6>?bP­]uð牱²Þ–&õ¦æe²ýT[Í{ìŒdîCÆìg€ÏUói]Ñüþ§h’LÿSÄ‡ÑØ´LòûéA7´å$·¼[…Ï5ú¡wÔøã÷ˆ8éC#DB2Ê’Œ“MêMé‡FQeã|FçÙñ{H/ßõ횎KÿM‚Ï5ÛÒÉ3¢%³47žÂßw+-ñ ÁçMÚNs.ž`æN7vÓ¦¹—úÂ'Z³àÁ'Bð‰à!øD>|"Ÿ>‚O„àÁ'Bð‰às‚¤Õ%ù$Ä}:x̘æü%S_Úµê V¬ŠÆÅw—¿ƒ“ÿìyÕ¾6Ÿý[±i>S´AÛ%%às+|^ö¶ÍâsöÕO›Îç.-²›\ñl>çºÈÃç‹ñéÙJø\?ŸQòQáÒ¡?,}cg…t=Ù4É‹ÚhǤT‰/©b!UNÑ¥L\Dkw]ë½kŽíúà —]ñó“]ÿ´=õUT61Ÿ:KLˆB1“$4ý/^&œ.>ÓàW“CAèdo‰#kl|lR\ü$-ö’-Ÿ&NV…hÛL,‹-hé?ÃgrÎΜޗ÷RÙÄßäîdB&ò<:â$\X,Ì÷™ì§âÓô-ŸœMp¡$—2)UlöÛ”Ïà°ÖZÁ Quû1Ò-ŸÁš^ÿÍ'ÇMü~DY“Ì»(ÞÎkðéò™´ïÃ|F \BÚä3rSª¨Kv_›2F§¸ðÚ÷žÏ5¥ Ÿqëíð¹S¯¥oÆ8ŸbÞª ŸN&øâÓ³Z#ö3xö3-ãÛÏÌÇføôÛwm™SûÂ]fˆ‹äí§²·r¡ýœÊ§×Àg–ÏLg0P†úŽ!—l0éæ>é4'ËeýÏxŒ¤í :´³ýO—ÝÜ_'â}7³ß¨´)^&ø²Ÿ¦}×# wüžO²¡Æ½›dÕÔáä°R ezÆGÆï1]zÁ¨“ïº~À.umSÈ{3$¿“Ö|’ñ{èòÒ&“]Úš­2ÊúûDíx às½t‚'|"Ÿ>‚OŸÁ'Bð‰à!øDð‰|"tG>ßÑd|Â'|Â'|Âçöµ¯ŠÓá€Oø\žÅgUUõççgŸð¹*•UõyR]Weq€Oø\•ª/.˺ÞÇ>WFé;|ÂçzùüÄ~>ŸõIÅr€œt˜{zq唟ø|*>OcŠráúŠù|Uûk._|–Åq¤T1¿Ÿ·àó8¿Ê˜ç!|{ÀŸKóùy• -‹¯¹¥Ãg Ÿð¹<ŸçÓ¯ïWŸðùª|Š5õ ¿nýg~3_ÁçVølÖUfËU^W]=t¯}©™Sû¯áýOø¼ Ÿ‡¨Ü¼>ÇéæêŠõMø¼1ŸŸ³fQ÷Å!]|.Ég¡‹^3lÛÃ'|.ͧ­zæBTQ5 ñÀ÷‚|Õuº€ÏÏz– -ë÷²„ÏðY”jìÔP‘Ù‹éïK|•æMÖ7ßàÛŸƒZz~~q>§OÖï‹}÷¬>á>—¿›™&´<õDŠªî»%ÀŸK*7¤šdB‹’åɾ:ûÙ|.¨}vŒVM:»Œl)|nŠÏÜ »ùø3Ê7¬ûrŽFnò00+5Á„FŸm)|n‡ÏYSKû9(Í\z»Éq_×Ñí1¿ŸKó9<ó?ê2Çí϶øàón|ŽšÐ²»ßCqö>ø\ÏÑ•®aZ6K]ûºí±|Þ“ÏaÚ ŠŽÕTÅ9Ðø6ÆçäñûD>'/ºOⳞÀx5tOe7N*ás‹|f(3ÅGùœzùr1>fššAÑÑ~‡>ása>÷» YZŸøC÷'|.ÇçÔ>mÖ‚3à¾uº>ø\ŽÏic¸!Ÿåƒq¾ñyØ5|ÚÃkå³¾Ìø6ÆçÒóK‹ò98R—:+|ÞÏYE€>—ãsxz~NÎàƒÏåø¬á>7Ëg ŸðùH>Ç¦ç ø|z>3ùæ*{¸\!Ÿ|>=Ÿ™6uóó£&þŸðyŸSÝ—ê%ø¬à>/ãó2Õlì¼î0|Âçãù¬à>ÆgÕ/²ü©¦zŸÏÍçÔü`Õø¬£EöÌRR ŸÏÍçÒóŸ‹óÙ$TÌÌ5Õø‡Àç$>÷³öï¼ÅÓ‹ºŸNl×:Ì.Üø²4v-nßËùŽ PàÛ Ÿe<ÛÝ­ov4\†¶¯,5j|Ô¯XÎÚ£&c@ðùÄ|î54ýú{o®&·ñ!îvôø½ú¼¦/¯6 À·1>÷ýg~¶i‘Hè4Ú“­;šùϾHuùœýþj |›âs_ÖOå¿uøÆ 茗ÅíúQd/ßL³¼vŽø6Äg·sUÜ#TþuE\`p_âÊ4¸Éúf<ʹ4ÃÂÕ^"À· >÷‡¢¬TcYéþbk‘´ße]•j7õ¹²Îš³tý]ÕYWÅ%X×PàÛŸé‡ÙýÒá}½ª/ïvÂçÓð¹~m·óò`=ø„ÏûЪ˜u*ðÁçÍUÎð,Oø¼›ö³Ï>ø\³€o;|Ž¥—‰ì•ë´¤«'O“ï‹bÿþ ßvø¬';ž×©OȾÒóôåôyöb^ºø|1>«ù|­e¥'ñ—å³(Ë~οÛ|øëYY¼§Ïáóyø¬;}Í!ö*.âó+±Ègóg¯®ä³Ò>Ò:ýH•<‡Ïgâóóò,iû~$¨¨ËŸåu|–ú–ZÓÜFǕџQÁç“ñ©R"Gi’ÏMhYU“øÜ77ÍèY¶û3T}#\4ãsò^©;Ç»:Js²úûóRýI~ÚçãS™©2%±öùLò}ç:UcÔÊú«öc3ÜÅ{”}š›bľ±+O™ Šö.N±!es•ÃåÎOÀ·> ‡ÏÊýÄëé.ëekÓŠ3VUo㪞Ïãðê0v_çé…ªké÷皊ÿõ ÞŠüÉÏÍgeø#é|ªÛxäé«ÓXu”¢ËdWл^µ|7UšKžÎvþDrÑôNÊrzZÀç ò9-w|iR…ÔçvÙv+»ëÖþqÍ}MdqÚj®ˆn×òY5A£õç¡éßvøGëw5»î]=m_Í/>¢yª}Ýæ;uA»Ôɧþg“ÏÁkæ¿@.¾òîÔªßÚÛÏ. ®ìø<¾ÔñYÃç³ðY¿t@÷ “ø<Ñ¡‚ßgYöíÍø¨ «K× §óK}ÿ³åó\eõ• òxìp8>-NOáóøÜ{|F~÷G; Õg†6ÕÚG|v'¥AzµšÛ,ãñûÉ\îãnubzZ­Š߯ùÜǵ#ï>÷M~›ÓTP¦SõL µGŽ•*=Í_ÚùφÏâ³ö å©}ÿ²Ÿ´ï[æslý¨j>þh$>Äç>JäÔ4Ù^"±"ZUÒóŸMl}¥Ê» Ý<²~Ô?43øÇö}Êg—¥þ«©>Á宋—ñ±d~þxn¼TÅóKÑ×Ü«ó¸þLkÑYÛŠñûsð­ÕñçÁt@‹ºzç³êRÐUm™"™5?å«Þ³|Kìu‡÷hù rúšÕj: Uk‘ÕüRiX‡Ï­ñéÕµI<¯Ÿ¿Oà³è’ëê3¿—Íè¨~’0•šÏº>Áçöù,ü¹˜ô.gòY¹3ö£þɵ?ÿ¹×>Ç|–oS|ºË=Ud™-\ò¹wÓÚòyðך¯Iõ®ù,›kTÕ¡üÚÂvÐãø6Äçû¢a¾ûýr%‹*ò>tñÈ_ú Cþº'æóõ|ð Ÿð Ÿð Ÿð Ÿð Ÿð‰à>á>‚OŸÁ'Bð‰à!øDðÉ[€à!øDð‰|"Ÿ>‚OŸÁ'Bð‰à!øDðéK:ñ6¢5òÉÛ‡¶Ë'ü"øDð ŸhÛ|vänÀÔ>iŸþ¥eºŸ¢ Ô4¦?;p²-ϧyÖÈò©ÊB&ºŸ‘%šo˧S–OÝOýl˜O]ˆ¢;ñÙµÙÉOIø4eºŸfÌ#þÈ(î¥2>BwäÓÎIw0´sJ}yæ—Ð=øD>|"ŸÁ'‚O„à!øDð‰|"øD>‚OŸÁ'‚O„à!øDð‰|"ø¼¡dÖËr³ †.ø4è0¿& o‰[@ð9ŸOQO"*£Ü<EÃçgÉŒ³4Ÿg*»ðù$|þéߟƒÏ•ѳ–ZøÜ2Ÿß~øñ‡ßèO]%i³3‰?'½ QÊÑg¶¯Å9J¼ŠCŸ©,Êêxþ-)Ö^*.åñ)>Ÿ„Ïï??åŸb ÚBÔÑõç4qµ¨m“*¦x\QZ±©5ØI±5|Fã#ø|>þÅñóüñge?—Ý'[§ÞL)>ÖDŸ$^E2c´Xp:›9û©/†6ɧc?ø”¨éÌg|’xõåÕäÐ|>µá†Ï óyêþ6LäSq‰ýô*†Ï¤}¿ŒOIH] mÏð§?þsÈðéö?'¶ïºÿ™Ç78ç¹§Œõ?%%>Ÿ€O+ÍD:~—0Í~ªñ»8“)ÑeB2~W8Jtý¶2wýȬ$¡'às9Ê|Â'‚O„àÁ'Bð‰à!øDè)ùd‚ >WÌ \ˆ¬:|Â'‚Ïì+“ùø„ϲ¡Jð‰VÄçï~ùÇ_þÞ¢…{D8©ß#—¢ÞŸÎÄÅ~N*©÷„Švª ¦Ÿáûßüqµw¼EƒfaŸ'ñDb=â$~ÉúH›|†ðóÏížOm'CΈZ>•ÿfâ|œºÜ» Ÿ±¾ÿ*Xû)I0‘Ĉ##>ÅF«ˆaŒ¤‚ŸB¼S=-<|Æúݯÿð눈‰?3†1¶¯žý šÏ\û"æB>#eB•"”lóì„#™¾¦>&: Éô?E<š|úŠ›Þþ@’:®Ÿ‡d¬®_×áGÝY]»¼q<‚ÏKEð¹B:!>‚O„àÁ'Bð‰|"ø¼VÌ1ÁçFº-‘IÏ=áŒxÉjÚ)ÒÊ=ab½º²Ñïfî«:|ðy/@§nK$NÚ‘Œ$÷chï#ÉÞÕÄzŠfñ9tðy_>'lKä$¸=Ü´÷‘dïjb½N±Ù|fª†Ï+4q«$I>Æám‰’âcg$™”§í}$Ù»šXï¢|úUÃç|%ñGA;!™íˆ&oK”le0v† |Ø{Iö„‰õ:™ ¡’¬ÏihÖpÕð9[nüQÚ$ºŸÃà¶D.ŸCgÄ[|]°÷‘dïj˜ÏñQ!èeîùíF-§$þh(Ö(oÚÒm_ÌV ãg µÃ{-o?“'&Ë ÍÂ~ÞÌ~Úø£8ÈëíOÝ–¨·yÏï^Àç5ýO»!”ÇJC³Fî>¯?²m^pùß–Èò9ñŒñ{p÷–Y~ü.žE5á-Þó|Õðy…¼ø£LL‡~yx["½¥Ì¤Œº-i.ÙûèóŸîĘVÅã“ùÏ;Hm4,bZÔ±m‰2㨠g4W–pÉ)aÒ‚Ó”u)Ñ/&èK²÷ÓèûŸ·uŽ!™—Yþ¹Ùå9>¯ôrCðJx¾Dhþu>‚OŸÁ'Bð‰à¡Íð)ÓKê†ñ mh#Óþ6Ÿð Ÿ÷Ä`5ðù¤|N *Z„Ïf‡ÏçäóÛ?þðËPhý¿¥O¹­|râZÔ«M„yûš ¬Ï Æ%(è<ÉbwL þ Æ*)³9Ž6Áç÷ŸŽØOßm‘š$¼õ—èUI÷àÊoT¤ÎÒa:âî¨`ýÛ37hÝ‹ ‚ÏÍðùó/Žtü˜ùÁ]âmÑa7ÍÒ ò‚ÝêÀ:…Šçb.7¨N“Ô˜ ä·ŸAr±ˆ}K.*˜#¥ qÚÕŽ½2‰OÑûÆçÙ¨ž˜Oé<¥õ™ãh|žúŸ¿u,Ûˆý y>ÅP)ƒsUÃöSÿâà¹ö3dì';8lŽOT$>%1æ3Ù iŸ*ÈÒëö ²» bæívIbø¥ÿ¹%>Sjüö݉© Îø=èýŽ†Ì•˜ éø]BvüžnL›œ–Œßõ&Kh“|æXZþ\0ÏÇó™oMá>‚O„àÁ'Bð‰à!øDèÞ|ÞÀ…X9wôW™¶×‚Ϙ¥åùˆvQ½Cq˜ºq‚OË’Ü Î4Eì´‹Ð óiãnA†·s¡×å3‰? ‰ÿ°õ(ÊD©­€ÚªLŠã>6.B/Ëgê?¯Hs½ýÈ 'zNç‡Ïó9¶qzY>“ø£-[#qÚ% íS1wã"ô²|:ñGš‡O72h:ŸrñÆEèeùLãÆùtKOâ3îd^°qz]>“M‚ÙX2ÛÍõ?uMz6ÕÙŒeÂÆEè…ùL/ìäÆïNdP²ä¦Qñ@C!øD>|"ŸÁ'‚O„àÁ'Bð‰ÐùTKMñcþŒøT1©ë‚›fîŠÜOá‚J$ÿ'ŠŒ —”€Ï;áÙþH2ÅNà³û%¥5^õq/§y>7Ãgâê4aí\2'«S%ÍÂ<óÞ®7‘ëDÏ€EøÜ äçÓ„*™­æòé8-‡pµ#=|¾Ÿ¿ûåù{ÿ-ôùŒÂ6ÒÜÅ.Ÿ&øÉð™xJ9û3©'ÉoÜôÞ Aq߸»Ï¶No×&Él¯dï±{nÞšøoŒ®«ÝÌž‘Ïï{üñwß³|ŠÝ^Èf£×¹ß‡f=VÊí+#ƒÛ¥^z¹›‚@e3›«ûŒ¿Av×&ukú+¥ïQ$Ÿ¨?5ÓÛr5|0Ÿ?ÿÜþ;6Ý»ó†§ÆUü4õâÚOñÀ–6½™à>ÉýL7Eì}f*n¯Ùþ)ÞÒé28[ElG¶Ÿ¿ÊØÏlWO˜ðéU“š.ä3quÎmÜüö=g?s|ƱX—ðiÞý¥…ÏYúݯÿðë°¶1äÇG2l?£18~ŸÂ§ŒÚÏàá–l#âÜgÖ~Œ3|Ê ý ð9K#¡JPù½‡b£&ÍJ†K·‹›é:fIùLwSÌð™ÝniŸ’ëzPøœ¥áõ#µ’øãw{ÖÐú‘é”ìÏÔçßÅÔ—ißí.ŒÑ7É0œÙnI†ùŒÊ¥÷$ñ·¶P­žSÖß¾(K–½zÝjúÉׯáÂç“ñ9¡¨\;ï(|Š%QøDð‰|"øD>‚OŸ=Ÿ‘æ˜uÑÈÔŠ&)Fæ;øä3^{”éÁ'|ÞÏð9ɹAº ŸÞVI9×ø„Ï{ËÆ)>EÇÒ¤¾GÁÙ&) 2‘?ÆHÇV4îs©‡š êñC†šK¹qD›ŠûÏ“’ø£„ÏޗΉÙ0¡=’uӌضQ¢ÔÇ~q öÎs­uniÈPˆ#"ÅøùLâÔ âÄeƒ}$‰ò#+l$“ˆS‡SÌ¿¶ çà3¹½œýüUj?=>}ÏïY|ªvVÚ0 ˧èç»§ÚŸ^üÑ Ÿ^°ùŸNäŒñæñܵð\>™¯Äg&þhhCΨ¨íú與¿Á̉O’a>%¤):=¡ÿ¹i>¹|ªCã÷Ž|äjgã ’ 5ħòÑ3IíûÓðy9Ê“JÉ=®}›KÁçSóy}äÏôk F>|"ŸÁ'‚O„àÁ'B¯Æ§ŸÎxü4æás½|'º/Ÿ—±Ÿè6|:AEð‰ÖÂç·~üá7š4pXt8Ï`‘ ØíÇdBFðéëûOGD~RNñɆAz˜|‘ ØíÇó–Gð™êç_iúQ%|†ìTáøÁ§ní¹Ý~œ­¯|zrìç4>Ÿ÷òém„Bº#‚O£¯þçoƒÏ§¡cÓÙLáÓÝ)`9ás‚lP‘ÝÈòét!ÃPX¤!]–½4ŸVj»ÔtÓ>;W Û° t·Ÿ v""Î>¯$•ôph¥|º:ð‰ÖÂ'Bð‰à!øDð‰|"ô²|ʆj½}ÝðyÓÏÂfzŸz;ù/WÞ¥É+.ÖÃ4va5‡&¬aèFùÔœ2™O»ƒ—\ɧã îÏ,ÅŠÿ€®ŸÏ‹ÐÐVP¦|ŒÆª-ȧxwß—x‡¦9¡Âç£ø´ñGóùœ¶_œøä\ǧÍy¯N—à@ħÔ‡ nY4ȧÓý“)|:[Ù;轡DrÁк¹¿€ÏÞÓÊ$R†Ï‡ðéúÏGÛ å¶,JiH‚9’l󩟳Ãg²EQr]pSB{ðwJ¸€ÏÞSU¿ðù >Ýø#±&-¸cf€7öÌi¼“Ø# _hñËf¶RHïm.Ÿž§?|®Ê~vvT²mzʧ &Ò§:ó Ÿæ4ŸO³9R Ñ|F‘Ñè|ºñGºåÌíGäaaì×@®œ¸Oàì7ãXE0ÔÎ&Ëð©~G÷çÓ‹?ò62òí§³ùPʇ»½ÑŸÙ]åT }²9’³#×¥óŸ¹þ'x>ŒO+q7IR4˜Å˜t»â4Qˆ„ü8ÛÂ/~ž‘~½'ݤHúE£xã$ó(axýÈ¿ƒçzø¼1û¾ÌÔ.ˆ|E>7'|Â'‚O„à!øDð‰|"øÜ–G‚Ï›BÖ3dzjºÛ…#É´˜#‘žŸOöó¢Ôž7 Gò\<GzI>Åô©|Þ1IGÚ ŸÇ ó©ž=4)ÜO½Ÿiü‘qR¡?½Rš=yˆÏ5„#Íá“p¤ó™øÏ—K㔣š¡ oœwû‘d~‘ÖÏg” ÌL½ó|æàïŽt­ý$iuö3 :g“™>ýñÑ#‘Žôh>Óø#ÇV]ϧg}>— GºŸêwt>mü‘áKo]”¥o¤Äã‘®›ÿ$éñ|&¶ÓD ‰FÒ¶šãa`k£G„#©Æ˜p¤gáóŽ" >áHð‰|"øD>|"ŸÁ'‚O„àÁ'Bð‰|"øD>|"ŸÁ'‚O„àÁ'Bð‰|"øD>|"ŸÁ'‚O„àÁ'BOÂç¿„½þ¿ÿý÷Õü÷—õh=oÊñì'Â~ýoº1(ØO´fa?ÑÍ…ýD‹ëÛ·o+¸ ì'òñlþÍÆ×àóÛ©­?ÿ<;ÿ¢žõ/}Ó¥¯û‰Æøüvf´yêѽôu¼;çzP°ŸhŸq-}=‡ßâ—b>û‰®çó[Ó¦÷|ªöû‰nÍç·1û4ŸýéØOt@¿… øtÚwúŸèv€öí³¿'|Æ/=¤¿ì'º¹°ŸhÍÂ~¢5 û‰Ö,ì'Z³°ŸhÍÂ~¢5ëAöó/¼óèÆ À'‚OŸð‰àsËþ#ºR·`èïá³åóïÑUúkÿ^þu)ý#|ö|Òâ]%ø„Oø„OŸð Ÿð©$ð Ÿ[áSàsµ|¾Á§'ø„Ïò™Åóoàó¦|¾½½õoýz°OÍ‹o/çÕ‚OŸÁ'Bð‰à!øDð‰|"Ÿ>‚OŸÁ'Bð‰à!øDð‰|"Ÿ>‚OŸÁ'Bð‰à!øDð‰|"Ÿ>‚OŸÁ'Bð‰à!øD>|"Ÿ>‚O„àÁ'Bð‰à¡‡èÿïžÎΙIEND®B`‚è @é(€àx( òD/È 0ÒÕ|·DArialr New0ÒT©T©ä+Ü–OÅ 0Ü–Õ·D-ÿ3ÿ 0ÿ´0·0Ã0¯0ew0ÒT©T©ä+Ü–OÅ 0Ü–Õ€2 ·DHGSuRñ‚Ò‰ŽÿŸÿoÿŒÿŸÿSO0ÒT©T©ä+Ü–OÅ 0Ü–Õ€20·D-ÿ3ÿ 0ÿfgŸÿoÿŒÿŸÿSO0ÒT©T©ä+Ü–OÅ 0Ü–Õ€@·DCourier New0ÒT©T©ä+Ü–OÅ 0Ü–Õ1¤€@€¥ .©  @£nÿý?" dÿd@ÿÿï€ÿÿ  @@``€€  ððàh[¥$ -9 5Q:'L,-/01235U7} :;Í=>?;@A4D8FGJKL-;MNOPQRS0U¤VWYZOð°bð$‡^‹ åÌÒÄ9û–ò26zëÿŠºð$ÿð$ÿbð$k®+,ÑJÉÒ)w8Øþýÿ8)Šºƒ ð0ƒ†A¿ÀÅAÿ€ñ ÿÿfÿf™ÿ™™™Ìÿ333ÿìÝêêê@ñ÷ðó€Ð3硎 Êš;…•ŸÊš;úgþý4dddd˜8ô–OÅ 0¦ÿÿÿ8þÿÿpûppû@ <ý4dddd —v‡ 0T©À+ÿ ˆ8Š0º___PPT10‹ pµpµðú7óŸ¨Introduction to Tokyo Products¡IŸª ó%Ÿ¨Tokyo ProductsŸ¨ùTokyo Cabinet database library Tokyo Tyrant database server Tokyo Dystopia full-text search engine Tokyo Promenade content management system open source released under LGPL powerful, portable, practical written in the standard C, optimized to POSIX¡PP PPPPPP PPP.P   € €   €   € .€ó'Ÿ¨"Tokyo Cabinet - database library -¡#Ÿª ó#Ÿ¨FeaturesŸ¨\modern implementation of DBM key/value database e.g.) DBM, NDBM, GDBM, TDB, CDB, Berkeley DB simple library = process embedded Successor of QDBM C99 and POSIX compatible, using Pthread, mmap, etc... Win32 porting was abolished (inherited to KC) high performance insert: 0.4 sec/1M records (2,500,000 qps) search: 0.33 sec/1M records (3,000,000 qps)¡Š-4dW€-€4€d € W€ªP±n)ó$Ÿª Ÿ¨’high concurrency multi-thread safe read/write locking by records high scalability hash and B+tree structure = O(1) and O(log N) no actual limit size of a database file (to 8 exabytes) transaction write ahead logging and shadow paging ACID properties various APIs on-memory list/hash/tree file hash/B+tree/array/table script language bindings Perl, Ruby, Java, Lua, Python, PHP, Haskell, Erlang, etc...¡ìP0PPfP P7P P6PP<P$0$f€ $7€  $6 € $<€ªt[2u8 ó(Ÿ¨TCHDB: Hash DatabaseŸ¨static hashing O(1) time complexity separate chaining binary search tree balances by the second hash free block pool best fit allocation dynamic defragmentation combines mmap and pwrite/pread saves calling system calls compression deflate(gzip)/bzip2/custom¡ìZZZ/ZZ,ZZZ ZZ  /€ ,€   €   €ª,ª Có)Ÿ¨TCBDB: B+ Tree DatabaseŸ¨âB+ tree O(log N) time complexity page caching LRU removing speculative search stands on hash DB records pages in hash DB succeeds time and space efficiency custom comparison function prefix/range matching cursor jump/next/prev¡ìPP P PP<PPPPP   € <€   €  €ª,Ñó*Ÿ¨TCFDB: Fixed-length Database¡-Ÿ¨Åarray of fixed-length elements O(1) time complexity natural number keys addresses records by multiple of key most effective bulk load by mmap no key storage per record extremely fast and concurrent¡TZNZZJZNJ€ª‰9óA/Ÿ¨TCTDB: Table DatabaseŸ¨acolumn based the primary key and named columns stands on hash DB flexible structure no data scheme, no data type various structure for each record query mechanism various operators matching column values lexical/decimal orders by column values column indexes implemented with B+ tree typed as string/number inverted index of token/q-gram query optimizer¡Ô P4PP?PPQPP_P 4?€9€  €  _€ó&Ÿ¨On-memory StructuresŸ¨@TCXSTR: extensible string concatenation, formatted allocation TCLIST: array list (dequeue) random access by index push/pop, unshift/shift, insert/remove TCMAP: map of hash table insert/remove/search iterator by order of insertion TCTREE: map of ordered tree insert/remove/search iterator by order of comparison function ¡¼Z$ZZ>ZZ4ZZ?Z$$$>€$4€  $?€ªPR#DH"ó+Ÿ¨Other MechanismsŸ¨Uabstract database common interface of 6 schema on-memory hash, on-memory tree file hash, file B+tree, file array, file table decides the concrete scheme in runtime remote database network interface of the abstract database yes, it's Tokyo Tyrant! miscellaneous utilities string processing, filesystem operation memory pool, encoding/decoding¡®N'CG$€N€'€$C €   $G € ª,^¾ *ó=+Ÿ¨ Example CodeŸª ó,Ÿ¨ Tokyo Tyrant - database server -¡! Ÿª ó-Ÿ¨FeaturesŸ¨Dnetwork server of Tokyo Cabinet client/server model multi applications can access one database effective binary protocol compatible protocols supports memcached protocol and HTTP available from most popular languages high concurrency/performance resolves "c10k" with epoll/kqueue/eventports 17.2 sec/1M queries (58,000 qps)¡€ ZYZZLZZNZ YL€N € ª>— ló.Ÿª Ÿ¨’high availability hot backup and update log asynchronous replication between servers various database schema using the abstract database API of Tokyo Cabinet effective operations no-reply updating, multi-record retrieval atomic increment Lua extension defines arbitrary database operations atomic operation by record locking pure script language interfaces Perl, Ruby, Java, Python, PHP, Erlang, etc...¡ìZCZZ1ZZ;ZZIZ Z.Z$C$1€$;€  $I €  $.€ª,î“ ó2"Ÿ¨Asynchronous Replicationó/Ÿ¨Thread Pool ModelŸª ó;)Ÿ¨ Lua Extentionª$ Ÿ¨Îdefines DB operations as Lua functions clients send the function name and record data the server returns the return value of the function options about atomicity no locking / record locking / global locking¡\'ZcZZ-Z' c -€ª³ó0 Ÿ¨case: Timestamp DB at mixi.jpªŸ¨Õ20 million records each record size is 20 bytes more than 10,000 updates per sec. keeps 10,000 connections dual master replication each server is only one memcached compatible protocol reuses existing Perl clients¡¬" " € €  €ª› 2ó@.Ÿ¨case: Cache for Big StoragesŸ¨-works as proxy mediates insert/search write through, read through Lua extension atomic operation by record locking uses LuaSocket to access the storage proper DB scheme TCMDB: for generic cache TCNDB: for biased access TCHDB: for large records such as image TCFDB: for small records such as timestamp ¡¤Z3ZZHZZ„ZZ 3 H€ „€ ª,B3 ­ó>,Ÿ¨ Example CodeŸª ó5$Ÿ¨*Tokyo Dystopia - full-text search engine -¡+Ÿª ó3#Ÿ¨FeaturesŸ¨Ffull-text search engine manages databases of Tokyo Cabinet as an inverted index combines two tokenizers character N-gram (bi-gram) method perfect recall ratio simple word by outer language processor high accuracy and high performance high performance/scalability handles more than 10 million documents searches in milliseconds¡Â8"(#@$8$"€€( € #€$@€ª] àó6&Ÿª Ÿ¨Eoptimized to professional use layered architecture of APIs no embedded scoring system to combine outer scoring system no text filter, no crawler, no language processor convenient utilities multilingualism with Unicode set operations phrase matching, prefix matching, suffix matching, and token matching command line utilities¡v8 2‰8€ €2€‰€ó<*Ÿ¨Inverted IndexŸ¨4stands on key/value database key = token N-gram or simple word value = occurrence data (posting list) list of pairs of document number and offset in the document uses B+ tree database reduces write operations into the disk device enables common prefix search for tokens delta encoding and deflate compression¡¦P PP'P<PP}P$ €€'€< € $}€ó7%Ÿ¨Layered ArchitectureŸ¨±character N-gram index "q-gram index" (only index), and "indexed database" uses embedded tokenizer word index "word index" (only index), and "tagged index" uses outer tokenizer ¡TL D$L $D€ª4Y E ó8'Ÿ¨case: friend search at mixi.jpªŸ¨920 million records each record size is 1K bytes name and self introduction more than 100 qps attribute narrowing gender, address, birthday multiple sort orders distributed processing more than 10 servers indexer, searchers, merger ranking by social graph the merger scores the result by following the friend links¡¼Z8Z&Z/ZZ0ZZ;Z8&/€0€  ;€ªYÞó?-Ÿ¨ Example CodeŸª óB0Ÿ¨-Tokyo Promenade - content management system -¡.Ÿª óC1Ÿ¨FeaturesŸ¨content management system manages Web contents easily with a browser available as BBS, Blog, and Wiki simple and logical interface aims at conciseness like LaTeX optimized for text browsers such as w3m and Lynx complying with XHTML 1.0 and considering WCAG 1.0 high performance/throughput implemented in pure C uses Tokyo Cabinet and supports FastCGI 0.836ms/view (more than 1,000 qps)¡€L‚a$L$‚€$a € ª>œ¶óD3Ÿª Ÿ¨¥sufficient functionality simple Wiki formatting file uploader and manager user authentication by the login form guest comment authorization by a riddle supports the sidebar navigation full-text/attribute search, calendar view Atom feed flexible customizability thorough separation of logic and presentation template file to generate the output server side scripting by the Lua extension post processing by outer commands ¡TZÓZZ¡ZÓ¡€ª,58.óE4Ÿ¨ Example CodeŸª ó:(ó"êø®ï  0`ð ÿÿÿ€€€»àã33™™™™Ì`ð ÿÿÿ–––ûßSÿ™fÌ3™f`ð ÿÿÿ€€€™ÌÿÌÌÿ33̯gÿ`ð Þöñ–––ÿÿÿÆÿf̨`ð ÿÿÙwwwÿÿ÷3ÌÌÿPPÿ™`ð €€ÿÿÿZXÿÿ™dbmoÇÿÿÿ`ð €ÿÿÿ\ßÒ“Ì3¾y`ÿÿ™Ó¢`ð ™ÿÿÿ3fÌÿÿ3f̰fÌÿÿç`ð ÿÿÿ3f™ãëñ3™FŠKfÌÿðå`ð hk]ÿÿÿwwwÑÑË‚€ž¨ÿÌféܹ`ð ff™ÿÿÿ>>\ÿÿÿ`Y{ffÿ™Ìÿÿÿ™`ð R>&ÿÿÿ- ßÀŒ{p_/Ì´Œž £>ÿý?" dÿd@ÿÿïÿÿ1£‚ÿý?" dÿdØ@ÿÿïÿÿ(ÿÿfþ€ Ô €€" Ð@€ ð`€»€ £nÿý?" dÿd@ÿÿï€ÿÿ   @@``€€P£R    @ ` €`£ p£>$€£>  êðâððzð( ð ððÌ ð “ ð6€è‘‡ƒ¿Àÿ ðID<òðà  ðNŸ Þ0¹0¿0 ¿0¤0È0ë0n0øf_-Šš[¢ª ðn ð ƒ ð0€¬”ƒ¿Àÿ ðD<—ðà  ðöŸ ZÞ0¹0¿0 Æ0­0¹0È0n0øf_-Šš[ ,{ 2 ì0Ù0ë0 ,{ 3 ì0Ù0ë0 ,{ 4 ì0Ù0ë0 ,{ 5 ì0Ù0ë0¢ªZðH ð ƒ ð03ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ8Š0º___PPT10‹ë.†£Èàíúâ º j–nÇ0¶0¤0ó0É ˜ððð4ð(ð( ð ð4ðò ð4 Ó ðN€4çt‚tºƒçt„tº¿ƒ¿Àÿ ð’Cðà   ð\Ÿ *¡ ù¦ñY--YY††³³ðô ð4 Ó ðN€\çt‚tºƒçt„tº¿ƒ¿Àÿ ðå wCðà  ð^Ÿ *¡ ø¦ñY--YY††³³ðø ð4 ã ðT€ˆ'çt‚tºƒçt„tº‡¿ƒ¿Àÿ ðä’'ðà   ð\Ÿ *¡ ú¦ñY--YY††³³ðú ð4 ã ðT€l+çt‚tºƒçt„tº‡¿ƒ¿Àÿ ðäå w'ðà  ð^Ÿ *¡ ئñY--YY††³³ðH ð4 ƒ ð0ƒ“rll”Zœ¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ8Š0º___PPT10‹ë. ¥ÈÇ(@î6ï€ 0 MðEÀðœðÝð( ð ðœðr ðœ S ð€Œo¸¿ÿðå°Ðë ðà ¸ ð žð+¢ 𜠣 ð<€Œp¸…‡¿ƒ¿Àÿðé 0ÈÝ ð¿Ÿ¨%Mikio Hirabayashi ¡@&'%'ª6 ðH 𜠃 ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.CÉ€ä^R+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î‹ï € 0 ¢ðš ð ¼ð2ð( ð ð¼ðr ð¼ S ð€Ä̸¿ÿðD<—ðà ¸ ð žð£ ð¼ “ ð6€¨¶¸…‡ƒ¿ÀÿðB W€  ð=Ÿ¨ file system¡  ðr ð¼ S ð€lи¿ÿðID<òðà  ¸ ð žð­ ð¼ “ ð6€Ò¸…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð2 Wp ð9Ÿ¨Cabinet¡ð¦ ð¼ “ ð6€ÔÕ¸…‡ƒ¿Àÿð" ¼ ` ð@Ÿ¨custom storage¡ ð¬ ð¼ “ ð6€8Û¸…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð"Ž ì` ð8Ÿ¨Tyrant¡ð® ð¼ “ ð6€Ú¸…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð"¿Á` ð:Ÿ¨Dystopia¡  ð¤ ð¼ “ ð6€tḅ‡ƒ¿Àÿð ÂP ð>Ÿ¨ applications¡  ðâ ð¼ £ ð<€清‡GÿÌ™ƒ¿Àÿ"ñ¿`ð”Z` ðhŸ¨ Prome nade¡   ª$ðH ð¼ ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÇDÉ@m&K+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +îï€ 0 ð0ðÀðªð( ð ðÀðr ðÀ S ð€Äĸ¿ÿðȯÏf ðà ¸ ð žðH ðÀ ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÊDÉðΟ+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î}ï € 0 ”ðŒðð°ð$ð( ð ð°ðr ð° S ð€@ø¸¿ÿðID<òðà  ¸ ð žðr ð° S ð€èø¸¿ÿðD<—ðà ¸ ð žðH ð° ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.­DÉ01ô)+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +îï € 0 ðð´ðªð( ð ð´ðr ð´ S ð€  ¢¿ÿðwD<—ðà ¢ ð žðH ð´ ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÄDÉPÉ5+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +îc"ï € 0 z!ðr!Pð<TÈð¢ð( ð ðÈð¢ ð4È £ ð<€dG¢…‡ƒ¿ÀÝÝÝËœ1ÿ"ñ¿`ðõ † ð(Ÿ¡ðr ðÈ S ð€,H¢¿ÿðID<òðà  ¢ ð žðx ðÈ c ð$€ J¢¿ÿðD —ðà ¢ ð žðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#Ø 3 ðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#3 Ž ðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð# è ðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#è Cðf ð È ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#Cžðf ð È ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#žùðf ð È ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#øSðf ð È ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#S®ðf ð È ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#® ðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð# dðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#c¾ðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#¾ðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#tðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#tÏðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#Î)ðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#)„ðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#ƒÞðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#Þ9ðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#8“ðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#“îðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#îIðf ðÈ ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð#I¤ð© ðÈ “ ð6€°P¢…‡Ìÿ̃¿Àÿ"ñ¿`ðŽë ËN ð5Ÿ¨key¡ð« ðÈ “ ð6€$V¢…‡Ìÿ̃¿Àÿ"ñ¿`ðŽË›N ð7Ÿ¨value¡ð© ðÈ “ ð6€Y¢…‡Ìÿ̃¿Àÿ"ñ¿`ð~ë Ë> ð5Ÿ¨key¡ð« ðÈ “ ð6€t\¢…‡Ìÿ̃¿Àÿ"ñ¿`ð~Ë›> ð7Ÿ¨value¡ð© ðÈ “ ð6€L`¢…‡Ìÿ̃¿Àÿ"ñ¿`ðnë Ë. ð5Ÿ¨key¡ð« ð È “ ð6€$d¢…‡Ìÿ̃¿Àÿ"ñ¿`ðnË›. ð7Ÿ¨value¡ð© ð5È “ ð6€i¢…‡Ìÿ̃¿Àÿ"ñ¿`ðcë Ë#  ð5Ÿ¨key¡ð« ð6È “ ð6€ül¢…‡Ìÿ̃¿Àÿ"ñ¿`ðcË›#  ð7Ÿ¨value¡ð© ð7È “ ð6€¸p¢…‡Ìÿ̃¿Àÿ"ñ¿`ðS ë Ë  ð5Ÿ¨key¡ð« ð8È “ ð6€˜t¢…‡Ìÿ̃¿Àÿ"ñ¿`ðS Ë›  ð7Ÿ¨value¡ð© ð9È “ ð6€ y¢…‡Ìÿ̃¿Àÿ"ñ¿`ðC ë Ë  ð5Ÿ¨key¡ð« ð:È “ ð6€¨}¢…‡Ìÿ̃¿Àÿ"ñ¿`ðC Ë›  ð7Ÿ¨value¡ð© ð;È “ ð6€„¢…‡Ìÿ̃¿Àÿ"ñ¿`ð9 ë Ëù  ð5Ÿ¨key¡ð« ð<È “ ð6€4€¢…‡Ìÿ̃¿Àÿ"ñ¿`ð9 Ë›ù  ð7Ÿ¨value¡ð© ð=È “ ð6€H^¢…‡Ìÿ̃¿Àÿ"ñ¿`ð) ë Ëé  ð5Ÿ¨key¡ð« ð>È “ ð6€ Ž¢…‡Ìÿ̃¿Àÿ"ñ¿`ð) Ë›é  ð7Ÿ¨value¡ð© ð?È “ ð6€„¢…‡Ìÿ̃¿Àÿ"ñ¿`ð ë ËÙ  ð5Ÿ¨key¡ð« ð@È “ ð6€ Ž¢…‡Ìÿ̃¿Àÿ"ñ¿`ð ˛٠ ð7Ÿ¨value¡ð© ðAÈ “ ð6€™¢…‡Ìÿ̃¿Àÿ"ñ¿`ðë ËÏ ð5Ÿ¨key¡ð« ðBÈ “ ð6€ü“¢…‡Ìÿ̃¿Àÿ"ñ¿`ðË›Ï ð7Ÿ¨value¡ð© ðCÈ “ ð6€hŸ¢…‡Ìÿ̃¿Àÿ"ñ¿`ðÿë Ë¿ ð5Ÿ¨key¡ð« ðDÈ “ ð6€¬£¢…‡Ìÿ̃¿Àÿ"ñ¿`ðÿË›¿ ð7Ÿ¨value¡ðŒ¢ ðHÈ £ ð<€¼¨¢…‡¿ƒ¿Àÿð@Ø d' 𠟨 bucket arrayðdr ðIÈ £ ð<ZG^H£mI¡úÿ¿ÀÑÿðë Ìîðdr ðKÈ £ ð<ZGa$HºfI5$þÿ¿ÀÑÿðë Îðdr ðLÈ £ ð<ZGW'H;`Iñþþÿ¿ÀÑÿðë ì£ ðdr ðMÈ £ ð<ZGL(HÅbIH\ÿÿ¿ÀÑÿðë 7y ð^‚ ðNÈ@ “ ð6GÔùÿÿH0*IŒZ¿ÀÑÿðîë ›Þð^‚ ðOÈ@ “ ð6GÔùÿÿH*IŒZ¿ÀÑÿðÎë ›Ãð^‚ ðPÈ@ “ ð6GÔùÿÿH*IŒZ¿ÀÑÿðÎë ›³ ð^‚ ðQÈ@ “ ð6GÔùÿÿH0*IŒZ¿ÀÑÿð£ ë ›™ ð^‚ ðRÈ@ “ ð6GÔùÿÿH0*IŒZ¿ÀÑÿð™ ë ›‰ ð^‚ ðSÈ@ “ ð6GÔùÿÿH0*IŒZ¿ÀÑÿðy ë ›oð^‚ ðTÈ@ “ ð6GÔùÿÿH0*IŒZ¿ÀÑÿðoë ›_ðH ðÈ ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?¿ð`ð ÈÈIÈð ÈÈKÈðÈ9ÈLÈð È?ÈMÈð ÈÈNÈð È5ÈOÈð È7ÈPÈð:È;ÈQÈð<È=ÈRÈð@ÈAÈSÈðBÈCÈTÈð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÌDÉàÚŽ +Dñ='ñ Uÿÿÿÿ=ñ @Bñ +îë9ï € 0 9ðú8pðv|ÐðŠ6ð( ð ðÐðl ð{Ð “ ð6…‡ƒ¿ÀÝÝÝËœ1ÿ"ñ¿`ðõ ´0ðr ðÐ S ð€„º¢¿ÿðID<òðà  ¢ ð žðr ðÐ S ð€¼Á¢¿ÿðD —ðà ¢ ð žðîðF ð% À€  ð Ð ðˆðøP  ó ð` ð Ð ƒ ð0…‡ƒ¿Àÿð% À» ð` ð Ð ƒ ð0…‡ƒ¿Àÿð» ÀR ð` ð Ð ƒ ð0…‡ƒ¿ÀÿðR Àé ð` ð Ð ƒ ð0…‡ƒ¿Àÿðé À€ ðîðF ð% À€  ðÐ ðˆðè ¨ ð` ðÐ ƒ ð0…‡ƒ¿Àÿð% À» ð` ðÐ ƒ ð0…‡ƒ¿Àÿð» ÀR ð` ðÐ ƒ ð0…‡ƒ¿ÀÿðR Àé ð` ðÐ ƒ ð0…‡ƒ¿Àÿðé À€ ðîðF ð% À€  ðÐ ðˆðH è ¨C ð` ðÐ ƒ ð0…‡ƒ¿Àÿð% À» ð` ðÐ ƒ ð0…‡ƒ¿Àÿð» ÀR ð` ðÐ ƒ ð0…‡ƒ¿ÀÿðR Àé ð` ðÐ ƒ ð0…‡ƒ¿Àÿðé À€ ðîðF ð% À€  ðÐ ðˆðˆ è ¨ƒð` ðÐ ƒ ð0…‡ƒ¿Àÿð% À» ð` ðÐ ƒ ð0…‡ƒ¿Àÿð» ÀR ð` ðÐ ƒ ð0…‡ƒ¿ÀÿðR Àé ð` ðÐ ƒ ð0…‡ƒ¿Àÿðé À€ ðîðF ð% À€  ðÐ ðˆðÈè ¨Ãð` ðÐ ƒ ð0…‡ƒ¿Àÿð% À» ð` ðÐ ƒ ð0…‡ƒ¿Àÿð» ÀR ð` ð Ð ƒ ð0…‡ƒ¿ÀÿðR Àé ð` ð!Ð ƒ ð0…‡ƒ¿Àÿðé À€ ð^b ð"Ð “ ð6G0*HÿÿIÀåúÿ¿ÀÑÿð´  è Dð^b ð#Ð “ ð6G0*HÒ#þÿIÀåúÿ¿ÀÑÿð5  è  ð^b ð$Ð “ ð6G0*HiyÅÿIÀåúÿ¿ÀÑÿð¶  è Ä ð^b ð%Ѐ “ ð6G0*H‘ÉIÀåúÿ¿ÀÑÿð„ è 7 ðîðF ð% À€  ð&Ð ðˆð~ ßéð` ð'Ð ƒ ð0…‡ƒ¿Àÿð% À» ð` ð(Ð ƒ ð0…‡ƒ¿Àÿð» ÀR ð` ð)Ð ƒ ð0…‡ƒ¿ÀÿðR Àé ð` ð*Ð ƒ ð0…‡ƒ¿Àÿðé À€ ðîðF ð% À€  ð+Ð ðˆðÎ ß9 ð` ð,Ð ƒ ð0…‡ƒ¿Àÿð% À» ð` ð-Ð ƒ ð0…‡ƒ¿Àÿð» ÀR ð` ð.Ð ƒ ð0…‡ƒ¿ÀÿðR Àé ð` ð/Ð ƒ ð0…‡ƒ¿Àÿðé À€ ðîðF ð% À€  ð0Ð ðˆð ߉ ð` ð1Ð ƒ ð0…‡ƒ¿Àÿð% À» ð` ð2Ð ƒ ð0…‡ƒ¿Àÿð» ÀR ð` ð3Ð ƒ ð0…‡ƒ¿ÀÿðR Àé ð` ð4Ð ƒ ð0…‡ƒ¿Àÿðé À€ ðîðF ð% À€  ð5Ð ðˆðnßÙ ð` ð6Ð ƒ ð0…‡ƒ¿Àÿð% À» ð` ð7Ð ƒ ð0…‡ƒ¿Àÿð» ÀR ð` ð8Ð ƒ ð0…‡ƒ¿ÀÿðR Àé ð` ð9Ð ƒ ð0…‡ƒ¿Àÿðé À€ ðîðF ð% À€  ð:Ð ðˆð" À  ð` ð;Ð ƒ ð0…‡ƒ¿Àÿð% À» ð` ð<Ð ƒ ð0…‡ƒ¿Àÿð» ÀR ð` ð=Ð ƒ ð0…‡ƒ¿ÀÿðR Àé ð` ð>Ð ƒ ð0…‡ƒ¿Àÿðé À€ ðîðF ð% À€  ð?Ð ðˆðrÀ Ýð` ð@Ð ƒ ð0…‡ƒ¿Àÿð% À» ð` ðAÐ ƒ ð0…‡ƒ¿Àÿð» ÀR ð` ðBÐ ƒ ð0…‡ƒ¿ÀÿðR Àé ð` ðCÐ ƒ ð0…‡ƒ¿Àÿðé À€ ðîðF ð% À€  ðDÐ ðˆðÂÀ -ð` ðEÐ ƒ ð0…‡ƒ¿Àÿð% À» ð` ðFÐ ƒ ð0…‡ƒ¿Àÿð» ÀR ð` ðGÐ ƒ ð0…‡ƒ¿ÀÿðR Àé ð` ðHÐ ƒ ð0…‡ƒ¿Àÿðé À€ ðîðF ð% À€  ðIÐ ðˆðÀ }ð` ðJÐ ƒ ð0…‡ƒ¿Àÿð% À» ð` ðKÐ ƒ ð0…‡ƒ¿Àÿð» ÀR ð` ðLÐ ƒ ð0…‡ƒ¿ÀÿðR Àé ð` ðMÐ ƒ ð0…‡ƒ¿Àÿðé À€ ð^b ðNÐ “ ð6G*H0øõÿIÞ_ýÿ¿ÀÑÿðD¨¼ð^b ðOЀ “ ð6G*H°GIÞ_ýÿ¿ÀÑÿð ¨Å ð^b ðPЀ “ ð6G*HIIÞ_ýÿ¿ÀÑÿð\ ¨F ð^b ðQЀ “ ð6G*H([IÞ_ýÿ¿ÀÑÿð¬ ¨Ç ð^b ðRЀ “ ð6G0*HòiI`•ûÿ¿ÀÑÿð` ¨À ð^b ðSЀ “ ð6G0*H4WI`•ûÿ¿ÀÑÿð°¨À… ð^b ðTЀ “ ð6G0*H,çI`•ûÿ¿ÀÑÿð¨À ð^b ðUЀ “ ð6G0*HTªI`•ûÿ¿ÀÑÿðP¨À‡ ð› ðVÐ “ ð6€`Õ¢…‡ƒ¿ÀÿðÛ Î~›  ð5Ÿ¨key¡ð ðWÐ “ ð6€¤Ù¢…‡ƒ¿ÀÿðÛ ~q›  ð7Ÿ¨value¡ð› ðXÐ “ ð6€|Ý¢…‡ƒ¿Àÿð› Î~[  ð5Ÿ¨key¡ð ðYÐ “ ð6€ôᢅ‡ƒ¿Àÿð› ~q[  ð7Ÿ¨value¡ð› ðZÐ “ ð6€Ô墅‡ƒ¿Àÿð[ Î~  ð5Ÿ¨key¡ð ð[Ð “ ð6€h颅‡ƒ¿Àÿð[ ~q  ð7Ÿ¨value¡ð› ð\Ð “ ð6€ü종‡ƒ¿Àÿð Î~Û  ð5Ÿ¨key¡ð ð]Ð “ ð6€ð¢…‡ƒ¿Àÿð ~qÛ  ð7Ÿ¨value¡ð› ð^Ð “ ð6€$ô¢…‡ƒ¿Àÿð Î~Ë  ð5Ÿ¨key¡ð ð_Ð “ ð6€¸÷¢…‡ƒ¿Àÿð ~qË  ð7Ÿ¨value¡ð› ð`Ð “ ð6€Lû¢…‡ƒ¿ÀÿðË Î~‹ ð5Ÿ¨key¡ð ðaÐ “ ð6€Ì²…‡ƒ¿ÀÿðË ~q‹ ð7Ÿ¨value¡ð› ðbÐ “ ð6€²…‡ƒ¿Àÿð‹Î~K ð5Ÿ¨key¡ð ðcÐ “ ð6€è²…‡ƒ¿Àÿð‹~qK ð7Ÿ¨value¡ð› ðdÐ “ ð6€4×¢…‡ƒ¿ÀÿðKÎ~  ð5Ÿ¨key¡ð ðeÐ “ ð6€²…‡ƒ¿ÀÿðK~q  ð7Ÿ¨value¡ð^b ðfÐ “ ð6G*H\ÌúÿI@ úÿ¿ÀÑÿð¼ßΫð^b ðgЀ “ ð6G*HI@ úÿ¿ÀÑÿð{ ßÎað› ðhÐ “ ð6€L²…‡ƒ¿Àÿð«Î~k ð5Ÿ¨key¡ð ðiÐ “ ð6€²…‡ƒ¿Àÿð«~qk ð7Ÿ¨value¡ð› ðjÐ “ ð6€h²…‡ƒ¿ÀÿðkÎ~+ ð5Ÿ¨key¡ð ðkÐ “ ð6€à ²…‡ƒ¿Àÿðk~q+ ð7Ÿ¨value¡ð› ðlÐ “ ð6€L$²…‡ƒ¿Àÿð+Î~ë ð5Ÿ¨key¡ð ðmÐ “ ð6€$(²…‡ƒ¿Àÿð+~që ð7Ÿ¨value¡ð› ðnÐ “ ð6€ü+²…‡ƒ¿ÀÿðëÎ~«  ð5Ÿ¨key¡ð ðoÐ “ ð6€l2²…‡ƒ¿Àÿðë~q«  ð7Ÿ¨value¡ð^b ðpЀ “ ð6G*H!úI@ úÿ¿ÀÑÿðK ßÎð› ðqÐ “ ð6€L6²…‡ƒ¿Àÿð{Î~; ð5Ÿ¨key¡ð ðrÐ “ ð6€,:²…‡ƒ¿Àÿð{~q; ð7Ÿ¨value¡ð› ðsÐ “ ð6€À=²…‡ƒ¿Àÿð;Î~û ð5Ÿ¨key¡ð ðtÐ “ ð6€TA²…‡ƒ¿Àÿð;~qû ð7Ÿ¨value¡ð› ðuÐ “ ð6€lD²…‡ƒ¿ÀÿðûÎ~» ð5Ÿ¨key¡ð ðvÐ “ ð6€ü8²…‡ƒ¿Àÿðû~q» ð7Ÿ¨value¡ð› ðwÐ “ ð6€|M²…‡ƒ¿Àÿð»Î~{ ð5Ÿ¨key¡ð ðxÐ “ ð6€@J²…‡ƒ¿Àÿð»~q{ ð7Ÿ¨value¡ð^b ðyЀ “ ð6G*H~˜I@ úÿ¿ÀÑÿðßΫ ðŒ¢ ð|Ð £ ð<€P²…‡¿ƒ¿Àÿð]# —D 𠟨 B tree indexðH ðÐ ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ðð ÐÐ"Ðð ÐÐ#Ðð ÐÐ$Ðð Ð!Ð%ÐðÐ*ÐNÐðÐ/ÐOÐðÐ4ÐPÐðÐ9ÐQÐð Ð>ÐRÐð ÐCÐSÐð ÐHÐTÐð ÐMÐUÐð *ÐdÐfÐð)Ð\ÐgÐð(ÐnÐpÐð'ÐwÐyÐð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÐDÉ0!VÍ+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +îšï € 0 ±ð©°ð-Ìàð9ð( ð ðàðr ðà S ð€k²¿ÿðID<òðà  ² ð žðr ðà S ð€8l²¿ÿðD —ðà ² ð žðl ðà “ ð6…‡ƒ¿ÀÝÝÝËœ1ÿ"ñ¿`ð@õ †ð« ð¤à “ ð6€@V²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðP} ž ð7Ÿ¨value¡ð« ð¥à “ ð6€ q²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðPËì ð7Ÿ¨value¡ð« ð¦à “ ð6€øt²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðP: ð7Ÿ¨value¡ð« ð§à “ ð6€Ðx²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðPf‡ ð7Ÿ¨value¡ð« ð¨à “ ð6€¨|²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð3} žó ð7Ÿ¨value¡ð« ð©à “ ð6€€€²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð3Ëìó ð7Ÿ¨value¡ð« ðªà “ ð6€X„²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð3:ó ð7Ÿ¨value¡ð« ð«à “ ð6€0ˆ²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð3f‡ó ð7Ÿ¨value¡ð« ð¬à “ ð6€Œ²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð} žÖ ð7Ÿ¨value¡ð« ð­à “ ð6€à²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðËìÖ ð7Ÿ¨value¡ð« ð®à “ ð6€¸“²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð:Ö ð7Ÿ¨value¡ð« ð¯à “ ð6€—²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðf‡Ö ð7Ÿ¨value¡ð« ð°à “ ð6€h›²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðø} ž¸  ð7Ÿ¨value¡ð« ð±à “ ð6€@Ÿ²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðøËì¸  ð7Ÿ¨value¡ð« ð²à “ ð6€£²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðø:¸  ð7Ÿ¨value¡ð« ð³à “ ð6€ð¦²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðøf‡¸  ð7Ÿ¨value¡ð« ð´à “ ð6€Èª²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðÛ } ž›  ð7Ÿ¨value¡ð« ðµà “ ð6€¡²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðÛ Ëì›  ð7Ÿ¨value¡ð« ð¶à “ ð6€8±²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðÛ :›  ð7Ÿ¨value¡ð« ð·à “ ð6€|µ²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðÛ f‡›  ð7Ÿ¨value¡ð« ð¸à “ ð6€T¹²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð¾ } ž~  ð7Ÿ¨value¡ð« ð¹à “ ð6€Ì½²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð¾ Ëì~  ð7Ÿ¨value¡ð« ðºà “ ð6€¬Á²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð¾ :~  ð7Ÿ¨value¡ð« ð»à “ ð6€@Ų…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð¾ f‡~  ð7Ÿ¨value¡ð« ð¼à “ ð6€ÔȲ…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð¡ } ža  ð7Ÿ¨value¡ð« ð½à “ ð6€h̲…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð¡ Ëìa  ð7Ÿ¨value¡ð« ð¾à “ ð6€üϲ…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð¡ :a  ð7Ÿ¨value¡ð« ð¿à “ ð6€Ó²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð¡ f‡a  ð7Ÿ¨value¡ð« ðÀà “ ð6€$ײ…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð„ } žD  ð7Ÿ¨value¡ð« ðÁà “ ð6€¸Ú²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð„ ËìD  ð7Ÿ¨value¡ð« ðÂà “ ð6€LÞ²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð„ :D  ð7Ÿ¨value¡ð« ðÃà “ ð6€àá²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð„ f‡D  ð7Ÿ¨value¡ð« ðÄà “ ð6€tå²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðf } ž& ð7Ÿ¨value¡ð« ðÅà “ ð6€é²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðf Ëì& ð7Ÿ¨value¡ð« ðÆà “ ð6€œì²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðf :& ð7Ÿ¨value¡ð« ðÇà “ ð6€0ð²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðf f‡& ð7Ÿ¨value¡ð« ðÈà “ ð6€Äó²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðI} ž  ð7Ÿ¨value¡ð« ðÉà “ ð6€X÷²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðIËì  ð7Ÿ¨value¡ð« ðÊà “ ð6€ìú²…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðI:  ð7Ÿ¨value¡ð« ðËà “ ð6€Ì»…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðIf‡  ð7Ÿ¨value¡ð¡¢ ðÌà £ ð<€»…‡¿ƒ¿ÀÿðnP  . ð5Ÿ¨array¡ðH ðà ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ðð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÔDÉàé+I+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î“;ï € 0 ª:ð¢:Pðg£Tðr9ð( ð ðTðx ðT c ð$€ôޏ¿ÿˆðID<òðà  ¸ ð žð~ ðT s ð*€Ì¸¿ÿˆðDm —ðà ¸ ð žð¢ ð@T £ ð<€D£¸…‡ƒ¿ÀËœ1ÿ"ñ¿`ðåõ † ð(Ÿ¡ð6ðF ð0 ÐÚÕ ðAT ðˆð«0 Ú°ð¹ ðBT “ ð6€°³¸…‡ÿÿ™ƒ¿Àÿ"ñ¿`ð0 Ð Õ ð=Ÿ¨ primary key¡  ð¦ ðCT “ ð6€àã»…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐÚÕ ð*Ÿ¡ðËðN ðÐà õµ  ðDT ðˆðå<ºð² ðET “ ð6€ˆæ»…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ðFT “ ð6€”ê»…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ðËðN ðÐà õµ  ðGT ðˆðWå|ºð² ðHT “ ð6€ î»…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ðIT “ ð6€Ôñ»…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ðËðN ðÐà õµ  ðJT ðˆð—强ð² ðKT “ ð6€,ö»…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ðLT “ ð6€ú»…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ð6ðF ð0 ÐÚÕ ðMT ðˆðû0 Ú ð¹ ðNT “ ð6€D…‡ÿÿ™ƒ¿Àÿ"ñ¿`ð0 Ð Õ ð=Ÿ¨ primary key¡  ð¦ ðOT “ ð6€(…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐÚÕ ð*Ÿ¡ðËðN ðÐà õµ  ðPT ðˆðå<ºð² ðQT “ ð6€Ð…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ðRT “ ð6€È…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ðËðN ðÐà õµ  ðST ðˆðWå|ºð² ðTT “ ð6€\²¸…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ðUT “ ð6€0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ðËðN ðÐà õµ  ðVT ðˆð—强ð² ðWT “ ð6€…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ðXT “ ð6€à…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ð6ðF ð0 ÐÚÕ ðYT ðˆðK 0 ÚP ð¹ ðZT “ ð6€È…‡ÿÿ™ƒ¿Àÿ"ñ¿`ð0 Ð Õ ð=Ÿ¨ primary key¡  ð¦ ð[T “ ð6€è…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐÚÕ ð*Ÿ¡ðËðN ðÐà õµ  ð\T ðˆðå<ºð² ð]T “ ð6€„$…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ð^T “ ð6€ü(…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ðËðN ðÐà õµ  ð_T ðˆðWå|ºð² ð`T “ ð6€@,…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ðaT “ ð6€„0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ðËðN ðÐà õµ  ðbT ðˆð—强ð² ðcT “ ð6€¬4…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ðdT “ ð6€Ô8…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ð6ðF ð0 ÐÚÕ ðeT ðˆð› 0 Ú  ð¹ ðfT “ ð6€h<…‡ÿÿ™ƒ¿Àÿ"ñ¿`ð0 Ð Õ ð=Ÿ¨ primary key¡  ð¦ ðgT “ ð6€Ü@…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐÚÕ ð*Ÿ¡ðËðN ðÐà õµ  ðhT ðˆðå<ºð² ðiT “ ð6€ÜD…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ðjT “ ð6€TI…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ðËðN ðÐà õµ  ðkT ðˆðWå|ºð² ðlT “ ð6€˜L…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ðmT “ ð6€$P…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ðËðN ðÐà õµ  ðnT ðˆð—强ð² ðoT “ ð6€àT…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ðpT “ ð6€ÀX…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ð6ðF ð0 ÐÚÕ ðqT ðˆðë 0 Úð ð¹ ðrT “ ð6€X[…‡ÿÿ™ƒ¿Àÿ"ñ¿`ð0 Ð Õ ð=Ÿ¨ primary key¡  ð¦ ðsT “ ð6€Àa…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐÚÕ ð*Ÿ¡ðËðN ðÐà õµ  ðtT ðˆðå<ºð² ðuT “ ð6€hd…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ðvT “ ð6€th…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ðËðN ðÐà õµ  ðwT ðˆðWå|ºð² ðxT “ ð6€Ll…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ðyT “ ð6€$p…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ðËðN ðÐà õµ  ðzT ðˆð—强ð² ð{T “ ð6€ n…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ð|T “ ð6€hx…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ð6ðF ð0 ÐÚÕ ð}T ðˆð; 0 Ú@ð¹ ð~T “ ð6€w…‡ÿÿ™ƒ¿Àÿ"ñ¿`ð0 Ð Õ ð=Ÿ¨ primary key¡  ð¦ ðT “ ð6€è…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐÚÕ ð*Ÿ¡ðËðN ðÐà õµ  ð€T ðˆðå<ºð² ðT “ ð6€ì‚…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ð‚T “ ð6€ˆ‡…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ðËðN ðÐà õµ  ðƒT ðˆðWå|ºð² ð„T “ ð6€À~…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ð…T “ ð6€X…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ðËðN ðÐà õµ  ð†T ðˆð—强ð² ð‡T “ ð6€ “…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÐà Ôµ  ð6Ÿ¨name¡ ð³ ðˆT “ ð6€—…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÕà õµ  ð7Ÿ¨value¡ ðf ð‰T ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðmN ® ýðf ðŠT ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðm®  ýðf ð‹T ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðm n ýðf ðŒT ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðmn Î ýðf ðT ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðmÎ .ýðf ðŽT ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðm.Žýðf ðT ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðmŽîýðf ðT ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðmîNýðf ð‘T ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðmN®ýðf ð’T ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðm®ýðf ð“T ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðmnýðf ð”T ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðmnÎýðf ð•T ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðmÎ.ýðf ð–T ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðm.Žýðf ð—T ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðmŽîýðf ð˜T ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðmîNýðf ð™T ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðmN®ýðf ðšT ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðm®ý𨢠ð›T £ ð<€ …‡¿ƒ¿Àÿð»@F{ ð<Ÿ¨ column index¡ ðr" ðœT £ ð<…‡ƒ¿ÀËœ1Îÿ"ñ¿`ð{P‰»𨢠ðT £ ð<€x¥…‡¿ƒ¿Àÿð:úÔú ð<Ÿ¨ bucket array¡ ðdb ðžT £ ð<ZG0*H°¤öÿI—eùÿ¿ÀÑÿðýh ž «ðdb ðŸT £ ð<ZG0*H°2ÿÿI Þþÿ¿ÀÑÿðýh Þ› ðdb ð T £ ð<ZG0*HË…þÿI?Tÿÿ¿ÀÑÿðýh ¾; ðdb ð¡T £ ð<ZG0*Hp€ÿÿIs ýÿ¿ÀÑÿðýh ^ûðdb ð¢T £ ð<ZG0*H¥bÿÿIÿÿ¿ÀÑÿðýh žë ðdb ð£T £ ð<ZG0*HÇ ÿÿIttþÿ¿ÀÑÿðýh ~K ðH ðT ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?oðÀð ŒTBTžTð ’TfTŸTðT~T Tð–TNT¡Tð”TrT¢Tð‘TZT£Tð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÌDÉàÚŽ +Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î}ï € 0 ”ðŒð¸ð$ð( ð ð¸ðr ð¸ S ð€$¹¿ÿðID<òðà   ð žðr ð¸ S ð€ü¹¿ÿðD<—ðà  ð žðH 𸠃 ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÇDÉÀ¿Ö;+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î}ï € 0 ”ðŒ ðÜð$ð( ð ðÜðr ðÜ S ð€˜Ç¿ÿðID<òðà   ð žðr ðÜ S ð€pÈ¿ÿðD<—ðà  ð žðH ðÜ ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÓDɰ%¶|+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î*ï € 0 Að9ð<ðÑ ð( ð ð<ðr ð< S ð€¼Ú¿ÿðID<òðà   ð žð˜¢ ð< Ó ðN€\ðÒ‚0*ƒðÒ„0*…¿ƒ¿À€€€ÿ"ñ¿`ðÅq*  𠟨b#include #include #include #include #include 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.hdb", 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)); } ¡äc G G ÿ™™þ G G ÿ™™þ•G G‚ ™ÌÿþG G ÿÿfþG G‚ ™ÌÿþG G ÿÿfþG G‚ ™ÌÿþG G ÿÿfþG G ÿÿfþG G ÿÿfþwG G‚ ™Ìÿþ G G ÿÿfþ³G ª¢       4! !5J<   ð¢ ð< Ó ðN€ððÒ‚0*ƒðÒ„0*…¿ƒ¿À€€€ÿ"ñ¿`ðCm &i ð󟨯 /* 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; } ¡&°G G‚ ™ÌÿþG G ÿÿfþAG G ÿÿfþoG G‚ ™ÌÿþG G ÿÿfþjG G‚ ™ÌÿþG G ÿÿfþG ªú G   %% ðH ð< ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.FÉ0q}+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î ï€ 0  ðÐðèð°ð( ð ðèðx ðè c ð$€D:¿ÿˆðȯÏf ðà  ð žðH ðè ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÊDÉðΟ+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î}ï € 0 ”ðŒàðìð$ð( ð ðìðr ðì S ð€hÕ»¿ÿðID<òðà  » ð žðr ðì S ð€@Ö»¿ÿðD<—ðà » ð žðH ðì ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.×DÉø–Æ+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î ï € 0  ððôð°ð( ð ðôðx ðô c ð$€@J¿ÿˆðwD<—ðà  ð žðH ðô ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÄDÉPÉ5+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +îE#ï € 0 \"ðT"@ð57ð$ ð( ð ððl ð7 “ ð6…‡ƒ¿ÀÝÝÝËœ1ÿ"ñ¿`ðMm ´—ðr ð S ð€X¿ÿðID<òðà   ð žðl ð “ ð6…‡ƒ¿ÀÝÝÝËœ1ÿ"ñ¿`ðMžå —ðœ" ð “ ð6€Ô[…‡Ìÿÿƒ¿Àÿ"ñ¿`ðZ(èª  ð(Ÿ¡ð©¢ ð £ ð<€¨Ù»…‡¿ƒ¿ÀÿðZ(F ð=Ÿ¨ master server¡ð®b ð “ ð6€œ¨·…‡™Ìÿƒ¿Àÿ"ñ¿`ðJ@š ð:Ÿ¨database¡   ðž ð  “ ð6€¸`…‡Ìÿ̃¿Àÿ"ñ¿`ð* HXz  ð*Ÿ¡ ð° ð  “ ð6€ˆc…‡Ìÿ̃¿Àÿ"ñ¿`ðú(J  ð<Ÿ¨ update log¡   ðd ð  £ ð<ZG¸zúÿHÿÿÿÿI¸zúÿ¿ÀÑÿðš  úðœ" ð  “ ð6€Pi…‡Ìÿÿƒ¿Àÿ"ñ¿`ðú ÈJ ð(Ÿ¡𨢠ð  £ ð<€l…‡¿ƒ¿Àÿðú Öº  ð<Ÿ¨ slave server¡ ð®b ð “ ð6€dn…‡™Ìÿƒ¿Àÿ"ñ¿`ðê à : ð:Ÿ¨database¡   ðž ð “ ð6€¤r…‡Ìÿ̃¿Àÿ"ñ¿`ðÊ(8 ð*Ÿ¡ ð° ð “ ð6€Ÿ¨standby master¡ð®b ð) “ ð6€ô¸…‡™Ìÿƒ¿Àÿ"ñ¿`ðû ;{K  ð:Ÿ¨database¡   ðž ð* “ ð6€€¾…‡Ìÿ̃¿Àÿ"ñ¿`ðÛ ƒ“+  ð*Ÿ¡ ð° ð+ “ ð6€¼À…‡Ìÿ̃¿Àÿ"ñ¿`ð« Scû  ð<Ÿ¨ update log¡   ðd ð, £ ð<ZG¸zúÿHÿÿÿÿI¸zúÿ¿ÀÑÿðK [[« ð^b ð-€ “ ð6G0*H­XI( þÿ¿ÀÑÿ𣠽;` ðÈ¢ ð. ³ ðB€`Å…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ðƒ ùÉ  ðHŸ¨replicate the difference¡ð¬" ð/ “ ð6€¼Æ…‡Ìÿÿƒ¿Àÿ"ñ¿`ðØ} ½È ð8Ÿ¨client¡ðd ð0 £ ð<ZG;rþÿHÿÿÿÿI;rþÿ¿ÀÑÿðÈ  ð¡¢ ð1 £ ð<€`Í…‡¿ƒ¿Àÿðø½ H ¸ ð5Ÿ¨query¡ðdb ð2 £ ð<´G0*H¬„ýÿI„Iþÿ¿ÀÑÿð° ÕSS ðjb ð3@ ³ ðBZG*HŸ¨ task manager¡ ðf ðð ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðû L<[ ðf ðð ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ð[ L<» ðf ðð ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ð» L< ðf ðð ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ð L<{ ðf ðð ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ð{ L<Û ðf ðð ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðÛ L<; ðf ð ð ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ð; L<› ðf ð!ð ƒ ð0…‡ÿÿ™ƒ¿Àÿ"ñ¿`ð› L<û ð¡¢ ð"ð £ ð<€¨þ…‡¿ƒ¿Àÿð; ì –û  ð5Ÿ¨queue¡ðÇ" ð#ð “ ð6€0Ó…‡Ìÿ̃¿Àÿ"ñ¿`ðË ¬ ì ‹  ðSŸ¨enqueue¡ªðÅ" ð$ð “ ð6€„…‡Ìÿ̃¿Àÿ"ñ¿`ð ¬ ì Ë  ðQŸ¨deque¡ªðdR ð%ð€ £ ð<´G ýÿH¹ I ýÿ¿ÀÑÿð« Ì L ðdR ð&ð@ £ ð<ZGºúÿHXIºúÿ¿ÀÑÿð‹ Ì LK ð³" ð'ð “ ð6€h…‡Ìÿÿƒ¿Àÿ"ñ¿`ð± c#¡  ð?Ÿ¨ worker thread¡ð³" ð(ð “ ð6€À…‡Ìÿÿƒ¿Àÿ"ñ¿`ðÑ c#Á ð?Ÿ¨ worker thread¡ð³" ð)ð “ ð6€L…‡Ìÿÿƒ¿Àÿ"ñ¿`ðñc#á ð?Ÿ¨ worker thread¡ðdR ð*ð@ £ ð<ZGqƒôÿH¯,Iqƒôÿ¿ÀÑÿðË Ì c) ðdR ð+ð@ £ ð<ZGh,ýÿH¯,Ih,ýÿ¿ÀÑÿðË Ì cIðdR ð,ð@ £ ð<ZGrcþÿH¯,Ircþÿ¿ÀÑÿðË Ì ciðXb ð-ðÀ ƒ ð0GiûÿÿHœIR¤¿Àÿð Ì #) ð^b ð.ðÀ “ ð6GiûÿÿH=’IR¤¿ÀÑÿð Ì #Ið^b ð/ðÀ “ ð6GiûÿÿHÏŠIR¤¿ÀÑÿð Ì #iðÈ¢ ð0ð ³ ðB€¸"…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ð}©= ðHŸ¨queue back if keep-alive¡ð¼¢ ð1ð ³ ðB€0(…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ððÈ µ° ð<Ÿ¨ do each task¡ ðï¢ ð2ð ³ ðB€ä+…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ð]¯,£ ðoŸ¨?accept the client connection if the event is about the listener¡@@ðÎ" ð3ð “ ð6€d$…‡Ìÿ̃¿Àÿ"ñ¿`ð«ÌÌ k  ðZŸ¨epoll_ctl(del)¡ª ðdb ð4ð@ £ ð<ZG0*H€—I@Óûÿ¿ÀÑÿðìL«ðdR ð5ð@ £ ð<ZG@´üÿHPÜI@´üÿ¿ÀÑÿðk L¬ + ðdR ð6ð@ £ ð<ZG€hùÿH "I€hùÿ¿ÀÑÿðkLü ËðdR ð7ð £ ð<ZGWúÿH€ üÿIWúÿ¿ÀÑÿð{Ì t ð¢ ð8ð ³ ðB€ 5…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ð6 ¢† |  𘟨Fmove the readable client socket from the epoll queue to the task queue¡GGª)ð'¢ ð9ð ³ ðB€ü:…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ð·› ý 𧟨Cfirst of all, the listening socket is enqueued into the epoll queue¡DDª,& ðH ðð ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ðððððð ððððððððððððð$ð%ðð#ðð&ðð$ð'ð*ðð$ð(ð+ðð $ð)ð,ðð 'ðð-ðð (ðð.ðð )ðð/ðð ð3ð4ðð3ð#ð5ððð ð6ððð3ð7ðð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÙDÉÐ?Ÿ+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +îDï € 0 [ðSàð4ðcð( ð ð4ðr ð4 S ð€@C¿ÿðD<Ëðà  ð žðl ð4 “ ð6…‡ƒ¿ÀÝÝÝËœ1ÿ"ñ¿`ðø'‡—ðr ð4 S ð€xD¿ÿðID<òðà   ð žðr ð4 £ ð<…‡ƒ¿ÀËjJÎÿ"ñ¿`ðS æ 60ðr ð4 £ ð<…‡ƒ¿ÀËjJÎÿ"ñ¿`ðS V&0ðf" ð4 ƒ ð0…‡Ìÿÿƒ¿Àÿ"ñ¿`ðs >Žƒðf" ð4 ƒ ð0…‡Ìÿÿƒ¿Àÿ"ñ¿`ð ^#ðf" ð4 ƒ ð0…‡Ìÿÿƒ¿Àÿ"ñ¿`ð³ Þ.à ðf" ð 4 ƒ ð0…‡Ìÿÿƒ¿Àÿ"ñ¿`ðc n žc ð±" ð 4 “ ð6€TH…‡Ìÿ̃¿Àÿ"ñ¿`ðã Î î  ð=Ÿ¨ Tokyo Cabinet¡ðË" ð 4 “ ð6€°L…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðã >  ðWŸ¨ Lua processor¡ª ðŒ¢ ð 4 £ ð<€`R…‡¿ƒ¿Àÿð“ Î Šz  𠟨 Tokyo Tyrantð¬b ð 4 “ ð6€$U…‡Ìÿ̃¿Àÿ"ñ¿`ðî Q 9 ð8Ÿ¨database¡  ð« ð4 “ ð6€LX…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðF ð7Ÿ¨script ¡ðdb ð4 £ ð<G*HÀüÐÿIäûÿ¿ÀËjJÿð Å Þ î ðdb ð4@ £ ð<G *HõL Iðqúÿ¿ÀËjJÿð .®ð§¢ ð4 ƒ ð0€Ø]¿ƒ¿ÀÿðÑG' ðGŸ¨user-defined operations¡ðf" ð4 ƒ ð0…‡Ìÿÿƒ¿Àÿ"ñ¿`ðS ®þc ð‡¢ ð4 £ ð<€Pc…‡¿ƒ¿Àÿðƒ Þ j  ðŸ¨Clients𤢠ð4 £ ð<€äe…‡¿ƒ¿Àÿð€ æ  @  ð8Ÿ¨back end¡ 𥢠ð4 £ ð<€(i…‡¿ƒ¿Àÿð V‚P  ð9Ÿ¨ front end¡ ðì¢ ð4 £ ð<€€n…‡¿ÿÿ™ƒ¿Àÿ"ñ¿`ð` VË Ù  ðrŸ¨,request function name key data value data¡*%-%ðÉ¢ ð4 ƒ ð0€hu¿ÿÿ™ƒ¿Àÿ"ñ¿`ðs N® à  ð[Ÿ¨response result data¡* - ðpb ð4€ à ðHG0*HàVI+šÿÿ¿ÀËjJÑÔÕÿð¬ øh $ ðvb ð4€ Ó ðN´G0*H€IËEÿÿ¿ÀËjJÑÔÕÿð þn ‹ ðH ð4 ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?Oð€ð 4 44ð4 44ð4ÿÿÿÿÿÿÿÿð4ÿÿÿÿÿÿÿÿð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.øEÉ€ë *+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +îôï € 0  ðð&3øð3ð( ð ðøðl ð(ø “ ð6…‡ƒ¿ÀÝÝÝËœ1ÿ"ñ¿`ðz@ iðr ðø S ð€†¿ÿðID<òðà   ð žðx ðø c ð$€Ü†¿ÿðD —ðà  ð žðf" ðø ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ðŠZ j:ðf" ðø ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ð/ú Úðf" ðø ƒ ð0…‡Ìÿ̃¿Àÿ"ñ¿`ðÔš ªzð  ð ø “ ð6€D‹…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð: J Jê  ð,Ÿ¡ ð  ð ø “ ð6€t‰…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð  º  ð,Ÿ¡ ð  ð ø “ ð6€Œ’…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðÚ ê êŠ  ð,Ÿ¡ ð³ ð ø “ ð6€ð”…‡ÿÌ™ƒ¿Àÿ"ñ¿`𪠺 ºZ  ð?Ÿ¨ other pages¡   ðË ð ø “ ð6€™…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðŠŠ Š:  ðWŸ¨ search.pl¡   ª ðÏ ðø “ ð6€Ìœ…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð:Z Zê ð[Ÿ¨ view_diary.pl¡ ª ðÐ ðø “ ð6€´¡…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð* *Ê ð\Ÿ¨show_friend.pl¡ ªðÉ ðø “ ð6€|¦…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðúú úª ðUŸ¨home.pl¡ ªðÒ ðø “ ð6€¤ª…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðj Z Z ð^Ÿ¨list_bookmark.pl¡ ªðÐ ðø “ ð6€@¯…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðJ ú úú  ð\Ÿ¨list_friend.pl¡ ªðÀ¢ ðø £ ð<€(´…‡¿ƒ¿ÀÿðÈ 3 ðTŸ¨mod_perl¡ ªðf" ðø ƒ ð0…‡Ìÿÿƒ¿Àÿ"ñ¿`ðm)Ypð©¢ ðø £ ð<€(¸…‡¿ƒ¿ÀÿðšV÷Z ð=Ÿ¨ TT (active)¡ ð°b ðø “ ð6€x¼…‡™Ìÿƒ¿Àÿ"ñ¿`ð’‰ÿ ð<Ÿ¨database¡  ðXb ðø ƒ ð0G#*H4TþÿIPþÿ¿ÀÿðÒú)ïðXb ðø€ ƒ ð0G"*H d¾IÖþÿ¿Àÿðï*)òðXb ðø€ ƒ ð0G!*HüII±bþÿ¿ÀÿðïZ)ðXb ðø€ ƒ ð0G *H.=I?þÿ¿ÀÿðïŠ)b ðXb ðø€ ƒ ð0G*HçóIþÿ¿Àÿðïº)‚ 𚢠ðø ³ ðB€lÂ…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ð?®Ú& ðŸ¨update𙢠ðø ³ ðB€$Æ…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ðƒ cj  ðŸ¨fetchðXb ð%ø€ ƒ ð0G*HôëIæýÿ¿Àÿðïê)² ðXb ð&ø€ ƒ ð0G*HÁäI_­ýÿ¿Àÿðï)â ðXb ð'ø€ ƒ ð0G*H6ÞIUiýÿ¿ÀÿðïJ) ðXb ð-ø€ ƒ ð0G#*HZ°IPþÿ¿Àÿðïú)" ðXb ð.ø€ ƒ ð0G!*H:¢I±bþÿ¿ÀÿðïZ)Bðf" ð/ø ƒ ð0…‡Ìÿÿƒ¿Àÿ"ñ¿`ðc „´f 𪢠ð0ø £ ð<€Ê…‡¿ƒ¿Àÿð ±ÁP  ð>Ÿ¨ TT (standby)¡ ð°b ð1ø “ ð6€pÏ…‡™Ìÿƒ¿Àÿ"ñ¿`ðˆ äY  ð<Ÿ¨database¡  ðjb ð2ø@ ³ ðBZG*H©Iéíüÿ¿ÀÐÑÿðpÁc 🢠ð3ø ³ ðB€`Ô…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ðø„ˆß  🨠replicationðH ðø ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?¿ð`ðøøøðøøøðøøøð øøøð øøøð øø%øð øø&øð øø'øðøø-øðøø.øðø/ø2øð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÚDÉ ?¾+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +îiï € 0 €ðx0ð/Hðð( ð ðHðl ðH “ ð6…‡ƒ¿ÀÝÝÝËœ1ÿ"ñ¿`ðz@ iðœ" ð/H “ ð6€Àæ…‡Ìÿÿƒ¿Àÿ"ñ¿`ð]õ m ð(Ÿ¡ðœ" ð.H “ ð6€üé…‡Ìÿÿƒ¿Àÿ"ñ¿`ð/È é ? ð(Ÿ¡ðr ðH S ð€ ë¿ÿðID<òðà   ð žðr ðH S ð€í¿ÿðD —ðà  ð žðf" ðH ƒ ð0…‡Ìÿÿƒ¿Àÿ"ñ¿`ðÔ(³fð°b ðH “ ð6€üÓ…‡™Ìÿƒ¿Àÿ"ñ¿`ð% °ýÕ  ð<Ÿ¨database¡   ð°b ðH “ ð6€$ñ…‡™Ìÿƒ¿Àÿ"ñ¿`ð °ýÈ ð<Ÿ¨database¡   ð°b ð H “ ð6€ðï…‡™Ìÿƒ¿Àÿ"ñ¿`ð?°ýï ð<Ÿ¨database¡   ð°b ð H “ ð6€ìø…‡™Ìÿƒ¿Àÿ"ñ¿`ð2 °ýâ  ð<Ÿ¨database¡   ðÁ¢ ð H £ ð<€D…‡¿ƒ¿Àÿð"V›â ðUŸ¨ MySQL/hBase¡ ª ðf" ð H ƒ ð0…‡Ìÿÿƒ¿Àÿ"ñ¿`ðÇ» F†ð­b ð H “ ð6€Ð…‡™Ìÿƒ¿Àÿ"ñ¿`ðß p½ ð9Ÿ¨cache¡ 𨢠ðH £ ð<€„…‡¿ƒ¿Àÿðé èÕ ð<Ÿ¨ Tokyo Tyrant¡ ðµ" ðH “ ð6€Hþ…‡Ìÿ̃¿Àÿ"ñ¿`ð2€  ðAŸ¨atomic insert ¡ðÃ" ðH “ ð6€8…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðCp¿S  ðOŸ¨Lua¡ªðµ" ðH “ ð6€Ô…‡Ìÿ̃¿Àÿ"ñ¿`ð W  ðAŸ¨atomic search ¡ðÃ" ðH “ ð6€à…‡ÿÌ™ƒ¿Àÿ"ñ¿`ð p¿)  ðOŸ¨Lua¡ªðœ" ðH “ ð6€T…‡Ìÿÿƒ¿Àÿ"ñ¿`ð› ¼  ð(Ÿ¡ð­" ðH “ ð6€…‡Ìÿÿƒ¿Àÿ"ñ¿`ðÕm Ž å ð9Ÿ¨clients¡ð^b ðH “ ð6G*Hl†ýÿI‰ üÿ¿ÀÑÿð¾'Ë ð^b ð!H@ “ ð6GÍÿHZÿÿI`»¿ÀÑÿð% ¾Àðdb ð$H€ £ ð<´GN*HLVI)Ìûÿ¿ÀÑÿð ¾'” ðdb ð%H@ £ ð<G YáÿHÀþÿI`»¿ÀÐÑÿðÎ ¾Àä ðdR ð&H@ £ ð<ZGQÇþÿHXAIQÇþÿ¿ÀÑÿðåÌ YðdR ð'H £ ð<´GGTýÿH0jÿÿIGTýÿ¿ÀÑÿðãY 0 ð÷¢ ð)H ³ ðB€Œ$…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ðtÒ" ðwŸ¨11. inserts to the storage 2. inserts to the cache¡22 ¦øØø8X𪢠ð*H ³ ðB€Ø.…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ðIm O ð*Ÿ Î1. retrieves from the cache 0if found, return 2. retrieves from the storage 3. inserts to the cache¡*h     G ¦øØø8XðL ð,H@ c ð$¿ÀÎÿð"”£2ðL ð-H@ c ð$¿ÀÎÿðW ÿ •IðH ðH ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ððHÿÿÿÿÿÿÿÿð !Hÿÿÿÿÿÿÿÿð$Hÿÿÿÿÿÿÿÿð%HÿÿÿÿÿÿÿÿðH&HÿÿÿÿðH'Hÿÿÿÿð)HH,HðH*H-Hð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.UHÉð±3+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +îCï € 0 Z ðR ð@ðê ð( ð ð@ðx ð@ c ð$€¬G¿ÿˆðID<òðà   ð žð` ¢ ð@ Ó ðN€ŒjðÒ‚0*ƒðÒ„0*…¿ƒ¿À€€€ÿ"ñ¿`ðq* à ðÔŸ¨6#include #include #include #include int main(int argc, char **argv){ TCRDB *rdb; int ecode; char *value; /* create the object */ rdb = tcrdbnew(); /* connect to the server */ if(!tcrdbopen(rdb, "localhost", 1978)){ ecode = tcrdbecode(rdb); fprintf(stderr, "open error: %s\n", tcrdberrmsg(ecode)); } /* store records */ if(!tcrdbput2(rdb, "foo", "hop") || !tcrdbput2(rdb, "bar", "step") || !tcrdbput2(rdb, "baz", "jump")){ ecode = tcrdbecode(rdb); fprintf(stderr, "put error: %s\n", tcrdberrmsg(ecode)); } /* retrieve records */ value = tcrdbget2(rdb, "foo"); if(value){ printf("%s\n", value); free(value); } else { ecode = tcrdbecode(rdb); fprintf(stderr, "get error: %s\n", tcrdberrmsg(ecode)); } ¡¾7 G G ÿ™™þG G‚ ™ÌÿþG G ÿÿfþG G‚ ™ÌÿþG G ÿÿfþ|G G‚ ™ÌÿþG G ÿÿfþG G ÿÿfþG G ÿÿfþwG G‚ ™Ìÿþ G G ÿÿfþ°G ª¼                                                   .        %                             5    J                        <                                        ðÊ¢ ð@ Ó ðN€OðÒ‚0*ƒðÒ„0*…¿ƒ¿À€€€ÿ"ñ¿`ð  m &; ð>Ÿ¨Ð /* close the connection */ if(!tcrdbclose(rdb)){ ecode = tcrdbecode(rdb); fprintf(stderr, "close error: %s\n", tcrdberrmsg(ecode)); } /* delete the object */ tcrdbdel(rdb); return 0; } ¡´ÑG G‚ ™ÌÿþG G ÿÿfþjG G‚ ™ÌÿþG G ÿÿfþG ª–% ðH ð@ ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.FÉ0q}+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î ï€ 0  ðpðð°ð( ð ððx ð c ð$€Üô·¿ÿˆðȯÏf ðà · ð žðH ð ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÊDÉðΟ+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î}ï € 0 ”ðŒ`ð ð$ð( ð ð ðr ð  S ð€˜•¿ÿðID<òðà   ð žðr ð  S ð€p–¿ÿðD<—ðà  ð žðH ð  ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.fEÉ-n¢+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î ï € 0  ð°ð ð°ð( ð ð ðx ð  c ð$€ô¤¿ÿˆðwD<—ðà  ð žðH ð  ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÄDÉPÉ5+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î ï € 0 2 ð* ðð8ð ð( ð ð8ðr ð8 S ð€©¿ÿðID<òðà   ð žðr ð8 S ð€ì©¿ÿðD<V ðà  ð žð¢ ð8 £ ð<€H­…‡ƒ¿ÀÝÝÝËœ1ÿ"ñ¿`ðg Þ~ ð(Ÿ¡ð½¢ ð8 £ ð<€œ³…‡¿ƒ¿Àÿðƒ GÝÊ  ðQŸ¨ID:21 text: "abracadabra"¡CðÜ¢ ð8 £ ð<€¨·…‡¿ƒ¿ÀÿðŸ *J ðpŸ¨ a ab ac br¡ Aª,ðÚ¢ ð8 £ ð<€¬¸…‡¿ƒ¿ÀÿðŸ m à ðnŸ¨ca da ra¡ Aª,ð¢ ð 8 £ ð<€HÀ…‡¿ƒ¿Àÿð  : Ž ð¯Ÿ¨!- 21:10 - 21:0,21:7 - 21:3 - 21:5¡r"ACA CACACðr ð8 £ ð<…‡ƒ¿ÀÿÿË>Îÿ"ñ¿`ðœ g Z ¬ ðr ð8 £ ð<…‡ƒ¿ÀÿÿË>Îÿ"ñ¿`ðœ x k ¬ ðr ð8 £ ð<…‡ƒ¿ÀÿÿË>Îÿ"ñ¿`𜠈 {¬ ðr ð8 £ ð<…‡ƒ¿ÀÿÿË>Îÿ"ñ¿`𜠑„¬ ðr ð8 £ ð<…‡ƒ¿ÀÿÿË>Îÿ"ñ¿`ð –‰­ ðr ð8 £ ð<…‡ƒ¿ÀÿÿË>Îÿ"ñ¿`𜠘¬ ðr ð8 £ ð<…‡ƒ¿À3fÿË>Îÿ"ñ¿`ð¾ Ý Ð Î ðr ð8 £ ð<…‡ƒ¿À3fÿË>Îÿ"ñ¿`ð¼ û î Ì ðr ð8 £ ð<…‡ƒ¿À3fÿË>Îÿ"ñ¿`ð¸ üÈ ðr ð8 £ ð<…‡ƒ¿À3fÿË>Îÿ"ñ¿`ð± Á ðr ð8 £ ð<…‡ƒ¿À3fÿË>Îÿ"ñ¿`ð±  Á ð¢ ð8 £ ð<€°Ì…‡¿ƒ¿Àÿð” ~  Õ 𔟨 - 21:1, 21:8 - 21:4 - 21:2, 21:9¡X!A CACA CðH ð8 ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ8Š0º___PPT10‹ë.ûEÉÐ î¬ ï € 0 Ãð» ððSð( ð ððr ð S ð€¨ï¿ÿðD<—ðà  ð žðl ð “ ð6…‡ƒ¿ÀÝÝÝËœ1ÿ"ñ¿`ð6 ‹ =—ðr ð S ð€P)¿ÿðID<òðà   ð žð±" ð “ ð6€8ñ…‡Ìÿÿƒ¿Àÿ"ñ¿`ðC ôc ð=Ÿ¨ Tokyo Cabinet¡ð°" ð “ ð6€ó…‡Ìÿ̃¿Àÿ"ñ¿`ðà  $ã ð<Ÿ¨ q-gram index¡  ð®" ð “ ð6€ô÷…‡Ìÿ̃¿Àÿ"ñ¿`ðà ´Äã ð:Ÿ¨ word index¡  ð´" ð “ ð6€ˆÎ»…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðs  $“  ð@Ÿ¨indexed database¡ð³" ð “ ð6€(…‡ÿÿ™ƒ¿Àÿ"ñ¿`ðs ´Ä“  ð?Ÿ¨tagged database¡ðr" ð  £ ð<…‡ƒ¿ÀË>Îÿ"ñ¿`ð³ ´ Tðr" ð  £ ð<…‡ƒ¿ÀË>Îÿ"ñ¿`ð³ „$ð²¢ ð  £ ð<€0…‡¿ƒ¿Àÿð³ ´ Õs  ðFŸ¨Character N-gram Index¡ð©¢ ð  £ ð<€ …‡¿ƒ¿Àÿð³ ô+s  ð=Ÿ¨ Tagging Index¡ð¶" ð  £ ð<€äλ…‡Ìÿÿƒ¿ÀÎÿ"ñ¿`ðc  ôƒ  ð<Ÿ¨ Applications¡  ðH ð ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.xEÉ ‚¸£+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î[ï € 0 rðjÀð#,$ðð( ð ð$ðr ð$ S ð€ä¿ÿðID<òðà   ð žðx ð$ c ð$€¼¿ÿðD —ðà  ð žð¢ ð$ £ ð<€´…‡ƒ¿ÀÝÝÝËœ1ÿ"ñ¿`ðMm i ð(Ÿ¡ðžb ð$ “ ð6€#…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðvÌ¿à ð*Ÿ¡ ðžb ð$ “ ð6€@ …‡ÿÌ™ƒ¿Àÿ"ñ¿`ðvìßà ð*Ÿ¡ ð°b ð $ “ ð6€8)…‡ÿÌ™ƒ¿Àÿ"ñ¿`ðÐÜÏ: ð<Ÿ¨ profile DB¡   ðf" ð $ ƒ ð0…‡Ìÿÿƒ¿Àÿ"ñ¿`ðë È f ð´b ð $ “ ð6€-…‡™Ìÿƒ¿Àÿ"ñ¿`ð¡ # è 8  ð@Ÿ¨inverted index¡ ð²b ð $ “ ð6€t0…‡™Ìÿƒ¿Àÿ"ñ¿`ð¡ Û8  ð>Ÿ¨ attribute DB¡   ð‡¢ ð$ £ ð<€ø5…‡¿ƒ¿Àÿð¾ È ¥  ðŸ¨indexerðf" ð$ ƒ ð0…‡Ìÿÿƒ¿Àÿ"ñ¿`ðž¡â ðf" ð$ ƒ ð0…‡Ìÿÿƒ¿Àÿ"ñ¿`ðpGˆë ðf" ð$ ƒ ð0…‡Ìÿÿƒ¿Àÿ"ñ¿`ðCì-¾ ðf" ð$ ƒ ð0…‡Ìÿÿƒ¿Àÿ"ñ¿`ð‘Ò ð´b ð$ “ ð6€L9…‡™Ìÿƒ¿Àÿ"ñ¿`ðËì±b  ð@Ÿ¨inverted index¡ ð²b ð$ “ ð6€l=…‡™Ìÿƒ¿Àÿ"ñ¿`ðËߤb  ð>Ÿ¨ attribute DB¡   ðˆ¢ ð$ £ ð<€ð>…‡¿ƒ¿Àÿðè‘5Ï ðŸ¨searcherðf" ð$ ƒ ð0…‡Ìÿÿƒ¿Àÿ"ñ¿`ðšÈ ðÊb ð$ “ ð6€F…‡™Ìÿƒ¿Àÿ"ñ¿`ðP# è ç ðVŸ¨ TT's cache¡   ªð²b ð$ “ ð6€PJ…‡™Ìÿƒ¿Àÿ"ñ¿`ðPÛç ð>Ÿ¨ social graph¡   ð†¢ ð$ £ ð<€(…‡¿ƒ¿ÀÿðmÈ T ðŸ¨mergerðdb ð$@ £ ð<G0*HÑGI°„üÿ¿ÀÑÿð8  ÖÐðdb ð$@ £ ð<G0*HÁžI°„üÿ¿ÀÑÿð8 ùÖÐð^b ð$€ “ ð6G*H±qIêzþÿ¿ÀÑÿð— è ìm ð^b ð$€ “ ð6G*H±qI`Dþÿ¿ÀÑÿð— Ûßm ðd ð$ £ ð<Gÿ¬þÿHÿÿÿÿIÿ¬þÿ¿ÀÑÿðçùù¡ ðÁ¢ ð!$ ³ ðB€ŒR…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ðX S ðAŸ¨dump profile data¡ðÉ¢ ð"$ ³ ðB€V…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ðs 6È3  ðIŸ¨copy the index and the DB¡ðÅ¢ ð#$ ³ ðB€@Z…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ðË› .‹  ðEŸ¨copy the social graph¡ð²" ð$$ “ ð6€ü^…‡Ìÿÿƒ¿Àÿ"ñ¿`ð‘Ò ð>Ÿ¨user interface¡ðjR ð)$€ ³ ðB´GÆñýÿHthIÆñýÿ¿ÀÐÑÿðŠé ‘šðdR ð*$ £ ð<GeþÿH­-þÿIeþÿ¿ÀÐÑÿðØ ²ðµ¢ ð,$ ³ ðB€Øc…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ðoFÑ/ ð5Ÿ¨query¡ðµ¢ ð'$ ³ ðB€°g…‡¿ÿÿ™‚š™ƒ¿Àÿ"ñ¿`ðOŽ  ð5Ÿ¨query¡ðH ð$ ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ðàð $ $$ð $ $$ð $$$ð $$$ð $$$ð$$$)$ð$$*$ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.{Eɰ—óº+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î›ï € 0 ²ðª ðDðBð( ð ðDðx ðD c ð$€¼t¿ÿˆðID<òðà   ð žðš¢ ðD Ó ðN€H“ðÒ‚0*ƒðÒ„0*…¿ƒ¿À€€€ÿ"ñ¿`ðzq*  🨔#include #include #include #include int main(int argc, char **argv){ TCIDB *idb; int ecode, rnum, i; uint64_t *result; char *text; /* create the object */ idb = tcidbnew(); /* open the database */ if(!tcidbopen(idb, "casket", IDBOWRITER | IDBOCREAT)){ ecode = tcidbecode(idb); fprintf(stderr, "open error: %s\n", tcidberrmsg(ecode)); } /* store records */ if(!tcidbput(idb, 1, "George Washington") || !tcidbput(idb, 2, "John Adams") || !tcidbput(idb, 3, "Thomas Jefferson")){ ecode = tcidbecode(idb); fprintf(stderr, "put error: %s\n", tcidberrmsg(ecode)); } ¡r• G G ÿ™™þªG G‚ ™ÌÿþG G ÿÿfþG G‚ ™ÌÿþG G ÿÿfþ‹G G‚ ™ÌÿþG G ÿÿfþ'G G ÿÿfþ G G ÿÿfþ}G ªä                                                        D        !    *                    !    #                                 ðè¢ ðD Ó ðN€à¤ðÒ‚0*ƒðÒ„0*…¿ƒ¿À€€€ÿ"ñ¿`ð}m &i ð\Ÿ¨R /* search records */ result = tcidbsearch2(idb, "john || thomas", &rnum); if(result){ for(i = 0; i < rnum; i++){ text = tcidbget(idb, result[i]); if(text){ printf("%d\t%s\n", (int)result[i], text); free(text); } } free(result); } else { ecode = tcidbecode(idb); fprintf(stderr, "search error: %s\n", tcidberrmsg(ecode)); } /* close the database */ if(!tcidbclose(idb)){ ecode = tcidbecode(idb); fprintf(stderr, "close error: %s\n", tcidberrmsg(ecode)); } /* delete the object */ tcidbdel(idb); return 0; } ¡&SG G‚ ™Ìÿþ G G ÿÿfþZG G ÿÿfþöG G‚ ™ÌÿþG G ÿÿfþjG G‚ ™ÌÿþG G ÿÿfþG ªÀ;        &% ðH ðD ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.FÉ0q}+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î ï€ 0  ð`ðXð°ð( ð ðXðx ðX c ð$€èÇ¿ÿˆðȯÏf ðà  ð žðH ðX ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÊDÉðΟ+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î‰ï € 0  ð˜pð\ð0ð( ð ð\ðx ð\ c ð$€Î¿ÿˆðID<òðà   ð žðx ð\ c ð$€ÜοÿˆðD<—ðà  ð žðH ð\ ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.fEÉ-n¢+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î ï € 0  ððdð°ð( ð ðdðx ðd c ð$€ìÙ¿ÿˆðwD<—ðà  ð žðH ðd ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ÄDÉPÉ5+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î ï € 0 7ð/ ðhðÇð( ð ðhðx ðh c ð$€`ç¿ÿˆðID<òðà   ð žðÍ¢ ðh à ðH€hðÒ‚0*ƒðÒ„0*¿ƒ¿À€€€ÿ"ñ¿`ðzq* ´ ðGŸ¨‡#! Introduction to Tokyo Cabinet #c 2009-11-05T18:58:39+09:00 #m 2009-11-05T18:58:39+09:00 #o mikio #t database,programming,tokyocabinet This article describes what is [[Tokyo Cabinet|http://1978th.net/tokyocabinet/]] and how to use it. @ upfile:1257415094-logo-ja.png * Features - modern implementation of DBM -- key/value database -- e.g.) DBM, NDBM, GDBM, TDB, CDB, Berkeley DB - simple library = process embedded - Successor of QDBM -- C99 and POSIX compatible, using Pthread, mmap, etc... -- Win32 porting is work-in-progress - high performance - insert: 0.4 sec/1M records (2,500,000 qps) - search: 0.33 sec/1M records (3,000,000 qps) ¡ˆˆG ª€^!Ti+  ð:² ðh 3 ð€€Að› ðH ðh ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.FÉ0q}+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î|ï€ 0 “ð‹Ðð0ð#ð( ð ð0ðë¢ ð0 £ ð<€¸ï…‡¿ƒ¿Àÿðf Jy¹ ðŸ¨3innovating more and yet more... http://1978th.net/ ¡04 ' 'ðH ð0 ƒ ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.ˆEÉÐÂÌ+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +î÷ï€ 0 ðÐð¬ðžð( ð ð¬ðf² 𬠃 ð0Aƒ¿Àÿ?"ñ¿`ð€àðH 𬠃 ð0ƒ“ŽŸ‹”Þ½h¿ÿ ?ð ÿÿÿ€€€»àã33™™™™Ìˆ‘Љº___PPT10‹ië.¬DÉðþUô+Dñ='ñ Uÿÿÿÿ=ñ @Bñ +r° @ ËGÛL"ðâ·Y<\Pâ¬WG^²€¥ºg*/,´.T †2 Å0iÄ5@XÂîÆÉÕ}ß:À^TvÿÈì ´àõœžGÚƒ”% 6 õs áEÅ1þÿà…ŸòùOh«‘+'³Ù0ð `h€” ¨´ Ø ä ðü¤Tokyo Products hirabayashi hirabayashi66Microsoft Office PowerPoint@PolþÎ@PÌæ†£È@à­o¨”øÊ Gäÿÿÿÿ‰g  ê ò ò &ÚWMFCŸ¸¸lÿÿÿÿÿÿÿÿ x ¸ EMF¸\@âèF(GDICÿÿÿÿ x!b $$€=€='3% % €V0ÿÿÿÿÿÿÿÿ xøÿøÿøÿxø xø øÿøÿøÿ% €% €$$€A€A" ÿÿÿÿFGDICF(GDIC "•OFGDICRpðÿÿÿ€2HGSuRñ‚Ò‰ŽÿŸÿoÿŒÿŸÿSOL£€ÒHGSuRñ‚Ò‰ìx¸ ÀÍ(ÔÕ:ðÿÿÿ€2HGSuRñ‚Ò‰ 0 hý¸ü²àÑhÅO0ÀÎúõÒ0˜œ5×0(Ôü²p¸è¸DÁ¢€«ÜÎôÒ0L«ü²øÎÜóÒ0,§¤¨dv%    T¨(‹7ÈAÈA6LÿÿÿÿÿÿÿÿlIntroduction to        ÿÿÿT¨'Š6ÈAÈA5LÿÿÿÿÿÿÿÿlIntroduction to     % €( Rpðÿÿÿ€2HGSuRñ‚Ò‰ŽÿŸÿoÿŒÿŸÿSOL£€ÒHGSuRñ‚Ò‰ìx¸ ÀÍ(ÔÕ:ðÿÿÿ€2HGSuRñ‚Ò‰ 0 dý¸ü²àÑhÅO0ÀÎúõÒ0˜œ5×0(Ôü²p¸è¸DÁ¢€«ÜÎôÒ0L«ü²øÎÜóÒ0,§¤¨dv%    T <ŒKÈAÈAJLÿÿÿÿÿÿÿÿhTokyo Products       ÿÿÿT ;‹JÈAÈAILÿÿÿÿÿÿÿÿhTokyo Products    % €( F(GDICdbœrFGDICRpúÿÿÿ€2HGSuRñ‚Ò‰ŽÿŸÿoÿŒÿŸÿSOL£è¢8¹sæ0L£\©¢ü²¼Îü²„¤8¹„¤ü²tα†æ0¢hÎäÏÌ·ÿÿ¤ÎDÁü²|?ÀÎ5×0(Ô|?{® 0i¢DÏü²DÏt·ï0ü²(Ï8Ï,§\©dv%    ÿÿÿTlgeujÈAÈAgjLÿÿÿÿÿÿÿÿXMikio¤ % €( Rpúÿÿÿ€2HGSuRñ‚Ò‰ŽÿŸÿoÿŒÿŸÿSOL£8¹„¤ü²tα†æ0¢hÎäÏ4θ•|ØžÏA•|H]•| àžàž|?{® 0i¢DÏü²DÏ5×0(ÔDž8Ï ,§\©dvˆ“àÎ^ZïwàΘP%ˆ“ ô ôÎDβ !˜dv%    ÿÿÿTxe˜jÈAÈAxj LÿÿÿÿÿÿÿÿdHirabayashiÿ % €( Rpüÿÿÿ€2HGSuRñ‚Ò‰ŽÿŸÿoÿŒÿŸÿSOL£€ÒHGSuRñ‚Ò‰h¸ÀÍ(ÔÕ:üÿÿÿ€2HGSuRñ‚Ò‰ 0 xý¸ü²àÑhÅO0ÀÎúõÒ0˜œ5×0(Ôü²p¸8¹DÁ¢¨«ÜÎôÒ0L«ü²øÎÜóÒ0,§\©dv%    ÿÿÿTTsluoÈAÈAsoLÿÿÿÿÿÿÿÿP<K % €( Rpüÿÿÿ€2HGSuRñ‚Ò‰ŽÿŸÿoÿŒÿŸÿSOL£ÿÿ€2HGSuRñ‚Ò‰ 0 xý¸4θ•|ØžÏA•|H]•| ôàžàžü²p¸8¹DÁ¢ÜÎôÒ0L«5×0(Ô¤ÜóÒ0 ,§\©dv¨«ˆ“àÎ^ZïwàÎU%ˆ“ xôÎDβ !˜dv%    ÿÿÿT´vl•oÈAÈAvoLÿÿÿÿÿÿÿÿphirarin@gmail.com % €( Rpüÿÿÿ€2HGSuRñ‚Ò‰ŽÿŸÿoÿŒÿŸÿSOL£A•|H]•| ôàžàžü²p¸8¹4θ•|ØžÏA•|H]•| õàžàž¤ÜóÒ0 ,§\©dvˆ“àÎ^ZïwàÎU5×0(ÔDžôÎ Dβ !˜dv¨«ˆ“àÎ^ZïwàÎ4W%ˆ“ ôÎDβ !˜dv%    ÿÿÿTT–l˜oÈAÈA–oLÿÿÿÿÿÿÿÿP> % €(   x ü3-úÿÿÿ-$ÿÿÿÿÿÿx x ÿÿÿÿÿÿú-üÿÿÿ-'ÿÿûðÿ€2HGS‘n‰pŠpÎ߯Ìß‘Ì-. 2 6Introduction to    .. ÿÿÿ2 5Introduction to    .û€System-ðûðÿ€2HGS‘n‰pŠpÎ߯Ìß‘Ì-. 2 JTokyo Products   .. ÿÿÿ2 ITokyo Products   .-ðûúÿ€2HGS‘n‰pŠpÎ߯Ìß‘Ì-. ÿÿÿ2 jgMikio¡.-ðûúÿ€2HGS‘n‰pŠpÎ߯Ìß‘Ì-. ÿÿÿ2 jx Hirabayashic.-ðûüÿ€2HGS‘n‰pŠpÎ߯Ìß‘Ì-. ÿÿÿ 2 os<.-ðûüÿ€2HGS‘n‰pŠpÎ߯Ìß‘Ì-. ÿÿÿ!2 ovhirarin@gmail.com.-ðûüÿ€2HGS‘n‰pŠpÎ߯Ìß‘Ì-. ÿÿÿ 2 o–>.-ðþÿÕÍÕœ.“—+,ù®0 ˆ¨´¼ÄÌ Ô Üä ìôü  §¤‰æ–ʂɇ‚킹‚éûå"' (Arial‚l‚r ‚oƒSƒVƒbƒNHGS‘n‰pŠpÎ߯Ìß‘Ì ‚l‚r ‚o–¾’© Courier New •W€ƒfƒUƒCƒ“Introduction to Tokyo ProductsTokyo Products#Tokyo Cabinet - database library - Features ƒXƒ‰ƒCƒh 5TCHDB: Hash DatabaseTCBDB: B+ Tree DatabaseTCFDB: Fixed-length DatabaseTCTDB: Table DatabaseOn-memory StructuresOther Mechanisms Example Code!Tokyo Tyrant - database server - Features ƒXƒ‰ƒCƒh 15Asynchronous ReplicationThread Pool ModelLua Extentioncase: Timestamp DB at mixi.jpcase: Cache for Big Storages Example Code+Tokyo Dystopia - full-text search engine - Features ƒXƒ‰ƒCƒh 24Inverted IndexLayered Architecturecase: friend search at mixi.jp Example Code.Tokyo Promenade - content management system - Features ƒXƒ‰ƒCƒh 31 Example Code ƒXƒ‰ƒCƒh 33 ƒXƒ‰ƒCƒh 34 Žg—p‚³‚ê‚Ä‚¢‚éƒtƒHƒ“ƒgƒfƒUƒCƒ“ ƒeƒ“ƒvƒŒ[ƒgƒXƒ‰ƒCƒh ƒ^ƒCƒgƒ‹"ö#_À‘ã™ ôhirabayashihirabayashi  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqþÿÿÿstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}þÿÿÿ€‚ƒ„…†‡ˆ‰Š‹þÿÿÿŽ‘’“þÿÿÿ•–—˜™š›þÿÿÿýÿÿÿýÿÿÿýÿÿÿýÿÿÿ¡þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRoot Entryÿÿÿÿÿÿÿÿd›Oφꪹ)èþÿÿÿPicturesÿÿÿÿÿÿÿÿÿÿÿÿÂãCurrent Userÿÿÿÿÿÿÿÿ”SummaryInformation(ÿÿÿÿ~ PowerPoint Document(ÿÿÿÿÿÿÿÿÿÿÿÿr½DocumentSummaryInformation8ÿÿÿÿÿÿÿÿŒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿtokyocabinet-1.4.48/doc/index.ja.html0000644000175000017500000002256712013574114016452 0ustar mikiomikio データベースマãƒãƒ¼ã‚¸ãƒ£ 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ã®å…„弟プロジェクトもã‚りã¾ã™ã€‚


    tokyocabinet-1.4.48/doc/benchmark.pdf0000644000175000017500000010124411106701741016477 0ustar mikiomikio%PDF-1.4 %Çì¢ 5 0 obj <> stream xœ­]]od·yÆŽ´;³ZA¶W²÷É3»¤‘vÄï9›&)ŠAnâ] ^%M ãX@Ô‹ü€HóËÄu _´@Óí_ìËoòÎÑ |°¬‡ïKò!ÏC¾$gü³)Z6Eæ_øãGŸí]}"§?ý§=´äJ+É?nºG¨žIù’²)AM™˜ÞþÃÞO ›äT#1íþ²!l³aDÄT(Õç´>ùžÿ¼¶Ç–X!mBþ÷>›~÷-0USL–LMßþd/B˜X3žr½Tj*[R§o?Û;»7ÚÙ½ÿ`yúìñó=ÿÚûUL@‰)æ øœjLÜ|†¡¨Rh:QÕ¯?~þè óy«W±@(§ªʉ:i:çÁhÂÈ9>~òÁ»5ŠÚ’(ŒEÑ›1!Òt†¢ÉYÊòÁôüЙOŸOK¦_QýLÆ™ïѳ0SÈ%J9ÌÄé‚©%ÌÍ.ËÁë›gã—Ç;#3ûÌNæ'§ÙÇLyÕQ3û]Ê…Fz÷˜ð•7çKe[)‰£¼:;Y¯ÎåãV•45¢9M Mï>MF™†Kžóùê¼ I¼>IB(ÎH:99çá(jÀ¬¤x¾¸8½œ_,Vg—Cj– áeô ^e›©’Š¥P>Óáñ.¼ß¯nÇ/Çû£ÕÍ{ãñr41ú¼<¹ºªT(ž ÔÃ(PFZ  ؇1µ‚¨ªÎÄÑÉ1rt0r îCq êÌH‚:^TÄ9z5ŽJÞy0†^œÃS Ú\\ ùbHiX(zõ¿¿±4 ‡¼>ÓˆÔ…yz2«©Þ'¹* ªäš•ªŒ ©ZÆð›]ÖU9:FŽ&ŽÞ}0ŽA•Ix÷œÎZ¢$¼ éuH:9÷áH*±Ä%ÉK˜Õ/V0‚æ:oÂOyõ?ÝXœ˜ÆÆÓ×B¯nW·£uó'ŒüEE©òåJõ0*–î¥RCÂ@J…V„!†OÙ Ÿ+5qtÒŒŒƒ{ɱ'Ð5ÁhoüÕUž5Ь`àW]è±^Ù¡†&E÷¡žCzþ ³‹Å¥ùY )s„!ÐpUÐeŽÐó™ÞgëóBÛ‚˜.Â…² ÊfZe翺®‘I7 Ãsvy‚Q}Ýš8z)ã\Ù‰£wŒePgÆr~2›Ï+ ƒH1(çÂŒŒœópý #xyy1›_]\-†œ{Í8“!Žz÷ÞFšä°4!šóãñQ¡K;×âÊ\ËtLyÉ[ÁTLf®5´ÃPº:YÌêkÕÄÑK°ˆ¤Çà>G¯Èœ$ôâÕ%ªH2r ,©HÉ;ÆÐI2gx qÔbnƒŠRB¤îUÿlSQJ ÿñ¢<ÞyÁýÁ¬ Æ;&¼ÏÔizµé3VRFq¢V '4Y¤e~Z_ $’Nެˆ¤Éà>É ÎË«ö»Ü Ž‘Täè!*"©ázuæ ÏWgV› óû|P… wîmªP!—:>‚‰ýéñ·6i@鼞úºBï.ó®ÉB2Çü£ûÇû«›—é:öêÖÝÇ6ɫۃÕÍîøh÷ðhl¯V¼Î:gu ½/°ÉÈœÿYt½ºÙêæKgè8z:¿œzE¼ýÁÞÙ×'ÇÏÖªÐÁÄ&Z9ú|øw3í¢nÿünƺžýÑêv:g|tä(@Eû)óù)¼’ì,gŒæ¾ùêæ¡Ù·üæã£Cß€ÑØærƒ!¶h’.Å˶g ú P:ÆðóWž­ÿ7ûöE'mQI›ûäÄr{t¸cÛõz<C'ߎ&û9‹9ööªm²>Þ}4~šúÒõÈëÑäƒÉƒñÎîÈ–ü¡y’&å;«›oï~su³8Á'3lÚ?Ù«'?šÇ‡øŠ=€çLÌýï>¼Ÿç; o?ì¬n¾{8†~ÙµOlq2§ý­Õͳø]RXä<þà=¨»¼>¯1qSžV2ÃMÀJ²âú|Ä]Ÿ×(Þˆ{vðàõ±é—ãƒè–åî®=“†@³ÚÅ׿¦Ùˣ㗻¦Ûn5Õ_Â\Á@œ2)â*–í‡OCøf4½ÊÛñ¢Ï<ܳبpß/m燘O.Œu,Ÿ?µºÙÃÛ90e¼åÃ[›„Ýñ¡9%uÒéØÔV29x8‚Ìä¾ùýÀ>m*¦¼Y&ã|”Ù"Tº·–Ýp¡ð·Ú}(†kÑ1ñð‰L]k㯵i¬×Ùar­áxUž X!rë‘ÙÜ•åªÍß­Ûܵ½ªÍßtªÛÜe“ªÍŸú×ml=ôU·±õ\üönÕæ·Òê6´ž‹ë6´ž‹_Õm¸Ê›%¯ûˆ–šbŒHxÈSÉÓG´àŸƒë÷áçS/ÛÜ×È…cãû=·+[,ÝÄô`Qãf>Þõ¾»I¬[BéõÆN.R ÌP–§ 8æ±Þ­z:%têЦW¢ ¢cëݪ£SB§Œ1/š "«ÅÁ˜)¸·ê©•Â+½†‰¦¥Ñ,¯ÍÂT›wo×V)…Öjcç^‹ ©>›¡][·”nM‚éÜ`Y“OH5٠횺¥tkRD”mWDæ5Y˜êñîíš*¥ˆJš› ¹›ÇÑ‹`Å:P¦EDtN´ŒÄˆÏ&X?øÏ>Ùæudv”a¨³‚KHhQ$÷ÂÅpnbBNšt:¨RJ·&7)˜gS˜šbœÈê >ÇÀDí<‰¦ŸÚ3ü|ò=xhy?A£ A´›`Ã.;—iõ™CÔ¾_ZÆä•ÂŒœÜKÈ3?¥/2®=~%³p b¤ 7 ¥ v÷²ÃöÉ0;éô뺕ÜP8g£N>¡‰ "ÜŒì:·Á+ì„°/[Q/»Š[ÁN¤=!F#lD¬d†Ø̈]"ÄI¾‡\Õ³ä‡Ã& V:ÂÆCR¼7/VêüxXöIv¿ä)—d A.Æ¿´R!¡I ¤÷’dV$˜Ø5IÅŠ_Éú¡Á`õPã“å°ˆ¸—™›°½«ó*~³t'Ùx§[±!–#â>~Œ[‰©ôò«ù•ü8ñk¸Üš(QVRBª»#5Çòu/ýbÄlâ'ÜDŒËñ6t©᪟l×­¤JýÀR\d¸ Þ³åÊDn?°°vKÒ/Š[A+?°˜]¥*?° _d|Þ†&·šw<þŠ›¡™¯eʽ·¸¨z„ ›’yã¶ðƒoí¾ñ›ïÁžoÅ«Û6Ö|ÝXãfwphí~[Ÿ°E}Ê=këã6–£G¾Ïlín;8ÚóÝak›¸Ñ£ÜÕ5>q×4ø´¶Q­ëàè±®¿Ó&°_^ÇÝÐŽFÕ(Éí”´ìöø7Ú=ÊíLåv¦Úv$r;-;9?r;Éùy”Ù™Êùy”Û©òëk§ªÕ?T‹ÌîQnwq vÞî_Nç=œÍ¿I3&Êìåv’óó(³c•óó(·³œ_öü‘=9ßè«)`YÂíM“pê®ý©;Ä—æÔÆïwvý‹óWÒ̽Lœ]ÿ«ù›Oxž]ÿ2KÿU–þë,ý7Yúo³ôßeé¿ÏÒÿpþJhsMB]Póß¿ý¾£Ë–Œu/,ÀÄ lqºézý—ëÏí ±dRÓé+Œì킃鿯ÿíúO×ÿ™]-b…ý¿î°ÿO²¿Ñqmî-æÿ‘`–é˜ÿbÌësÌh ï­daýSÌ ³“ šfî{‡’?¿þpù üþ#ðüÜ×R)'wý³i²ï- Dq­»6-¸í ¯u…mͦ%—α\¹”ZcQiÝ¿_yý×_87{Í"qct<Å!œOµ#Šb>è7µKG¤µ=á‹{3Z¹ÃŸ7ã„J_(3XÝþÀÚQºmElj(^ÖëÜkz›ñ^¿eäù® ©ÙÖ l¸þ_èã/¡+¿¸þ9üþžéù%W×é3x¾Ò§ð|?ÄïÞqýÐ>ké[Å„V Aú»¥íðf£+Gwõ† tâ„ògè‡?‚»}Ñ ¢¸UAÌWöQáoUÐt­Æ]ÿɬ¦*â= i´YSC¢YB¡ˆ‹(¼¿•CÖ7a¢]BËËߣà,ó”åiÌ\?˜H¹ëçÉ^;–H…×N%RÞÊ¡D2VÏ$R¹ýg©˜;Ž$:ÝtG±ÀÝ­–lH÷®ãˆvtO#úw}¸÷ÿ4gÚendstream endobj 6 0 obj 5687 endobj 4 0 obj <> /Contents 5 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R ] /Count 1 /Rotate 0>> endobj 1 0 obj <> endobj 7 0 obj <>endobj 10 0 obj << /Registry(Adobe) /Ordering(WinCharSetFFFF) /Supplement 0 >> endobj 14 0 obj <> endobj 15 0 obj <> endobj 12 0 obj <> endobj 18 0 obj <>stream xœ]A E÷œ‚”ÚšÒÄ°Ñ Q/@a0,J Ö…·—Ð…“0É<þOf~³?ŽÁ¯¼¹¤ÅÜ`åΛ๼’>ÁÃÖn¸õf­u3ëÈšýIÇû;Ïpe>ëšk+‰´Åc Ϩ $Àv"—Ú¹\ŠA°ß²˜&WÕU»¡É‘ì ™‚H5 D]Q’ ê[BZ¨Ú3 ê m قȨ'DÛjBŽÐ@‹WÄ0ïýܼR‚°Rj” ¦áü‚KDÏ}”Äuˆ endstream endobj 11 0 obj <> endobj 8 0 obj <> endobj 19 0 obj <> endobj 20 0 obj <>stream xœc`£`Œ‚Q0 Fe€‘AòÿžÏíŽQ0 ˆñ¢ endstream endobj 13 0 obj <> endobj 16 0 obj <>stream xœí—[lWÇ¿ÙÙ3·3÷ÙÙ‹/k¯c»ö:m³ÞݤI·‰Ý\§-H•Ú4éAK”VPP4Š´âNT@ n@ÝMEET!„xB}'„P+@¼TB¼ ÍwfÇ©Ñ"xþïÏ¿o¿=;—ïœ9gfMYôiÒ)8÷ñgZôWú·|‹ýÁcòÕ#¿=C¤­•>òøG?ùå¯Ê:‘S~âѳçý }š´—þÆý'¸A{ký7DúOùóÌO>óìhûa‡è_}ôcçÎŽ>}ç÷Ož}öÂgô:oÿ;nl]¸øè…7..¿ÃŸÿÁç|˜4úÿ^1%J9V)ã˜QM–BŽ!E]’=ò9 2óQ°¯ÇmGØÜÌéðE¾=©3ÕZ¶~¬5¤NO 4‡ÚÙ3ZjkTZm¯=|xiXâ\_ݽ4Ô91‹Æ2ç–j¬pR) Î Õ(8qŠF“sW5ZœdE£ÍyM5:œ$E£Ëyª%'¢Ñ㼩}N¼¢1àÜW!'aÑq©Æ¸3Ôªaoi˜t´àWKôCê­ÚÑZa»­µ×>pzOsêÌÒ0ëÐÍMµNkM ɰu–Y}z|ô©Ñi k‹ÃŸ£Éi}ñÚæŸ9ë´h¶gi8Þ™ZNt®jc«‡®–Æ9èÊ“*-Æ1ÍÁls°f8Ø;88³Ü9ržƒw C°È!ìpˆ–8Ä;9$·rHoãP½C¶‹C­Ë¡¾Ì¡ÑãÐì¯òUœäRª”ݪ”=ª”;T){U)ûT)ûU)wªR¨RVT)w©RîV¥T¥R¥¬ªRÖT)÷¨R«RލRŽªRŽ©RŽ«RN¨RîU¥œT¥¬«RNq)”¯‡77ß%6o^!w UËÉw´·é¶Q¯QYÍW9vVNæž:}MÛ¼4¤šù»ö™æ.9vM£ûOµ¡öâ™W{Ë´øê®Û9ܺ“Cg‘Ã-ófwphOshMr£Å×i‚Æilµ½úð¡×iŠÏ:Yä3Ô¦é"Ÿ£YÚQä t Íùuh±Èo£[ig‘wiÝ^ä}êÑrž¿º~’Ïzâ8‡£G8ܳÆáÐAw­p¸s?‡½wpØ=P…í¡Ý4(²öÒE~€î¤ýE~7ÝE+E¾J‡è`‘¦{h­ÈÑQ:Rä÷ò/òS´N'óœGø<óMF'ôºéùÁùÁ@] ómî{ J‡‹Úkz¥\)‰z­TÖË%}!kÚ¼}ݶí¬Ö¬VÕ>‚î§OÒ9>–Tû𶜖ü…A7m‹‹ÏTâ;V‰>AWù¾Ö¦J¾Õ`yÐïîʪãZ"ŒöôÜl¯/ þU»»ýýÚòÜlÛJ¼4õ’Q\2¥gZ¶)¥iŸë$ž—t’Eõ¶xÒ”3YÛ3ÕÛŒ4UMMžLë|>»èG¹T)åý¨C”œ…¾*®Ç]Ù58¨¹Ù„å™?r^¾äš¼¯äòŸæ›æôV¢ JÚÂVÅ)œ£Ñ0=Uì´0Ò0QUJßóMSTŽ=VòëAh–i8ÏLRʸ…e™fÚQ3Š×4 §–¯ɪóö)Ék®5ê UóϲjM«Þxúêè]Û~­›£¦êXÔz[©*I„¾Œ«Òµ…i ×óê©çe–°¥É/[•fš†m SûTæKé&‰-4]¸IÕãWl Éß™!nhš¾4 aFùõÔéY®Õæª'Uµ+¼›©9šÔÊ\òŠ­Ü,Y¥²¦æT>ÜÑ=ž¥ivðàÒÒð©¸ÓIÙÖ÷ Z¼aåû¶Sëôz×U—çºÕw;œ÷²Ýï ê<¨•4é'Ò‹¼4‘RÏJÜAW8–aº–QJ´ÊF¬Ç‘®»ÒkNGž”˜{ä4ê–azYçR‰Ÿ·]ä¾µxæj¿ùöÖuîn¨GMÝí× íÕÜ(vef–eeÃ’‘ënùÑ¢p\aòù,!¾Ps]™x†aó‚+õ'&׺žpâšË- ¾Tùø„<x|Œ­ñ©p:ÁK2|è¶êmG6ª_»ÂÛ¹¼ý3\ÿÝη‹{ æNºmòôxR z³ÿ¡ ÆMŸCÏMSž a™®'ëué¹Ü áìàù!,×à¨fËí–_÷\)«¡Ë ¯dò¼âÑæ¥ìšüQ8Á„0}φéû¦8ÎSËç™hqTý}¿k°Ux×(Š3ÞëDNÉ´êű#3Í*›–Ê'ðí`Þ°M!\Ãq„õ•È‘ér¯êñ›´Ê"X4*Ž4o1ìzÂ:Aݪ&ù×$Ô¬/®_G¾ ´nÖžk žÞú¡äP-½;í7–õ—_^^þÂ*÷GϯßWøúEüô™§N¾¾)Žbµ¾W¼p2šŒù›‰d>1´…®1kŽÏÍÎë_â›ý¢6H¯÷R¼›†ë‰t5á]ß­Ìú¼æyþïîú|Q,ƒwàð,nTõÝ+=aJÇ4ï7-uc=‰Õ¸;Ÿ;7ŒzÖž-fþÖw·†Ý«Ça¤ÕJŽF^É8q¼š‡­‰¸þ|=£±Ôq½hÇŽH:^c:ãú¢O5&_ç1á{4ÍŒFı|Dl˵]‡Ûùöñsž]Iœ–¶Ä #î›àOîËfb7æ 6Òjg!‹¯\rãé™È»¾~žçs941:“î裱W×Õ(‹²mò`7œÂð©:õàÓq/ÞÆ'á­/óq,jDZFDZ·ŽSÞvŒvòôƒ‡gñîøÄhÿ€Ushîñcõ±ü¹To6š<“Š{ý{ û@=²ÒöÜhèƒ,˜ˆ­¦9NË(–Yè8¥ñåЭ¤þ|–Æj’ÑìL,Ç âÓa˜ÔòZÔ=¼¿mL,ݾyLø;C¿ÞŸ^·×¶–‡ÿ6ªÏU«ÏúñÖ?å㸞d«][= Ý,ÔŠö\ÚŽ&wîœÌv;³K—~òY~míÿå|L·öê׆%ŠýóóÎõõŒÿ6._þÉåËjþø›ÿ¢Ò+¼ÿŽë³un½û4Vƒ6{ý¶‘‰bØ>lUýªúqì‡ÕxÓZÊk.Ž9IDS6‚É™Ð÷ÃŽOÕÒ´¶0Ûˆ½Äæ„òûÏL^'¯ðf šÍéËÑLpCÜÎ3ágõZCÛ¾„Õm"mo›½éü ’BÎÕg–³l¯-xÍ:¯H™eR”N=ze¯|£T¿K|öEž;ƒ÷z*qG¼{“ÜvCÜ¢{Þødàe¦§¦(Wd6n Çó§§"™™N–òÏuŒxAX_õ¼À‹ùŽÎ«Ð*W*¶Ô…ã'žÉzd:6ß´J¥"ÌŠnX>~êôE¾ž¢X#üÍö5Âí[k„ˆÏíÉölÄW®ŒæR‰ýE>—Š_‰júðOÑ.U›SÕ©fu6i·“çøõòç??ÚÏà#5ŸËÅ~sù^™Úí®~c~¾ÑßÛXZj> endobj 17 0 obj <>stream xœ|¼ `åù?þ¾ïÌìì½³÷}e³»I6Én6@rÂ"„# B‚r„+ Ê•hU@ŠÁÒzQKÑZ¨Ø ´µ­BÛxµØ¯²›ÿóÎ&ˆµ¿ÿnÞ™÷}gvfÞçü<ÏûNFiPbPýäiÑ8’>‹ê`Ó8oÙÜöt{á­á3ó:Öøž®8v:þŒçZØ~˲OË? õ¯b7Ýr놅éó…«Õ—,Z0wþYòØßZ2:KA‡°Kñ:BÊÛ¡¹hÙšõC÷;×_ëŠysÓí¹¹pÍ1Ëæ®oç·³]p>G¾ås—-:Ž#{ûŠÕkÒí%6z¼}Õ‚öEo7Ãùg’qï wy¹Í3O"BƒýÃ%Õ9øo8æ‡ý˜­p·Ñ÷?0jt ]ü|†OÜBJþÇ®ÿRªöýï3à“@ÿ€ÒeêF¹èMTCMèNô(ÎAÿ3ªáIT°} Õ¢R´ ýêÍh „qÕ¢:¸ök8€¦£'Ð1ôúíàIÄ#éÑ|´-„kþýùàú»Ñ 謩‘Qš‚v¡Óèß8·1M¨ÕÀÕ6 ? wˆ 8« Ý„nF›Ñc¤…܇¢pï:´­F£gÐU¬Çlý`xp÷àýƒg?‚+Ö¢Ihú)zï«ð½‚9œ‡ÿÊø˜ŽÁ5ƒOÁGÑa#œu]@‚Ñ\ÃEøçø¾Bldéc&s³ŸAJû5£6´îÙnC›ÐÝèt/:W ý3ð ìÆÙ8Ž ñ"ÜŽ„ë|@Ü$‡DÉ,&ŸyŒyžù#[Äí¼ #R˜ìðaG£ÑXñßr¸òV Í!xò'ad/ ßÂ5§â…x1¾?Šàgñ¿H™H>&ÿaÌfæ~æçìxnnòíÔéߥ® Ÿ½€ëkÅà;M¾  I=Œ¶î´¨³­‡qlE;Ñ^´õgC‡Ñ³0¢×Ðëè×èô)ÐCÍØƒ‹q)®Àx+Þ‰÷Á“¼4úJ¢#’ ßli‚L!+ȽäaòSÒKRLSÏœ`.3_°VÖÆúØ8;‹½›}ˆ=Â^äfrK¹»¹½Ü£² þÖo~•ÌO©S S?OýFá3Ø8¸qðƒïµXÉà+‡Q9@~2A6óP>nU µh*|§¡4ÍC‹@c–§(¯èoCwÁ(w]»ÑýèAt= #}dô0:ŽN÷<ü zýdà¯ðíG¢OÐôDƒÀ[«±(¡Jdâ0Ðbƒ'á<Ïî¬À«à» ¾÷à‡ñ€>?Åáãø,Pé"þ#þ–‰“”Ñd©"Óà;ƒ´“mä>²ŸÖps‰GAƒO3 ¹»e(Âߊ ;=dràß².¼ÿ˜­'3IÍ,D£˜8{y›-`þ"·±GðDæA i§@ç`?Èv&p" +CH,Y6¢´¨0^‹æçåFr²³Â¡`f ÃïózÜ.§Ãn³ZÌ&£A/è´µJ©ó2ŽeF¹Õš6_o¨­— êêòh;0:æÞÐÑÖ냮šïžÓëk“Nó}÷LÎ\ø_gŠé3ÅëgbÁWŽÊór}Õ_ïo«¾>B/¬ž;¿·~ÊŒê*§ßßœ—Û‹+çnîEq½ºˆt ª”nÓ+«ìå¥ÛøÓá ]¾grÏì¼§O@7·EÔóóçΞÑËÌm¦÷ÐGzkU½µûmy¹}ø±é3z•}MŸqMìzf|WUU3½›¡rƶOw2;«m‹}´¹sç6_ïÁ)3n<ê§Ûæf¸h^nbê ?t³ó(£‘æ^ÒFœ>bn Gº†\ÿy[ÄùYD!›¹Wºþ§,ÆêE#{±åÿçð‚ôqPŸjß3,ÜY?#4wç.g¨mç=ÍÀšPÅ;k¾šm;çö vÝð Ï$;Û«Û†‡Ô7xf—³W¼§y¢ö¦©Ñk¬œÁ8IsºFœ ÔÓ‰)3gøªw¶ 1n¨g„Ô¢†ž”ÿ[ªðÚ$ð?{_JÏ~ãgí! £r´qà¿°€£mÎLÅ )ÉÃ3ÏyÛ Ìøøœ÷ZZáªG½Ÿn…¨ònÝxÎûËÖroëÚVzc³ÄYdVkn0ãùç¼§¡´5á˨ J7”Ï °@„ËH€âƒƒ"BáðlQ ç/¡¿Y:Ò‹Æ–‘*…Ò ¥ J”#PÎC¹EŽ. ÕòÂõއk½ê¡œrÊe(²ë½mPÚ¡tA9ø_g}†ß•‡—žó¢CÂ!ß¡Ø!ñPý!j| ±±¡¾A†ö û}ûcûÅýõûeh©°Ô·4¶T\Z¿T†š„&_S¬Il?ªoª_Qÿ‘Â÷Qì#ñ#&¦éD­¨» ½ “Å:ÄÛ(n$]ݤkc÷FòÙ!¬¸|—Ùpaé:Ð}€ÄˆHWk×/»[»ÉzY/e£ë%^†x‘/ö2‚BÐEq”ñ)|º \ÁÄ1Ýd<™¢®·2õŠzÝ ¦“ÙÍXWà6E›®·+Úu»q—¢K׃»Ý:õ|PqPw÷*zuçñ…xêŒî¾ ¸ S{yAå}ª >¦šÌ‹ªV¾^µ‚oSuòíªÝ|—ª‡ïVáªNó½ªóüÕ%þ‚Ê¡“éø¨,ʳÕ Yß*kåå:VÇùXWÁVp+Ø\'ÛÉõ°=œÚË š(çÓTp1ÍdNÔ´rõš\›¦“k×ìæº4=\·æwPsšëÕœçÎh.q4dò™DS—‰E55 2½\¬ïxùh]vHïÞJïÞLï~›Þý&½{9½›ÞÍLïf¤wMé]Cz7%½›œÞyÒ;gzçHï¬â<Ø_…ò(ÿ‚ò)”@ùÊP~ å4”_@yÊ(?„²Ê(·AÙ¥Ê(ó¡´Bi2 Ê é>ýéÛý:½;—Þ½&ZaÊ[Pƒò`]\¯Ð+ºûðQ仟à»W{øîu|÷*¾{ß=—ïnä3årŸÜ#wÉr›Ü"7É rA®•«åJ¹\.“³r"ýF&AÓÆáDï™y(q³¯÷‹i>¬œ2³— ŒÃ½†JLgëI€˜Ú[Iôòõ³f<ƒñ½`Ë·K®¼Ûi{‹“zñ`‡J·üÀI÷ƒ[~ÐÜŒ,‘ïl×k8Q¿áH½ë9Þû9ï]ÏC_btuÓ®îÏùn©Ë†Ö£‡swµ¹ÑõŸþ ÿ?>¸z1dýŒgäh\såìôþQ)á‘Ûœþæq¡}Œôü£ü¶;'Y„Ÿ@*ðWj@(ôPÞØ¼±ô9zHK±ÑÐ!Û£üΓø‰¡Ctë€hõÚի׬…ÏêÕ«ñê5«×ÌY=g tÁ‡ö®¬Y½vÍjxÂôæúÓæšµ‘ÈÚÈjÔKªõú ˆ»!’U/ê#·udÀFR78žÆ7< gg#~6î#‘ç‘’½q÷1'H¡hêáTA·1¿Þ¯ÂÃp®ù˜3×D}ƒ|ì°µK™"\Êm)çôÑ*ƒ[q1D½1†­gÚÂ<­¾XÙ2PV†* bF&T\TR”Í3'J­EÑüQÎR¸Rååñàý©·àÚ››É£²óȈòŽ#¥)pö‹&®éOÓµêvëzt¼îï&á‹–äåpùh´ †­21› + ‡Hq‘¡´„lsÜ»­vÂ=[NݺsÓM²ó¥{®¦~ýë—S¿Mþ èÞù?Æó_:güˆ¦{î’î\/æ)ogì|“Ã7“f¦‚Û˜Ûø]Ì.~ÙÏìç÷É ôùŒ\‡ê$Ä:ì?ÖeÄÆø§žl冭Åh±Z fáe|¸¤ÄP\DBáÀ‘)wnéêšõ„cË&Ômûì|ϼý©ÿò©ŸÿxõƒE÷¤püåßàâÏ¢ÏVºÙÙ÷ 9ÁÇY?Vö,:\’‘ŒÉvU—ê ªWÅÖ«ºUDuR-|±ªåjy²¼E¯” åýÀ¿¾Pï7ûõ½¿°rnM9É—øÃ”cKʃÿ wÛ’ú ~"T#š$vB8Û‚óî-™a†1D1Æ]†‹“Õ+Ô»Õ—Ôìnuú´úÌkì/°8ÆÖ³ml˲}d¼h5\T>•¨bÚT”?݇dª¤¹$õH¦oNo_¹AŒc' jBÖ "èÄö=XÐö ‹ j`£÷ £ÛƒõjØØ8ódTÂæ;¶nnÁq«E°1#Ò ¥%Ö ™^0ÆKKô!å/ßõhêã×Nwܳᷛط鮚i?â¶ì=•ÚœêIíÞÿ2nÇsÎ?V—L­JÝ–º¶~7¾ oÇJ*ƒóñ·YÐøg9…¡Á؇-bØÂ*äj –¿¢á_Á­†WOS¯iÓœÑ\ÐÈlˆFc³êA V¶ô'û…~•†Š«B9¨†ÞdµŒÂfqa¼¤X_Šàâ1¸”.?‡ÞQ½ý$ú%ú\þj…µ¨þLÍ Øµ©»ÔgÔÔ—Õ¼dH’–À÷ª´’-@Iý°U¦&´¢iá’Ô¨¬fB~¡Ë‡yYˆ]¼§Ö4bö42Ñ Œ›s6¢xmQ#€i#ƱñùhŠæ `'#åߪÔ&øPFµ`‰#ÔtåãaÛ1›<˜Ê.åÛu»æÅ nß²r˜‘¥C¿"v×/ܸëÒÖ_¶MQ8Æ—½nÖ=›:3 ³ ¿7PëÅVl•”Gão}ý…ËÈßSnÙû³Eëg<4gâŒÊ‘ÁB‹ÕìIäí?|l\f7š­„Œ»ýñÔgX‚ñ’ì«–L]ó«—þBJ´ t¥´×ŒlèuÑ6gYJÌã-Í–íh/âoAØ‚Ìæ>\):­“Õj±˜cm`M&¤Ñª4j5Fœ¦ÌMúêNõõ š…@AUW¨Yu™%*eV3’YêÌôá?Š&Œe­–N}þŠžÑé½ú¨žÑ÷™¨÷™E3i3·›»ÌÍÌœ™*‹²ù*èK²\èoþÊ„þ•-B?ª(¯€NÊqº++Û–ÙvÇ+Ô­¢\¨/4Ì7‘ÑØ_¬À~F=öÃ-¾œ†än)*HþŠ”¤ÜøoÜÒk“þàÒ[‰Üœ—ÛÈÞd •ýÝ ûF}ËŲZ¶Y³­µÙuyMÙ3òçñk.¶.Ì&r‰cƈB‘NWT´fŒh’c£×k/íÃÅü‚ÃF£]Ö‰t:ÍErq‘ÃvM‘ CcDÙÈ‘.¹B‘±ÍrÄrÞrÅÂZ¨†]ù-–Î œ1¿=Üî3áe>—èºìúÌŶ¹º\hÅE5aì°&€II¶$Ë¢e”(Ö´9…&•þhò*UkÙ6.?r‡ð ¯-çµB9eÉ/ˆÙ@5l¹1“-ז߈c&ØäY#(jåD>-ìðgZRy5›´\ C2›Ð žû¿¤_ÆÓ3%1/5–`sò`b³_Ï)gî_°öÇþ‰;Ǽ‚U©þ¿>ýnuIq¥?7·|ä}>µ¹|}9Ÿé2Ù²ÕœUËmé6*tÕU™©»B¡ÔµÓo¥>}uûÔæÚ²HLÐ[|7üââËÏææÖD]ö܈F‰C?§Ò½8˜ÌBEè qÖkº·µoë> ~©åxm±ºTË”Ô44.ä,)\T$—)KÕÄngôHÁÎÎVë¹èa…f··Bíç|ó“ñ‹2„r‘L”‘˜¬^Ö&ë’±²>Ü)Ús/R/'j˜6M»¦KÓ­9¨éOÇk¨$Klh¡ÈG[5Ð2l§ PŽ [~LijIÓ”ÖÌÂø¶¢¸(L©  6H͈%Mg\3ég·<ö—Ô¯ì|gǘ»÷þ… ‰’x­Íâ̬˜{pλ£çáuÝ[H÷¢™ùÙùÔ§?Ä#.ü/¾~ì¬Ñ“ÆåyB“Ë`ݘzû„3çøO޾pô™ž Z°hä6#šwÙñ\QiiE„UkÌ Åàö.y7Ä…óuȾ8æõŽ6G·ã CæèÃóEEç=º^ÝÝ NÓuë.èÎåL{~0Þe ´WAX+\³úÍ2%æµ$-R¥† ì! üíæUdùŽí5|•æÆGäO]V¿3ù×òQÙuÜf{þ¿RÇR~õøäHÕø±/¿„ÙSÓß qÍíðô… V:c€mb{$Z¢©Ñ4»š}MY¹³¢‹ô‹]K|  ²äîŒ ëå537oîPìPïÐìÒn1íÌÜíÓǺÎcÓ¾kì{º¶¤ Êk7~Ñ#7§>lïî^³úÀ!ò»›ç½ù›³Øšz"õz*•:ðô¶9c&ŽË÷gÙ ®ìÕ˜ƒœ;õÂÏž|î'O£Öí1ìÄ³× –±¢bPý/ƒì_ÈÀ\T(t£æ ódó ó%3·ÛÜ•ñš±¹Õ–¿}B: @poˆmŒ7Æ9ÞšêÂÂÚÚ"ˆuâññu쵚¢êÚxA]Ò\[¡„?ðƒÿBˆ{“ûyosxrì¹È²”)±²ËD›É>€–›uËSÌTobDS¯éŒ‰1 µ(N£Öþò~€KIšd‰è‡Â *Æåi\œsù‰ÆvÌ%ìë'…s5ÔùËó‡P°ÌËdPQ  çŠËo@ÀFÚßKQ¸!ZÃ~*÷ù˜Z;¦„K£¹ ó·=ñäo¬Ký£7iÚXZÝ…ObÛ¦}ãwÞ02:úØÑ§z÷sgÏí¹³·0÷ÅîåOu$2­ì£9‘䡽O/]_ÈM>–|-+ëÀc{€wºwðOÜ|>úµ(.–Íw-ö­:ô›…}²‡µ è÷˜ö˜÷ZO 'õº°y‚l¦l©íNné^îan¯æqçs®‹.u:%Î0'‰ü¨hò0>ƒhh3´º ½™¡§DAwÈ¢@*7Õl³:Ÿõ1£hl3öYÑØnì22Æ>2U´©. Ÿã‚ã²ã3‡´Á\p`Ç 2ùA ‡¬ÌÕtЂ*’ý-Po¯¥…Æ@ˆúy¢¬æ*$€2‡ð€…}´ºÂQ9nþí?;ù÷­«~÷ìÕ`­7õVÿ‰Ô¯OœÆ9?k{–›¾;ùô7ëL=›Z“:vä>I}•ê?‰[.¼‡7¿<¬G Èßk 6A?ý¾<¼'°7‡ˆr» äΧkÐÊûd yÔ^2€p¶‹7žI·|·±ÇHŒTÞr…«- nTØ`åð—NXd䵜WðJ^ų²`(312–c8"Ëe¨Ã«pMˆ÷¯BY\dUhFrrrlB`Â%eÇ>‚cºA¶ ˜õ’H¢,)€L5äÚSë8?&úò=‹$§ßSœÞCly‘xùOvN‹ŒÚ´ü}îlùÄÔ#ÿ>wð¶¾üŒDF&û¸7 }3?3Ø4rñ½_ãU?»䉮º¨’ƒ=bÍVíãCÚ}Æ]¸®ºx‹\“ÙÇÆrëØ‰Ü$×lvŽk±f±q±kƒfƒqƒk‡k«ìÂ錤(yk™®Ü$šT^CÔPa˜lØmè1Èt¯èªÁbññ9 (J*!}¤^ôbŒˆ³Áß x|ž˜GôÔ{º<Ýyh0ž>2Ytûä]ƒ^ "Âòú¿?ùÉ=÷'Ûrãߥìrî}í>âë#ÓDú¢ãˉå0õ98穹ºú©5’”de’Qè§^{`ˆd哬‹/œ§Ð)ƒ¹!m(+¨ÈS¯Bº0lTù*¬ÉV¡ò4HŠ©®TXTƒEÊHiE¿™ú(=¤ î[Û9µ286±ð®'6ýfÙ²3›Žœ.y¹uúˆØ¸ÌpâüݧR¿zMrÇ_HÁÇ,áÎ&"ÓR#ð¹Æ¬DÂâÔ×}óÎMBF"0N!$ùÅ$ÁŸH8lB5›;-B)5Àå>š$jcNL(Ò©4rÀ Bjü§£ü­"ü'Qß)éõ ‘ly6-bïR» f…ojd£‹Ç+"ø‚ûo+ˆÍêc,Vëý„1Â`»Ívš`¨b ±3—ÝÀ2@­ÞžNçóÊmV9¶Yc&$“}©ƒ¢D Uk1•¯P¶){”¬r¬Š´A´è…ãväÀÏRr œ§ˆeŠ™¤;gž ­(‡†;Cd )Dª400”¤˜t¡|H'Ê¿ ¶Ý!°‹­$Ràÿ–ZÃ2ƒ` =Ø m“%Âeà|ïÁ_Ý66¯dÜÊäì;KŠRÖªä¿cÍ·$ßÜqs~ÅÜÙoÚÙîD(úͲ@(‘Èö±½™ëøæ·m­…²DB1¿“¹ P ˜¶bÐ5},®éuàÅÜ|Çî6u‡qc·K½ÕØåØÏíWw»Z½Ë ð€ÆV‚ǰ#9¡}\†ÙÖ Xå¼2£AèV/0Äàq»gFÌ"Zê-ŒåÿQ>¢kµbëòÖ œ±ÜI6Ña]Þ*f6NŸ3æ¬wv9»—Ÿ9ÈI;EèäØt;ç üF¶àÜú[Éî€õ¦qÍ@PZKY[)0£[h­}…}å•aJcªŠa°BZ f™ºÀaC?x:žËÜ>²x1á&O9çÚ‚]Éß,¹]7»ó+¶ÌøÎÑ#H"‘»¦ú›Åì‰k›7¬ƒ{w+óC‰R ÈwÄ ¿¨Cn´]¼uš¬Ù0Ýv‚—õiÏò_ÉäͤQÛhjv­gÖst*o»óqeÿœò¸þ9Ãs¦ãæ“Ö“¶SΓîÿã¾TZ„Cˆ±’+£¬Èžf™ÛÆö’ñ¢ ö©cêv5[¯îVi£¦îÎ3D$ 1K|ä³t*˜öAV ý R&A !F8…kOœLzîhêÜ‹Ïã1/¼üﯞïziõ­ç¸_Mõ¼÷^êGg_ÆKßû#n={Sê…Ôý©Õ© ;qîU¬Ä‘nš-¨›•)Ù¬0Ú*ú³ä®…©¥æ'³Á ¶Õ%Ë_Q°ø.¶KÓ4œh@˃ë—÷Z°`ña$ó’%\]Ùr•ÎSNCè’Æ7i˜(9h—'@xY(#äá}8@`ãåÜÈÏde”©kˆÏJi'ÿ÷çûÒùãtYOÞý³ëúïÔWfWô‰ä¨r³‹ê–á_?ýäÄ>±bwöòñã­w§ø\£ <Š‡ÝŸ—•\Ôó|׎q9Ek¯I­ä>ŽûÑTƒÝb©Œ­eI“y–F )ºÕ´mÜÚgñɬgsŽEŸ÷Öðn¹¿–gž5Ÿ°Ÿp¿z1—‹Ê€<[ ëÊL:]£ÓÆ TcGW2*Ší4"%ÍÉÅ”¢’ƒPý¼(Ê:4Ù‡œLÒ1A¡¬,(ΉłyŠ9Ê®àŨWôžö21/ö^Ê»˜öìíV¶ÞÚfí¶2õ°!V*/µßÊKz†+©ÏÎèE“ýipT1@uªÌP6œÖHgõ}¡\›Ç½ ÚCY® ]޼U(äÉ^…sm°ù.RZ‰[V-t’Q¡Ršk ¥%þ´Œ†iR %¡ ÖtîËjaÒøŠÎºÀ¯ÙGcÀçË•’r¼sÅ-›Ì#EO¦^=ñÆ/°ãE<â…ž/‰/?wçí¯®WCz6Ý5cÜX’WÖ1gý„ÚIåKfg×nÉÊj˜÷ìS?ý»VûK¼øox\ÿùÓ©ýïý*õ ¿ðÓc×pðž{Ro>›J­ÉwÝ~°i\êýg§,Zß:gº×Cå½hð_Ü!÷8ƒ¾uËð"õbç>7“'Wi4”“!‡Ëäp¸.•†µÈ |¾x0»!K>ª!N62 4»b¢8~AH‡¬ð±E!/ »ð#Öoœ#˜°i[éEI]By…áå~€cYËż޼3y Êò|ymyíy]yò.çÉëópU¡ ‰©i–¤Ú´‘¤™€•ýয«SÙ·™ª4K=Å#©j•…ŠùÒFf$)mÄ%\Q#âå#˜QÀP¡|X¿¨]Içh †öë|¥©u0¦ù$ò?&ÚoT¼"ríýÏç>ÿÊî[Çæx2o±¬Êøñø¥N³C7vgÛ£­ðÞä”ûbyùÑûðËGzÏ&6ëÁýÕOnÞ7ËN¬{þîXpÖêÔÏ&Ô•|°¥½*y*¨3&!?ûH(”<´å©µÛãþ,êë¶ ö³? ÍÇ bÞ=Á¡?™Ru­ZtˆÎf®Ñµ˜[¨^èÚÀu¸æ^·¾n{ÍõNX(–ÛÛûðÈcÎÅÇ „Gv¬DVl;fÆ‹ íg3Ì „·G—*ØŽåjtàÔ.‹“!S  itŒgt`r`i 3°;p ðÛÀ¥À•À`@é Dp€ xOHб~%(—ñ2+&3­ Íõ0Ü%·JóšGEµV:_tÚÀhü-8V+gA4˜í˾œýY6—MS?Qá @c4ÜîKÖKž1=¢ÿ6;oCÂ?¯`:QômM2­ªœˆ×g4Éòؘ9Ç×ä§{¬È᥽Ïè9ç „†fîÒŠM 4S)dñi~)Ìô…4Äqê_ÓylæØÌñ²qcþóÈæ'ÄÎÞ/ÏÞœúÏ>xre]]Î|þS5{/}Þ|Çÿ½Ôò×´)ùÁWý¬ª]Z*Yþxû¹Ôÿ=°“ŒH}zäŽÚ¥ã‹FÖ-<õòÿÓ÷ã nô´XÕÂþ‰}ËÉT;q;Á1Ó1Ó¹Ýëü’å+ØrG¹s2;ÙÁq6ÆëŽº+ܓݭîÝî÷ DEîá.ÎÝG¦‹zš«RëtZ9fê &'ñ ¤ ¨T0!WC«»i—ŽbHd:hê51õ ²&¯ghíIz JÚI¥b E—g”OÇI±¹ÿ¿±_z^ŸÌd{ÝÕU©Ë##9±“SžÊÈÌ/¼on}`®yLû¼RàUÅÊRUµ¢VsS@¶(—*°™Ø# xNˆ:¿ÂºÜm^®ÛPï>â&n Ú2Bd9v(ì ÅBÝ¡ƒ¡Þ‡BBÈju…8!„C'È$”+¡rºü&ÙB—IP‰”ðð·YžáX)=¡«/Ê'á˜9=a2#ÙrxGi®5ãøý¿zË­›N¢Ysfoß—úóµ²H4ú2ƒ×^¼»r^ÇGÈã3[Šì‰Ä‚¶dö§»ïXÛ¼¹## ´ ¾ÇÿhÃmb~¥—©jlãs;…g/Ê]g»Ý½={W®J­¥ž¨o®Ëârr"*µJ©ÎîœuÌžåëœ-Þ©Ìøy¹¹K” “R©°ådg/±ÛLv»-œ•5Ó4)2S©‚ã*…Re³;Â9y &×/ €Ï #1­¬lE$WÑfÀƒ]§«ÐM¦“—9ʰ#`ËÎbìjUNŽna•±6ö½+R;B}"ª|aûdû ûz; 6*çYQTbå)¼ùÀ-õºXWŒ´Ç°«‘X,^@å‘NÎ8ìɇ Š´à³\~uú²2›$¸ÐÑ"­¨ ü*§½þ»ñËÐÜÆ ÑL„— åòaæñÖäÅ L“MCl5Y±‘æê¥-áq@˜Žþ40)¢“á<,Ï/?“,§Çe¸ðšnôè||at]±3õ@Mê©Ô ‰T›½¢8nsÂÆk ̼≄Ói6\+bÆäUV$òH}Ë7cÙÑß¼Ìùo*ÎÁ>ìΗâ¸x´É<³`yiÁóí÷<\ ï°î2…BiµY-6¹Õj †‚™!‹B!/ŒÅ•Á`¨°¨0.·Ù–d™23‹,¡Ð̸Ò+å ¥ÅjË †àD5‰gK…RšÝ‹ºó=‘hC¾¼0ߪ]ù[¦’ÕŠâ!9KJ'ƒÑ9ífÜvéþtÉ€u†¨a²1<銑´“.ÒM8òxi©¾äQ–kDW»ëŠ‹Ñ¹¼.âêÃÇÄ|œ]IS©tÅW ÅÍÂÀõˆÈNY |¥±h’òP(š§Òo‚JåÛèö•m7Ô‡ã¦ïðNŠŸÀ<)p:<• |3×ç°ÆP3?HZko*tcç»9uóˆ}FaõÈÔü{Ó¨ñÉ禌Êó¤Þ¼\<¦|dò73vUâ²Ô­± S€­“.'Õ†'ˆáp>N$?óØï“¨äÊÜáןÚmbkˆ ð‘ùžAàç¿9Š1zg¢.Äâzˆ;¾p$ËP”Îïa>8ɉ˚úûòBiÛjÿõ”BO¡ˆ†V«V3¯¢|T†Î‹ùÖûˆ¬$º_§Sœöœ÷\ò0:O‡x=Øóµqi&_²T~“×Î0 ²ì2¤Á£„²YõÙ+²³ovOöùìKÙlv©mêzg›³]JÐdœ"4çI2ÐYƒè;èïõ“˜ûý…t‰©POV·Ò)úÖôBaÒ‚F¦£ê¢€ 4U.!bïÐ9MÀÍýMªÃiiž“R;h• Ç,@„Ìàм—ÛIÁŠ”þ+ŒcÙð„}1ëÏß\Õhí^ÐýnÂîsš*òlî‘Ñxå¥ãϘa±©¿¥–Lž:®ãé[F$&´Ì¯¼}îý¿ôÝ;ñÖL§CçËòæ•UÏûIÝøE)TåóãÃÉåËoj:¶R]ü7«åÞA6°t}N‘µåXƒ½vk“m}Ÿå½Ku‚R%(Àâ;E•A§3h4.a·õá‹Z×FŸ-fmõ¶6gëÃv1 |†.C·õbÑP/aÐ.ÃAÃÃeÃg•]Ý`¶Nà7HAÇÊä‡Òš0iÆ¢TQa(ßÒéòtŠÝá r 0ÈæaŸ6™2û<œ!÷Î VŽŸ¾`®“0Nb_Ï9×zpmâDƒ=]ÇDãEʺÂ}h!ïðÂi)$^0ª»á§ýŸo¼gBYq–«º"‡LYyï=+Ž™´ƒ{'Y7mj*õûRUM™5R»ú6P0"‘|ìÕ¿½sröš·ß:MíÊèÁ~¦äÙŽ‰! )òé¾¶/UðèK_;£B–Và C§4ñvÅ0hàuàÍ©(Z°C´¨wûp ×ãƒø ¾Œe Ú‚¤øUb¦»£}huÈÙÙed>]ñÕ 8‚e+2†BsØ\¥Rjžkž¨p8¼¬Üô²îyÈ!‡‡óÍC.H9LEltI¤Å$ßYå5$·@(F7vRê‹—¿ÂBßÎg+óóÆd“*7VÌ.’€?)À¹.Wi‚¸ýƒ>ä6€ù5°ñ.0mÆ‹U S©ÊTf¬gªàz¬T ‹•tÔÊ2™Ã¡ÙµžË2Áq³%}œÒ38Ô%#@†–×Z%Ùc¾]“‘F¼¼¬llꌭl_ëÁ¿t¯üáÌÝÅc«+GÞÚy×Âeû§Õqgõ_§bâôÂ[°úÏŸÞ»bæ,¢Ï)H~qô÷œzãõ_,k£zÝÒw¤Ï„<è˜'úð(¹œxÌ©´˜‘y?Æœk©ÀsíJ/·w ƒír|AŽ9–SŠê4fÜqEƒu¯†hVx)%锬$SéÅÌ4áCa t}šŒ¹ÊDX£É`"2&äÔÛ×a·Î<±* ’fr­Ã-–¨9JØtB gM `¿”(¥b‡Š‹ ”8Î?¼Z“9šœË%·¼²nìý©¾LžØþZ<œ‡ÊÌÚѲè>ü»‹3æ]r¥ú±ðV?¶ßs÷ìê‚QË>I51ŠSÈý"›gEçBw‡{»o¯µÈI;ƆvóI£Ìë‰zˆ§"z²šlX¡nR)ƒ³Zý+üþÝþÿÿ Ÿ×kè#sžGθÆÙ‡ƒ¢/B–+–³ÿ¾ I½Ë°ëŠ€‘pD ‚P/t Œp¿?Ðô§'¾`AÝ𺄖Šél¢D?mvŽÅšM׸æX²üRÁY‰ Íï&DÅ…iý—fÊÌøî¿ÿÝâ)éJ¾4¹8çÇ?þÝÞ•££9¥™î¼¼†µÌ“MÉéQ#D8™!òbSÍW˜[Þ<²²$;걩õ®="ÍK­dG3ï¢Q¨5 _‰#›ã‹ãûâ¯Ï•¾:V6"0"sÄèR±tl­X;–ßoØoì.eò:b3 +:JJ±œe‰.Ç2!æ‹ÅbbŒ¥›öØ™Øg1.öt#EªÉtºq LZ¾BZL’^ƒÁƒ`Dš_,£gÑôDz®µd˜^4(-Ò…‡¼paiz™œ?CJ6”J«æK‡×Îó7.š»žK¤É F?z|\ž§Ì$7fdE|Ó_n^¶&òz¿<õÓ9w§®½½hêâÛçÍŸ2¿¡ñâáš»~Õ¼fÍÌ™«×zúŒšÔý³Ÿ—t0kˆùÁ‚OÞ¨¦xÓ–…6{,l6sw,[?uÍä17ºÙfk©[3¢[·÷eÙîî¸s×¶;×n¿¶qa]U¸hUMvmÊxPìq°©QôG1ºÏ†="çpË£’1u|Äñ™B )3 º&A‰±7‡®r?¢?­gõ§°Y€íG-UÔÉ(rPæÁÌÞL&œùQïÆõé¦ÎèîhO”‰¾‰r°SŸÓÃäPñŽI»ôu™º´ ½ >*ÍíÑï*†ÒD›]¦àƒv™u–+lrÇ:à]Á5lBn†uÝŒc=çd=ëC®›PÔb JÞ¢þï§=´ßëõÁó&<Õ:³nÄäÜh}J6B,X9£ºcjN¬>¥Q«(¯cšÌokd<µ&U 'ÙÙøÅµwOØ3=µ3ž“HäEÙ9å£Àž€Qmúªµˆš¡ó-™·I½IÖ‡ÿ!fkdK“Yi|SK_×"ôå-¢haíFó›ê7‘㈃Ô;ºÄAIæš —f?…ô4ð‡W![ùÝ¥LðdB‹òªÆåçWV½×ÖÀ’ä?çåf‹7qïTåç«ÌË«J¾¡ºöG¿Ö”HD²É¤eT> A>öÀó`Aœ:]7]˜ájv³:àö€Iv»·ê´X­Ç³–°&BXÁíòè´,‘+CM  ZVQδ›LAsФt+3u¬™Õ Œ²/;®REÍ´þ.ÑÚj:mºdºbb£¦ÝãÆ Ifê#›D[/3Æ™õ™Ý™ŒBÈÄeTÔD—·3¼;ÜfÂoz7vì.è)` Þ¼ÁõÀLJ¸¸4uÚBzÿœ–9«ZhŽ*{KY4)¥ª¥lµä•ÊÓ³zýR›Nëié´Ä­Zé]ƒ¡¤Ã**,¹^ŸÊtKð¢0Û¿?#_ˆâ‚\GxþÏ“ÞÆ-¾ÉE)TXV¦v±±)Å”xÌS[Òu cEØmךHM¼ætÊ¡k/µZ<Ý€[kL$rìæ<³‡ò(2Ö@ÿ/*=@d ¹‚ þM¼Qþ¦éM¤9¢!õšnpÈ”æ!!º*‰U°ÿΛ‡%`û½=s^òÓ9ùyÎ`Étt ¹Y울՗ w^€ìmæ$Jà‰â¨¶DKöz÷E&ÃøÇ–|$ó¤VËøµ&²±¹¹ÖJQÌÏDìvK“ŒÉ¨ZE8ÎŒªµŸ×oÔÙØ‰òÓŒÜ=îKî+ inïPÆ”¦Hå¢;Ž"BĉEFz#g"|{¤+Bb‘úHw„‰ÐßzÂuùÞüh~Eþ‘üÓùçóùÎüÝù¤"¿5¿'ŸÉO_>Þ¿¿guqo|r¼5¾;ÎÅáòÏÕ"ñ HDj¾Fm¬< .A/YQÛ^Û]ûY-‹j¨©=_;X+jë¡ÁÔR¢N¼>"¥EhB´bd#NÝ5;ùø¤iwÞwêxç-dmq ÁˆÁ³?—¼ãE1²<*²6‡Ü̪Ô 2ušŒJp’HÆ~dSšLZ³™7ÀC?«ÕIÖÁ~”ß_uoôÓ±EW½ë‚벋EPisµ»º\¥³å‚«ÛE‚ —¹Nà?ÓåC>r «¾M‹RÙ°éêÀ®ìNÂ0v܈Nâ`Áã0•ÅÊÉdös–³òŸc‹ù[L…Ó4J;¹¡Ô''¥>¥‰ykáÍ«gµ¨¾//•zÕ4ý©¦ÎƦˆ§…9™üáªÛoÇ­±b¦Fm°¸¬R~bÞü†é¯ã=%.ˆ¤€‚ŽÁ~¶„ydàÎ4Ë´Þ¸•ßnÞk~Þ|Ž—‹rÜ7¸Ml”›M>Þb6o•ó&¹œWM¦µrTÞÈüí]}lS×?÷>ûù}Ø~þŠýœÄØ±ã¯Ø‰œ/BX\'ä£òå$•ØQ3B‡ŠT&•ÐvíªJé–R*Æhᕊ1’vb¬Bí&­k¥­£Ñ¤ÁF·ýUþØ:Ú³sŸc(lûoš„¯Žï}ïÝ—<߯sÏ9¿sž àNG6MZ&…Nº'‡/ð¢Ý¬8 <‡C8"4 ›…'…ýE`õ4.µ[=w†!§Ï1§Wº#mL’dœœ$爞LÑíi;Àûp fAg>ÆJ-ÃÝìPÑ07¤92j+Ó0%àUÍpÞ\΄U=3‘› ˆßœfOeÞ®®2M^e†pbЬàáRòîú”tí²~œ<±¸Ãžµ!?îX´„;ÞN=eNçܪšÍ]¶öv¾.~ýÏ\m< Ù>ëo|İãd"½inÄ †žwéE¹ÓIÓ¾~©?œl‘¶„¿%í¨ÙWsÁ)±JCÁþÐPd(ú=ù9çKÁC‘CQ9 KAI ÊD Y¢8-YÅðe½gÁ,›²æª'» =ucï)W°ÒÇ Ø[ÅHØ ‡‚áQrˆ¢DFEK‚ Ž";*+G\*–Ô€êr²H§ÑÄEBQPˆ&À++#‘°,D#a±FH/VTÎåò†’â}"EðN‘Ø©×B$4Eérß>·ipöa7éÃE ¢¶]Óä+P]œ êpuÞ¶‡½Â¬ã|¶&}Üüæmâl‰Ô:ì_ ó䌕ª`¹J,çþý÷V™ÐÉΡì¼iB/c`‡ú[GEƒzapÚ(¨'“ ¿ô›3¡æ.r>T?8n­[FiïPó³ÍùkWšó'-O2î§{îcr­/œÉ”©6ËÈ3¹õ¾9™œÛε„Ë(‹Vˆ2`5Ž’Ù–>Mtòýö~ßûߣü/>1I×ÚÖÚ|ýë’µ|2Yk„„Áf·óÉ Ÿ¯Öf³Wø­V¿$!8 —ÚQ_­¯$ü>».–´¬Ïׯ˜ñ5¦i Éĺ7aòKU£Ñ*©Â`ã¨`Å[#þ›„_OSFœiï< æg Q—Z€A¼Ou@*‚£lR–¤RѪÇÕ1õ°úžªSÔVu…Ê©oÒíà‚(þ y‡kù.—Dž;…û1ùñªÃUçª8`þ@8,ê4 )1ïúSȆrW‹À‰šÜrKût³Èê*¹éMEÔ3CM˜-ç±diѾ ݯè5$LãütgÊ)ä¾7€¤ ½f` äA­„„ÂIr~›íÈKÖ=ä;c½gH…§}i¾Â_ï¶Ø:ász»óßèŽ,­#µæaº€Åý»·Ž˜œÉ¸6ü%ÿdÐÛÓ# F¹ôÓ·¢n·ÉFzz jt鲩…ÓôÌùœ¶î&á£t:[µ[¤ žî¡%ïðNÝx,^“JœNGÜY\˜Ð§sWLrÄbRµ 9pƒ‹§å/ÓÕ‘ nŦ$-É5Üà÷ùÇýÔÿXy9Ô:$™³X…jOýñœÈ–)òµ3i+‹Ó³„N“öPÓt›æ®Åø¶[îh6\d’æ4¯XZç Š„Ä—¡,PSÔ‚E ŠÂp' o`æù/9×3ù¡‘MÁòóªÅ1žèß*iÎ’Ñ\8l``ðë=뚪Bu:·bÕ²Áü!û¢F\š½“iîWä· “Á(ßÞ.^Ÿy÷þå+[ª›d»bP“[OÓÏ#a¦‡ye‰g°Ýmðût§·¶ô}…r.®‘ëä²Ü£Üwˆ^y…;nü^à>4š œh¤FäÞ£²÷¾FúP4؇‚Ág³;SÛŠ&5š† /sºa ÜHuD ÃÂë°mìedkÔ§™¡¹¢ Ú_„“÷I/K³mì}1³D{k ‹7‹íËàè€öÚ‘ã,Çök„µ…•Ð…<( õÐ+ *! ‚8¤`!4C°ÖCz±Õ­àì¸-Ê º¡ÊQÆ„*h‚°€dè#,†u° †  $ðÀjfÿǧ±aR,ñ,ÜcÛæm»Fvj¼‘ {ã,¤îùn?œ…Ù·˜Ïc¡H(<ݕȦ'à $‘‘n2x~?–ŸÂüŒtß„ÍHDÚ€ô4Ò#HcH#H“H{‘vcÝcw#ý;Ú{ƒBú.¿:0Õ¯…­XÞŸ€ ÜRè-žOž?¿Vãõíx܇ù.¼§N/Á~>stream AFPL Ghostscript 8.54 PDF Writer result.xlshirabayashi endstream endobj 2 0 obj <>endobj xref 0 22 0000000000 65535 f 0000006020 00000 n 0000032670 00000 n 0000005952 00000 n 0000005792 00000 n 0000000015 00000 n 0000005772 00000 n 0000006085 00000 n 0000006980 00000 n 0000011136 00000 n 0000006126 00000 n 0000006824 00000 n 0000006276 00000 n 0000007801 00000 n 0000006205 00000 n 0000006235 00000 n 0000008010 00000 n 0000011337 00000 n 0000006540 00000 n 0000007391 00000 n 0000007695 00000 n 0000031287 00000 n trailer << /Size 22 /Root 1 0 R /Info 2 0 R /ID [<20B633D03FD906D31FD643E9483C9B28><20B633D03FD906D31FD643E9483C9B28>] >> startxref 32850 %%EOF tokyocabinet-1.4.48/tokyocabinet.pc.in0000644000175000017500000000050410756231336016736 0ustar mikiomikioprefix=@prefix@ exec_prefix=@exec_prefix@ datarootdir = @datarootdir@ bindir=@bindir@ libdir=@libdir@ libexecdir=@libexecdir@ includedir=@includedir@ datadir=@datadir@ Name: Tokyo Cabinet Description: a modern implementation of DBM Version: @PACKAGE_VERSION@ Libs: -L${libdir} -ltokyocabinet @LIBS@ Cflags: -I${includedir} tokyocabinet-1.4.48/tcftest.c0000644000175000017500000014150612013574446015142 0ustar mikiomikio/************************************************************************************************* * The test cases of the fixed-length database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" #define RECBUFSIZ 48 // buffer for records #define EXHEADSIZ 256 // expected header size /* global variables */ const char *g_progname; // program name unsigned int g_randseed; // random seed int g_dbgfd; // debugging output /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void iprintf(const char *format, ...); static void iputchar(int c); static void eprint(TCFDB *fdb, int line, const char *func); static void mprint(TCFDB *fdb); static void sysprint(void); static int myrand(int range); static void *pdprocfunc(const void *vbuf, int vsiz, int *sp, void *op); static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op); static int runwrite(int argc, char **argv); static int runread(int argc, char **argv); static int runremove(int argc, char **argv); static int runrcat(int argc, char **argv); static int runmisc(int argc, char **argv); static int runwicked(int argc, char **argv); static int procwrite(const char *path, int rnum, int width, int64_t limsiz, bool mt, int omode, bool rnd); static int procread(const char *path, bool mt, int omode, bool wb, bool rnd); static int procremove(const char *path, bool mt, int omode, bool rnd); static int procrcat(const char *path, int rnum, int width, int64_t limsiz, bool mt, int omode, int pnum, bool dai, bool dad, bool rl, bool ru); static int procmisc(const char *path, int rnum, bool mt, int omode); static int procwicked(const char *path, int rnum, bool mt, int omode); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; const char *ebuf = getenv("TCRNDSEED"); g_randseed = ebuf ? tcatoix(ebuf) : tctime() * 1000; srand(g_randseed); ebuf = getenv("TCDBGFD"); g_dbgfd = ebuf ? tcatoix(ebuf) : UINT16_MAX; if(argc < 2) usage(); int 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], "remove")){ rv = runremove(argc, argv); } else if(!strcmp(argv[1], "rcat")){ rv = runrcat(argc, argv); } else if(!strcmp(argv[1], "misc")){ rv = runmisc(argc, argv); } else if(!strcmp(argv[1], "wicked")){ rv = runwicked(argc, argv); } else { usage(); } if(rv != 0){ printf("FAILED: TCRNDSEED=%u PID=%d", g_randseed, (int)getpid()); for(int i = 0; i < argc; i++){ printf(" %s", argv[i]); } printf("\n\n"); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: test cases of the fixed-length database API of Tokyo Cabinet\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s write [-mt] [-nl|-nb] [-rnd] path rnum [width [limsiz]]\n", g_progname); fprintf(stderr, " %s read [-mt] [-nl|-nb] [-wb] [-rnd] path\n", g_progname); fprintf(stderr, " %s remove [-mt] [-nl|-nb] [-rnd] path\n", g_progname); fprintf(stderr, " %s rcat [-mt] [-nl|-nb] [-pn num] [-dai|-dad|-rl|-ru]" " path rnum [width [limsiz]]\n", g_progname); fprintf(stderr, " %s misc [-mt] [-nl|-nb] path rnum\n", g_progname); fprintf(stderr, " %s wicked [-mt] [-nl|-nb] path 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); } /* print a character and flush the buffer */ static void iputchar(int c){ putchar(c); fflush(stdout); } /* print error message of fixed-length database */ static void eprint(TCFDB *fdb, int line, const char *func){ const char *path = tcfdbpath(fdb); int ecode = tcfdbecode(fdb); fprintf(stderr, "%s: %s: %d: %s: error: %d: %s\n", g_progname, path ? path : "-", line, func, ecode, tcfdberrmsg(ecode)); } /* print members of fixed-length database */ static void mprint(TCFDB *fdb){ if(fdb->cnt_writerec < 0) return; iprintf("minimum ID number: %llu\n", (unsigned long long)tcfdbmin(fdb)); iprintf("maximum ID number: %llu\n", (unsigned long long)tcfdbmax(fdb)); iprintf("width of the value: %u\n", (unsigned int)tcfdbwidth(fdb)); iprintf("limit file size: %llu\n", (unsigned long long)tcfdblimsiz(fdb)); iprintf("limit ID number: %llu\n", (unsigned long long)tcfdblimid(fdb)); iprintf("cnt_writerec: %lld\n", (long long)fdb->cnt_writerec); iprintf("cnt_readrec: %lld\n", (long long)fdb->cnt_readrec); iprintf("cnt_truncfile: %lld\n", (long long)fdb->cnt_truncfile); } /* print system information */ static void sysprint(void){ TCMAP *info = tcsysinfo(); if(info){ tcmapiterinit(info); const char *kbuf; while((kbuf = tcmapiternext2(info)) != NULL){ iprintf("sys_%s: %s\n", kbuf, tcmapiterval2(kbuf)); } tcmapdel(info); } } /* get a random number */ static int myrand(int range){ if(range < 2) return 0; int high = (unsigned int)rand() >> 4; int low = range * (rand() / (RAND_MAX + 1.0)); low &= (unsigned int)INT_MAX >> 4; return (high + low) % range; } /* duplication callback function */ static void *pdprocfunc(const void *vbuf, int vsiz, int *sp, void *op){ if(op){ char *buf = NULL; int len = 0; switch((int)(intptr_t)op){ case 1: len = vsiz + 1; buf = tcmalloc(len + 1); memset(buf, '*', len); break; case 2: buf = (void *)-1; break; } *sp = len; return buf; } if(myrand(4) == 0) return (void *)-1; if(myrand(2) == 0) return NULL; int len = myrand(RECBUFSIZ); char buf[RECBUFSIZ]; memset(buf, '*', len); *sp = len; return tcmemdup(buf, len); } /* iterator function */ static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op){ unsigned int sum = 0; while(--ksiz >= 0){ sum += ((char *)kbuf)[ksiz]; } while(--vsiz >= 0){ sum += ((char *)vbuf)[vsiz]; } return myrand(100 + (sum & 0xff)) > 0; } /* parse arguments of write command */ static int runwrite(int argc, char **argv){ char *path = NULL; char *rstr = NULL; char *wstr = NULL; char *lstr = NULL; bool mt = false; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!wstr){ wstr = argv[i]; } else if(!lstr){ lstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int width = wstr ? tcatoix(wstr) : -1; int64_t limsiz = lstr ? tcatoix(lstr) : -1; int rv = procwrite(path, rnum, width, limsiz, mt, omode, rnd); return rv; } /* parse arguments of read command */ static int runread(int argc, char **argv){ char *path = NULL; bool mt = false; int omode = 0; bool wb = false; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else if(!strcmp(argv[i], "-wb")){ wb = true; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procread(path, mt, omode, wb, rnd); return rv; } /* parse arguments of remove command */ static int runremove(int argc, char **argv){ char *path = NULL; bool mt = false; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procremove(path, mt, omode, rnd); return rv; } /* parse arguments of rcat command */ static int runrcat(int argc, char **argv){ char *path = NULL; char *rstr = NULL; char *wstr = NULL; char *lstr = NULL; bool mt = false; int omode = 0; int pnum = 0; bool dai = false; bool dad = false; bool rl = false; bool ru = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else if(!strcmp(argv[i], "-pn")){ if(++i >= argc) usage(); pnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-dai")){ dai = true; } else if(!strcmp(argv[i], "-dad")){ dad = true; } else if(!strcmp(argv[i], "-rl")){ rl = true; } else if(!strcmp(argv[i], "-ru")){ ru = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!wstr){ wstr = argv[i]; } else if(!lstr){ lstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int width = wstr ? tcatoix(wstr) : -1; int64_t limsiz = lstr ? tcatoix(lstr) : -1; int rv = procrcat(path, rnum, width, limsiz, mt, omode, pnum, dai, dad, rl, ru); return rv; } /* parse arguments of misc command */ static int runmisc(int argc, char **argv){ char *path = NULL; char *rstr = NULL; bool mt = false; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int rv = procmisc(path, rnum, mt, omode); return rv; } /* parse arguments of wicked command */ static int runwicked(int argc, char **argv){ char *path = NULL; char *rstr = NULL; bool mt = false; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int rv = procwicked(path, rnum, mt, omode); return rv; } /* perform write command */ static int procwrite(const char *path, int rnum, int width, int64_t limsiz, bool mt, int omode, bool rnd){ iprintf("\n seed=%u path=%s rnum=%d width=%d limsiz=%lld mt=%d omode=%d" " rnd=%d\n\n", g_randseed, path, rnum, width, (long long)limsiz, mt, omode, rnd); bool err = false; double stime = tctime(); TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(mt && !tcfdbsetmutex(fdb)){ eprint(fdb, __LINE__, "tcfdbsetmutex"); err = true; } if(!tcfdbtune(fdb, width, limsiz)){ eprint(fdb, __LINE__, "tcfdbtune"); err = true; } if(!rnd) omode |= FDBOTRUNC; if(!tcfdbopen(fdb, path, FDBOWRITER | FDBOCREAT | omode)){ eprint(fdb, __LINE__, "tcfdbopen"); err = true; } for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", rnd ? myrand(rnum) + 1 : i); if(!tcfdbput2(fdb, buf, len, buf, len)){ eprint(fdb, __LINE__, "tcfdbput"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tcfdbrnum(fdb)); iprintf("size: %llu\n", (unsigned long long)tcfdbfsiz(fdb)); mprint(fdb); sysprint(); if(!tcfdbclose(fdb)){ eprint(fdb, __LINE__, "tcfdbclose"); err = true; } tcfdbdel(fdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform read command */ static int procread(const char *path, bool mt, int omode, bool wb, bool rnd){ iprintf("\n seed=%u path=%s mt=%d omode=%d wb=%d rnd=%d\n\n", g_randseed, path, mt, omode, wb, rnd); bool err = false; double stime = tctime(); TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(mt && !tcfdbsetmutex(fdb)){ eprint(fdb, __LINE__, "tcfdbsetmutex"); err = true; } if(!tcfdbopen(fdb, path, FDBOREADER | omode)){ eprint(fdb, __LINE__, "tcfdbopen"); err = true; } int rnum = tcfdbrnum(fdb); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", rnd ? myrand(rnum) + 1 : i); int vsiz; if(wb){ char vbuf[RECBUFSIZ]; int vsiz = tcfdbget4(fdb, i, vbuf, RECBUFSIZ); if(vsiz < 0 && !(rnd && tcfdbecode(fdb) == TCENOREC)){ eprint(fdb, __LINE__, "tcfdbget4"); err = true; break; } } else { char *vbuf = tcfdbget2(fdb, kbuf, ksiz, &vsiz); if(!vbuf && !(rnd && tcfdbecode(fdb) == TCENOREC)){ eprint(fdb, __LINE__, "tcfdbget"); err = true; break; } tcfree(vbuf); } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tcfdbrnum(fdb)); iprintf("size: %llu\n", (unsigned long long)tcfdbfsiz(fdb)); mprint(fdb); sysprint(); if(!tcfdbclose(fdb)){ eprint(fdb, __LINE__, "tcfdbclose"); err = true; } tcfdbdel(fdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform remove command */ static int procremove(const char *path, bool mt, int omode, bool rnd){ iprintf("\n seed=%u path=%s mt=%d omode=%d rnd=%d\n\n", g_randseed, path, mt, omode, rnd); bool err = false; double stime = tctime(); TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(mt && !tcfdbsetmutex(fdb)){ eprint(fdb, __LINE__, "tcfdbsetmutex"); err = true; } if(!tcfdbopen(fdb, path, FDBOWRITER | omode)){ eprint(fdb, __LINE__, "tcfdbopen"); err = true; } int rnum = tcfdbrnum(fdb); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", rnd ? myrand(rnum) + 1 : i); if(!tcfdbout2(fdb, kbuf, ksiz) && !(rnd && tcfdbecode(fdb) == TCENOREC)){ eprint(fdb, __LINE__, "tcfdbout"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tcfdbrnum(fdb)); iprintf("size: %llu\n", (unsigned long long)tcfdbfsiz(fdb)); mprint(fdb); sysprint(); if(!tcfdbclose(fdb)){ eprint(fdb, __LINE__, "tcfdbclose"); err = true; } tcfdbdel(fdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform rcat command */ static int procrcat(const char *path, int rnum, int width, int64_t limsiz, bool mt, int omode, int pnum, bool dai, bool dad, bool rl, bool ru){ iprintf("\n" " seed=%u path=%s rnum=%d width=%d limsiz=%lld mt=%d omode=%d pnum=%d" " dai=%d dad=%d rl=%d ru=%d\n\n", g_randseed, path, rnum, width, (long long)limsiz, mt, omode, pnum, dai, dad, rl, ru); if(pnum < 1) pnum = rnum; bool err = false; double stime = tctime(); TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(mt && !tcfdbsetmutex(fdb)){ eprint(fdb, __LINE__, "tcfdbsetmutex"); err = true; } if(!tcfdbtune(fdb, width, limsiz)){ eprint(fdb, __LINE__, "tcfdbtune"); err = true; } if(!tcfdbopen(fdb, path, FDBOWRITER | FDBOCREAT | FDBOTRUNC | omode)){ eprint(fdb, __LINE__, "tcfdbopen"); err = true; } for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(pnum) + 1); if(dai){ if(tcfdbaddint(fdb, tcfdbkeytoid(kbuf, ksiz), myrand(3)) == INT_MIN){ eprint(fdb, __LINE__, "tcfdbaddint"); err = true; break; } } else if(dad){ if(isnan(tcfdbadddouble(fdb, tcfdbkeytoid(kbuf, ksiz), myrand(30) / 10.0))){ eprint(fdb, __LINE__, "tcfdbadddouble"); err = true; break; } } else if(rl){ char vbuf[PATH_MAX]; int vsiz = myrand(PATH_MAX); for(int j = 0; j < vsiz; j++){ vbuf[j] = myrand(0x100); } if(!tcfdbputcat2(fdb, kbuf, ksiz, vbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbputcat"); err = true; break; } } else if(ru){ int id = myrand(pnum) + 1; switch(myrand(8)){ case 0: if(!tcfdbput(fdb, id, kbuf, ksiz)){ eprint(fdb, __LINE__, "tcfdbput"); err = true; } break; case 1: if(!tcfdbputkeep(fdb, id, kbuf, ksiz) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbputkeep"); err = true; } break; case 2: if(!tcfdbout(fdb, id) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbout"); err = true; } break; case 3: if(tcfdbaddint(fdb, id, 1) == INT_MIN && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbaddint"); err = true; } break; case 4: if(isnan(tcfdbadddouble(fdb, id, 1.0)) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbadddouble"); err = true; } break; case 5: if(myrand(2) == 0){ if(!tcfdbputproc(fdb, id, kbuf, ksiz, pdprocfunc, NULL) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbputproc"); err = true; } } else { if(!tcfdbputproc(fdb, id, NULL, 0, pdprocfunc, NULL) && tcfdbecode(fdb) != TCEKEEP && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbputproc"); err = true; } } break; default: if(!tcfdbputcat(fdb, id, kbuf, ksiz)){ eprint(fdb, __LINE__, "tcfdbputcat"); err = true; } break; } if(err) break; } else { if(!tcfdbputcat2(fdb, kbuf, ksiz, kbuf, ksiz)){ eprint(fdb, __LINE__, "tcfdbputcat"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tcfdbrnum(fdb)); iprintf("size: %llu\n", (unsigned long long)tcfdbfsiz(fdb)); mprint(fdb); sysprint(); if(!tcfdbclose(fdb)){ eprint(fdb, __LINE__, "tcfdbclose"); err = true; } tcfdbdel(fdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform misc command */ static int procmisc(const char *path, int rnum, bool mt, int omode){ iprintf("\n seed=%u path=%s rnum=%d mt=%d omode=%d\n\n", g_randseed, path, rnum, mt, omode); bool err = false; double stime = tctime(); TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(mt && !tcfdbsetmutex(fdb)){ eprint(fdb, __LINE__, "tcfdbsetmutex"); err = true; } if(!tcfdbtune(fdb, RECBUFSIZ, EXHEADSIZ + (RECBUFSIZ + sizeof(int)) * rnum)){ eprint(fdb, __LINE__, "tcfdbtune"); err = true; } if(!tcfdbopen(fdb, path, FDBOWRITER | FDBOCREAT | FDBOTRUNC | omode)){ eprint(fdb, __LINE__, "tcfdbopen"); err = true; } if(TCUSEPTHREAD){ TCFDB *fdbdup = tcfdbnew(); if(tcfdbopen(fdbdup, path, FDBOREADER)){ eprint(fdb, __LINE__, "(validation)"); err = true; } else if(tcfdbecode(fdbdup) != TCETHREAD){ eprint(fdb, __LINE__, "(validation)"); err = true; } tcfdbdel(fdbdup); } iprintf("writing:\n"); for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", i); if(!tcfdbputkeep2(fdb, buf, len, buf, len)){ eprint(fdb, __LINE__, "tcfdbputkeep"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("reading:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", i); int vsiz; char *vbuf = tcfdbget2(fdb, kbuf, ksiz, &vsiz); if(!vbuf){ eprint(fdb, __LINE__, "tcfdbget"); err = true; break; } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){ eprint(fdb, __LINE__, "(validation)"); err = true; tcfree(vbuf); break; } tcfree(vbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(tcfdbrnum(fdb) != rnum){ eprint(fdb, __LINE__, "(validation)"); err = true; } iprintf("random writing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum) + 1); char vbuf[RECBUFSIZ]; int vsiz = myrand(RECBUFSIZ); memset(vbuf, '*', vsiz); if(!tcfdbput2(fdb, kbuf, ksiz, vbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbput"); err = true; break; } int rsiz; char *rbuf = tcfdbget2(fdb, kbuf, ksiz, &rsiz); if(!rbuf){ eprint(fdb, __LINE__, "tcfdbget"); err = true; break; } if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(fdb, __LINE__, "(validation)"); err = true; tcfree(rbuf); break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } tcfree(rbuf); } iprintf("random erasing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum) + 1); if(!tcfdbout2(fdb, kbuf, ksiz) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbout"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("writing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "[%d]", i); char vbuf[RECBUFSIZ]; int vsiz = i % RECBUFSIZ; memset(vbuf, '*', vsiz); if(!tcfdbputkeep2(fdb, kbuf, ksiz, vbuf, vsiz) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbputkeep"); err = true; break; } if(vsiz < 1){ char tbuf[PATH_MAX]; for(int j = 0; j < PATH_MAX; j++){ tbuf[j] = myrand(0x100); } if(!tcfdbput2(fdb, kbuf, ksiz, tbuf, PATH_MAX)){ eprint(fdb, __LINE__, "tcfdbput"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("erasing:\n"); for(int i = 1; i <= rnum; i++){ if(i % 2 == 1){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "[%d]", i); if(!tcfdbout2(fdb, kbuf, ksiz)){ eprint(fdb, __LINE__, "tcfdbout"); err = true; break; } if(tcfdbout2(fdb, kbuf, ksiz) || tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbout"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("random writing and reopening:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum) + 1); int vsiz = myrand(RECBUFSIZ); char *vbuf = tcmalloc(vsiz + 1); memset(vbuf, '*', vsiz); switch(myrand(3)){ case 0: if(!tcfdbput2(fdb, kbuf, ksiz, vbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbput"); err = true; } break; case 1: if(!tcfdbputcat2(fdb, kbuf, ksiz, vbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbputcat"); err = true; } break; case 2: if(!tcfdbout2(fdb, kbuf, ksiz) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbout"); err = true; } break; } tcfree(vbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tcfdbclose(fdb)){ eprint(fdb, __LINE__, "tcfdbclose"); err = true; } if(!tcfdbopen(fdb, path, FDBOWRITER | omode)){ eprint(fdb, __LINE__, "tcfdbopen"); err = true; } iprintf("checking:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "[%d]", i); int vsiz; char *vbuf = tcfdbget2(fdb, kbuf, ksiz, &vsiz); if(vbuf){ tcfree(vbuf); } else if(tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbget"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("writing:\n"); for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", i); if(!tcfdbput2(fdb, buf, len, buf, len)){ eprint(fdb, __LINE__, "tcfdbput"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("reading:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", i); int vsiz; char *vbuf = tcfdbget2(fdb, kbuf, ksiz, &vsiz); if(!vbuf){ eprint(fdb, __LINE__, "tcfdbget"); err = true; break; } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){ eprint(fdb, __LINE__, "(validation)"); err = true; tcfree(vbuf); break; } tcfree(vbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("checking iterator:\n"); if(!tcfdbiterinit(fdb)){ eprint(fdb, __LINE__, "tcfdbiterinit"); err = true; } char *kbuf; int ksiz; int inum = 0; for(int i = 1; (kbuf = tcfdbiternext2(fdb, &ksiz)) != NULL; i++, inum++){ int vsiz; char *vbuf = tcfdbget2(fdb, kbuf, ksiz, &vsiz); if(!vbuf){ eprint(fdb, __LINE__, "tcfdbget2"); err = true; tcfree(kbuf); break; } tcfree(vbuf); tcfree(kbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(tcfdbecode(fdb) != TCENOREC || inum != tcfdbrnum(fdb)){ eprint(fdb, __LINE__, "(validation)"); err = true; } iprintf("iteration updating:\n"); if(!tcfdbiterinit(fdb)){ eprint(fdb, __LINE__, "tcfdbiterinit"); err = true; } inum = 0; for(int i = 1; (kbuf = tcfdbiternext2(fdb, &ksiz)) != NULL; i++, inum++){ if(myrand(2) == 0){ if(!tcfdbputcat2(fdb, kbuf, ksiz, "0123456789", 10)){ eprint(fdb, __LINE__, "tcfdbputcat2"); err = true; tcfree(kbuf); break; } } else { if(!tcfdbout2(fdb, kbuf, ksiz)){ eprint(fdb, __LINE__, "tcfdbout"); err = true; tcfree(kbuf); break; } } tcfree(kbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(tcfdbecode(fdb) != TCENOREC || inum < tcfdbrnum(fdb)){ eprint(fdb, __LINE__, "(validation)"); err = true; } if(myrand(10) == 0 && !tcfdbsync(fdb)){ eprint(fdb, __LINE__, "tcfdbsync"); err = true; } if(!tcfdbvanish(fdb)){ eprint(fdb, __LINE__, "tcfdbvanish"); err = true; } TCMAP *map = tcmapnew(); iprintf("random writing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum) + 1); char vbuf[RECBUFSIZ]; int vsiz = sprintf(vbuf, "%d", myrand(rnum) + 1); switch(myrand(7)){ case 0: if(!tcfdbput2(fdb, kbuf, ksiz, vbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbput2"); err = true; } tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: if(!tcfdbputkeep2(fdb, kbuf, ksiz, vbuf, vsiz) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbputkeep2"); err = true; } tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 2: if(!tcfdbputcat2(fdb, kbuf, ksiz, vbuf, vsiz) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbputcat2"); err = true; } tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 3: if(!tcfdbout2(fdb, kbuf, ksiz) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbout2"); err = true; } tcmapout(map, kbuf, ksiz); break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("checking transaction commit:\n"); if(!tcfdbtranbegin(fdb)){ eprint(fdb, __LINE__, "tcfdbtranbegin"); err = true; } for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum) + 1); char vbuf[RECBUFSIZ]; int vsiz = sprintf(vbuf, "[%d]", myrand(rnum) + 1); switch(myrand(7)){ case 0: if(!tcfdbput2(fdb, kbuf, ksiz, vbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbput2"); err = true; } tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: if(!tcfdbputkeep2(fdb, kbuf, ksiz, vbuf, vsiz) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbputkeep2"); err = true; } tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 2: if(!tcfdbputcat2(fdb, kbuf, ksiz, vbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbputcat2"); err = true; } tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 3: if(tcfdbaddint(fdb, tcfdbkeytoid(kbuf, ksiz), 1) == INT_MIN && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbaddint"); err = true; } tcmapaddint(map, kbuf, ksiz, 1); break; case 4: if(isnan(tcfdbadddouble(fdb, tcfdbkeytoid(kbuf, ksiz), 1.0)) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbadddouble"); err = true; } tcmapadddouble(map, kbuf, ksiz, 1.0); break; case 5: if(myrand(2) == 0){ void *op = (void *)(intptr_t)(myrand(3) + 1); if(!tcfdbputproc(fdb, tcfdbkeytoid(kbuf, ksiz), vbuf, vsiz, pdprocfunc, op) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbputproc"); err = true; } tcmapputproc(map, kbuf, ksiz, vbuf, vsiz, pdprocfunc, op); } else { vsiz = myrand(10); void *op = (void *)(intptr_t)(myrand(3) + 1); if(!tcfdbputproc(fdb, tcfdbkeytoid(kbuf, ksiz), NULL, vsiz, pdprocfunc, op) && tcfdbecode(fdb) != TCEKEEP && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbputproc"); err = true; } tcmapputproc(map, kbuf, ksiz, NULL, vsiz, pdprocfunc, op); } break; case 6: if(!tcfdbout2(fdb, kbuf, ksiz) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbout2"); err = true; } tcmapout(map, kbuf, ksiz); break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tcfdbtrancommit(fdb)){ eprint(fdb, __LINE__, "tcfdbtrancommit"); err = true; } iprintf("checking transaction abort:\n"); uint64_t ornum = tcfdbrnum(fdb); uint64_t ofsiz = tcfdbfsiz(fdb); if(!tcfdbtranbegin(fdb)){ eprint(fdb, __LINE__, "tcfdbtranbegin"); err = true; } for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum) + 1); char vbuf[RECBUFSIZ]; int vsiz = sprintf(vbuf, "[%d]", myrand(rnum) + 1); switch(myrand(7)){ case 0: if(!tcfdbput2(fdb, kbuf, ksiz, vbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbput2"); err = true; } break; case 1: if(!tcfdbputkeep2(fdb, kbuf, ksiz, vbuf, vsiz) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbputkeep2"); err = true; } break; case 2: if(!tcfdbputcat2(fdb, kbuf, ksiz, vbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbputcat2"); err = true; } break; case 3: if(tcfdbaddint(fdb, tcfdbkeytoid(kbuf, ksiz), 1) == INT_MIN && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbaddint"); err = true; } break; case 4: if(isnan(tcfdbadddouble(fdb, tcfdbkeytoid(kbuf, ksiz), 1.0)) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbadddouble"); err = true; } break; case 5: if(myrand(2) == 0){ void *op = (void *)(intptr_t)(myrand(3) + 1); if(!tcfdbputproc(fdb, tcfdbkeytoid(kbuf, ksiz), vbuf, vsiz, pdprocfunc, op) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbputproc"); err = true; } } else { vsiz = myrand(10); void *op = (void *)(intptr_t)(myrand(3) + 1); if(!tcfdbputproc(fdb, tcfdbkeytoid(kbuf, ksiz), NULL, vsiz, pdprocfunc, op) && tcfdbecode(fdb) != TCEKEEP && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbputproc"); err = true; } } break; case 6: if(!tcfdbout2(fdb, kbuf, ksiz) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbout2"); err = true; } break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tcfdbtranabort(fdb)){ eprint(fdb, __LINE__, "tcfdbtranabort"); err = true; } iprintf("checking consistency:\n"); if(tcfdbrnum(fdb) != ornum || tcfdbfsiz(fdb) != ofsiz || tcfdbrnum(fdb) != tcmaprnum(map)){ eprint(fdb, __LINE__, "(validation)"); err = true; } inum = 0; tcmapiterinit(map); const char *tkbuf; int tksiz; for(int i = 1; (tkbuf = tcmapiternext(map, &tksiz)) != NULL; i++, inum++){ int tvsiz; const char *tvbuf = tcmapiterval(tkbuf, &tvsiz); if(tvsiz > RECBUFSIZ) tvsiz = RECBUFSIZ; int rsiz; char *rbuf = tcfdbget2(fdb, tkbuf, tksiz, &rsiz); if(!rbuf || rsiz != tvsiz || memcmp(rbuf, tvbuf, rsiz)){ eprint(fdb, __LINE__, "(validation)"); err = true; tcfree(rbuf); break; } tcfree(rbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); inum = 0; if(!tcfdbiterinit(fdb)){ eprint(fdb, __LINE__, "tcfdbiterinit"); err = true; } for(int i = 1; (kbuf = tcfdbiternext2(fdb, &ksiz)) != NULL; i++, inum++){ int vsiz; char *vbuf = tcfdbget2(fdb, kbuf, ksiz, &vsiz); int rsiz; const char *rbuf = tcmapget(map, kbuf, ksiz, &rsiz); if(rsiz > RECBUFSIZ) rsiz = RECBUFSIZ; if(!rbuf || rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(fdb, __LINE__, "(validation)"); err = true; tcfree(vbuf); tcfree(kbuf); break; } tcfree(vbuf); tcfree(kbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); tcmapdel(map); if(!tcfdbvanish(fdb)){ eprint(fdb, __LINE__, "tcfdbvanish"); err = true; } if(rnum >= 100){ if(!tcfdbtranbegin(fdb)){ eprint(fdb, __LINE__, "tcfdbtranbegin"); err = true; } if(!tcfdbput3(fdb, "99", "hirabayashi")){ eprint(fdb, __LINE__, "tcfdbput3"); err = true; } for(int i = 0; i < 10; i++){ char buf[RECBUFSIZ]; int size = sprintf(buf, "%d", myrand(rnum) + 1); if(!tcfdbput2(fdb, buf, size, buf, size)){ eprint(fdb, __LINE__, "tcfdbput2"); err = true; } } for(int i = myrand(3) + 1; i < PATH_MAX; i = i * 2 + myrand(3)){ char vbuf[i]; memset(vbuf, '@', i - 1); vbuf[i-1] = '\0'; if(!tcfdbput3(fdb, "99", vbuf)){ eprint(fdb, __LINE__, "tcfdbput3"); err = true; } } if(!tcfdbforeach(fdb, iterfunc, NULL)){ eprint(fdb, __LINE__, "tcfdbforeach"); err = true; } } iprintf("record number: %llu\n", (unsigned long long)tcfdbrnum(fdb)); iprintf("size: %llu\n", (unsigned long long)tcfdbfsiz(fdb)); mprint(fdb); sysprint(); if(!tcfdbclose(fdb)){ eprint(fdb, __LINE__, "tcfdbclose"); err = true; } tcfdbdel(fdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform wicked command */ static int procwicked(const char *path, int rnum, bool mt, int omode){ iprintf("\n seed=%u path=%s rnum=%d mt=%d omode=%d\n\n", g_randseed, path, rnum, mt, omode); bool err = false; double stime = tctime(); TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(mt && !tcfdbsetmutex(fdb)){ eprint(fdb, __LINE__, "tcfdbsetmutex"); err = true; } if(!tcfdbtune(fdb, RECBUFSIZ * 2, EXHEADSIZ + (RECBUFSIZ * 2 + sizeof(int)) * rnum)){ eprint(fdb, __LINE__, "tcfdbtune"); err = true; } if(!tcfdbopen(fdb, path, FDBOWRITER | FDBOCREAT | FDBOTRUNC | omode)){ eprint(fdb, __LINE__, "tcfdbopen"); err = true; } if(!tcfdbiterinit(fdb)){ eprint(fdb, __LINE__, "tcfdbiterinit"); err = true; } TCMAP *map = tcmapnew2(rnum / 5); for(int i = 1; i <= rnum && !err; i++){ uint64_t id = myrand(rnum) + 1; char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%llu", (unsigned long long)id); char vbuf[RECBUFSIZ]; int vsiz = myrand(RECBUFSIZ); memset(vbuf, '*', vsiz); vbuf[vsiz] = '\0'; char *rbuf; switch(myrand(16)){ case 0: iputchar('0'); if(!tcfdbput2(fdb, kbuf, ksiz, vbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbput2"); err = true; } tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: iputchar('1'); if(!tcfdbput3(fdb, kbuf, vbuf)){ eprint(fdb, __LINE__, "tcfdbput3"); err = true; } tcmapput2(map, kbuf, vbuf); break; case 2: iputchar('2'); if(!tcfdbputkeep2(fdb, kbuf, ksiz, vbuf, vsiz) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbputkeep2"); err = true; } tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 3: iputchar('3'); if(!tcfdbputkeep3(fdb, kbuf, vbuf) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbputkeep3"); err = true; } tcmapputkeep2(map, kbuf, vbuf); break; case 4: iputchar('4'); if(!tcfdbputcat2(fdb, kbuf, ksiz, vbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbputcat2"); err = true; } tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 5: iputchar('5'); if(!tcfdbputcat3(fdb, kbuf, vbuf)){ eprint(fdb, __LINE__, "tcfdbputcat3"); err = true; } tcmapputcat2(map, kbuf, vbuf); break; case 6: iputchar('6'); if(myrand(10) == 0){ if(!tcfdbout2(fdb, kbuf, ksiz) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbout2"); err = true; } tcmapout(map, kbuf, ksiz); } break; case 7: iputchar('7'); if(myrand(10) == 0){ if(!tcfdbout3(fdb, kbuf) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbout3"); err = true; } tcmapout2(map, kbuf); } break; case 8: iputchar('8'); if(!(rbuf = tcfdbget2(fdb, kbuf, ksiz, &vsiz))){ if(tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbget2"); err = true; } rbuf = tcsprintf("[%d]", myrand(i + 1)); vsiz = strlen(rbuf); } vsiz += myrand(vsiz); rbuf = tcrealloc(rbuf, vsiz + 1); for(int j = 0; j < vsiz; j++){ rbuf[j] = myrand(0x100); } if(!tcfdbput2(fdb, kbuf, ksiz, rbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbput2"); err = true; } tcmapput(map, kbuf, ksiz, rbuf, vsiz); tcfree(rbuf); break; case 9: iputchar('9'); if(!(rbuf = tcfdbget2(fdb, kbuf, ksiz, &vsiz)) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbget2"); err = true; } tcfree(rbuf); break; case 10: iputchar('A'); if(!(rbuf = tcfdbget3(fdb, kbuf)) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbget3"); err = true; } tcfree(rbuf); break; case 11: iputchar('B'); if(myrand(1) == 0) vsiz = 1; if((vsiz = tcfdbget4(fdb, id, vbuf, vsiz)) < 0 && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbget4"); err = true; } break; case 12: iputchar('C'); if(myrand(rnum / 128) == 0){ if(myrand(2) == 0){ if(!tcfdbiterinit(fdb)){ eprint(fdb, __LINE__, "tcfdbiterinit"); err = true; } } else { if(!tcfdbiterinit2(fdb, myrand(rnum) + 1) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbiterinit2"); err = true; } } } for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){ if(tcfdbiternext(fdb) < 0){ int ecode = tcfdbecode(fdb); if(ecode != TCEINVALID && ecode != TCENOREC){ eprint(fdb, __LINE__, "tcfdbiternext"); err = true; } } } break; default: iputchar('@'); if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX); if(myrand(rnum / 16 + 1) == 0){ int cnt = myrand(30); for(int j = 0; j < rnum && !err; j++){ ksiz = sprintf(kbuf, "%d", i + j); if(tcfdbout2(fdb, kbuf, ksiz)){ cnt--; } else if(tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbout2"); err = true; } tcmapout(map, kbuf, ksiz); if(cnt < 0) break; } } break; } if(i % 50 == 0) iprintf(" (%08d)\n", i); if(i == rnum / 2){ if(!tcfdbclose(fdb)){ eprint(fdb, __LINE__, "tcfdbclose"); err = true; } if(!tcfdbopen(fdb, path, FDBOWRITER | omode)){ eprint(fdb, __LINE__, "tcfdbopen"); err = true; } } else if(i == rnum / 4){ char *npath = tcsprintf("%s-tmp", path); if(!tcfdbcopy(fdb, npath)){ eprint(fdb, __LINE__, "tcfdbcopy"); err = true; } TCFDB *nfdb = tcfdbnew(); if(!tcfdbopen(nfdb, npath, FDBOREADER | omode)){ eprint(nfdb, __LINE__, "tcfdbopen"); err = true; } tcfdbdel(nfdb); unlink(npath); tcfree(npath); if(!tcfdboptimize(fdb, RECBUFSIZ, -1)){ eprint(fdb, __LINE__, "tcfdboptimize"); err = true; } if(!tcfdbiterinit(fdb)){ eprint(fdb, __LINE__, "tcfdbiterinit"); err = true; } } else if(i == rnum / 8){ if(!tcfdbtranbegin(fdb)){ eprint(fdb, __LINE__, "tcfdbtranbegin"); err = true; } } else if(i == rnum / 8 + rnum / 16){ if(!tcfdbtrancommit(fdb)){ eprint(fdb, __LINE__, "tcfdbtrancommit"); err = true; } } } if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum); if(!tcfdbsync(fdb)){ eprint(fdb, __LINE__, "tcfdbsync"); err = true; } if(tcfdbrnum(fdb) != tcmaprnum(map)){ eprint(fdb, __LINE__, "(validation)"); err = true; } for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", i); int vsiz; const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz); int rsiz; char *rbuf = tcfdbget2(fdb, kbuf, ksiz, &rsiz); if(vbuf){ iputchar('.'); if(vsiz > RECBUFSIZ) vsiz = RECBUFSIZ; if(!rbuf){ eprint(fdb, __LINE__, "tcfdbget2"); err = true; } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(fdb, __LINE__, "(validation)"); err = true; } } else { iputchar('*'); if(rbuf || tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "(validation)"); err = true; } } tcfree(rbuf); if(i % 50 == 0) iprintf(" (%08d)\n", i); } if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum); tcmapiterinit(map); int ksiz; const char *kbuf; for(int i = 1; (kbuf = tcmapiternext(map, &ksiz)) != NULL; i++){ iputchar('+'); int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); if(vsiz > tcfdbwidth(fdb)) vsiz = tcfdbwidth(fdb); int rsiz; char *rbuf = tcfdbget2(fdb, kbuf, ksiz, &rsiz); if(!rbuf){ eprint(fdb, __LINE__, "tcfdbget2"); err = true; } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(fdb, __LINE__, "(validation)"); err = true; } tcfree(rbuf); if(!tcfdbout2(fdb, kbuf, ksiz)){ eprint(fdb, __LINE__, "tcfdbout2"); err = true; } if(i % 50 == 0) iprintf(" (%08d)\n", i); } int mrnum = tcmaprnum(map); if(mrnum % 50 > 0) iprintf(" (%08d)\n", mrnum); if(tcfdbrnum(fdb) != 0){ eprint(fdb, __LINE__, "(validation)"); err = true; } iprintf("record number: %llu\n", (unsigned long long)tcfdbrnum(fdb)); iprintf("size: %llu\n", (unsigned long long)tcfdbfsiz(fdb)); mprint(fdb); sysprint(); tcmapdel(map); if(!tcfdbclose(fdb)){ eprint(fdb, __LINE__, "tcfdbclose"); err = true; } tcfdbdel(fdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } // END OF FILE tokyocabinet-1.4.48/tcbdb.c0000644000175000017500000037310112013574446014542 0ustar mikiomikio/************************************************************************************************* * The B+ tree database API of Tokyo Cabinet * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include "tcutil.h" #include "tchdb.h" #include "tcbdb.h" #include "myconf.h" #define BDBOPAQUESIZ 64 // size of using opaque field #define BDBLEFTOPQSIZ 64 // size of left opaque field #define BDBPAGEBUFSIZ 32768 // size of a buffer to read each page #define BDBNODEIDBASE ((1LL<<48)+1) // base number of node ID #define BDBLEVELMAX 64 // max level of B+ tree #define BDBCACHEOUT 8 // number of pages in a process of cacheout #define BDBDEFLMEMB 128 // default number of members in each leaf #define BDBMINLMEMB 4 // minimum number of members in each leaf #define BDBDEFNMEMB 256 // default number of members in each node #define BDBMINNMEMB 4 // minimum number of members in each node #define BDBDEFBNUM 32749 // default bucket number #define BDBDEFAPOW 8 // default alignment power #define BDBDEFFPOW 10 // default free block pool power #define BDBDEFLCNUM 1024 // default number of leaf cache #define BDBDEFNCNUM 512 // default number of node cache #define BDBDEFLSMAX 16384 // default maximum size of each leaf #define BDBMINLSMAX 512 // minimum maximum size of each leaf typedef struct { // type of structure for a record int ksiz; // size of the key region int vsiz; // size of the value region TCLIST *rest; // list of value objects } BDBREC; typedef struct { // type of structure for a leaf page uint64_t id; // ID number of the leaf TCPTRLIST *recs; // list of records int size; // predicted size of serialized buffer uint64_t prev; // ID number of the previous leaf uint64_t next; // ID number of the next leaf bool dirty; // whether to be written back bool dead; // whether to be removed } BDBLEAF; typedef struct { // type of structure for a page index uint64_t pid; // ID number of the referring page int ksiz; // size of the key region } BDBIDX; typedef struct { // type of structure for a node page uint64_t id; // ID number of the node uint64_t heir; // ID of the child before the first index TCPTRLIST *idxs; // list of indices bool dirty; // whether to be written back bool dead; // whether to be removed } BDBNODE; enum { // enumeration for duplication behavior BDBPDOVER, // overwrite an existing value BDBPDKEEP, // keep the existing value BDBPDCAT, // concatenate values BDBPDDUP, // allow duplication of keys BDBPDDUPB, // allow backward duplication BDBPDADDINT, // add an integer BDBPDADDDBL, // add a real number BDBPDPROC // process by a callback function }; typedef struct { // type of structure for a duplication callback TCPDPROC proc; // function pointer void *op; // opaque pointer } BDBPDPROCOP; /* private macros */ #define BDBLOCKMETHOD(TC_bdb, TC_wr) \ ((TC_bdb)->mmtx ? tcbdblockmethod((TC_bdb), (TC_wr)) : true) #define BDBUNLOCKMETHOD(TC_bdb) \ ((TC_bdb)->mmtx ? tcbdbunlockmethod(TC_bdb) : true) #define BDBLOCKCACHE(TC_bdb) \ ((TC_bdb)->mmtx ? tcbdblockcache(TC_bdb) : true) #define BDBUNLOCKCACHE(TC_bdb) \ ((TC_bdb)->mmtx ? tcbdbunlockcache(TC_bdb) : true) #define BDBTHREADYIELD(TC_bdb) \ do { if((TC_bdb)->mmtx) sched_yield(); } while(false) /* private function prototypes */ static void tcbdbclear(TCBDB *bdb); static void tcbdbdumpmeta(TCBDB *bdb); static void tcbdbloadmeta(TCBDB *bdb); static BDBLEAF *tcbdbleafnew(TCBDB *bdb, uint64_t prev, uint64_t next); static bool tcbdbleafcacheout(TCBDB *bdb, BDBLEAF *leaf); static bool tcbdbleafsave(TCBDB *bdb, BDBLEAF *leaf); static BDBLEAF *tcbdbleafload(TCBDB *bdb, uint64_t id); static bool tcbdbleafcheck(TCBDB *bdb, uint64_t id); static BDBLEAF *tcbdbgethistleaf(TCBDB *bdb, const char *kbuf, int ksiz, uint64_t id); static bool tcbdbleafaddrec(TCBDB *bdb, BDBLEAF *leaf, int dmode, const char *kbuf, int ksiz, const char *vbuf, int vsiz); static BDBLEAF *tcbdbleafdivide(TCBDB *bdb, BDBLEAF *leaf); static bool tcbdbleafkill(TCBDB *bdb, BDBLEAF *leaf); static BDBNODE *tcbdbnodenew(TCBDB *bdb, uint64_t heir); static bool tcbdbnodecacheout(TCBDB *bdb, BDBNODE *node); static bool tcbdbnodesave(TCBDB *bdb, BDBNODE *node); static BDBNODE *tcbdbnodeload(TCBDB *bdb, uint64_t id); static void tcbdbnodeaddidx(TCBDB *bdb, BDBNODE *node, bool order, uint64_t pid, const char *kbuf, int ksiz); static bool tcbdbnodesubidx(TCBDB *bdb, BDBNODE *node, uint64_t pid); static uint64_t tcbdbsearchleaf(TCBDB *bdb, const char *kbuf, int ksiz); static BDBREC *tcbdbsearchrec(TCBDB *bdb, BDBLEAF *leaf, const char *kbuf, int ksiz, int *ip); static void tcbdbremoverec(TCBDB *bdb, BDBLEAF *leaf, BDBREC *rec, int ri); static bool tcbdbcacheadjust(TCBDB *bdb); static void tcbdbcachepurge(TCBDB *bdb); static bool tcbdbopenimpl(TCBDB *bdb, const char *path, int omode); static bool tcbdbcloseimpl(TCBDB *bdb); static bool tcbdbputimpl(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int dmode); static bool tcbdboutimpl(TCBDB *bdb, const char *kbuf, int ksiz); static bool tcbdboutlist(TCBDB *bdb, const char *kbuf, int ksiz); static const char *tcbdbgetimpl(TCBDB *bdb, const char *kbuf, int ksiz, int *sp); static int tcbdbgetnum(TCBDB *bdb, const char *kbuf, int ksiz); static TCLIST *tcbdbgetlist(TCBDB *bdb, const char *kbuf, int ksiz); static bool tcbdbrangeimpl(TCBDB *bdb, const char *bkbuf, int bksiz, bool binc, const char *ekbuf, int eksiz, bool einc, int max, TCLIST *keys); static bool tcbdbrangefwm(TCBDB *bdb, const char *pbuf, int psiz, int max, TCLIST *keys); static bool tcbdboptimizeimpl(TCBDB *bdb, int32_t lmemb, int32_t nmemb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts); static bool tcbdbvanishimpl(TCBDB *bdb); static bool tcbdblockmethod(TCBDB *bdb, bool wr); static bool tcbdbunlockmethod(TCBDB *bdb); static bool tcbdblockcache(TCBDB *bdb); static bool tcbdbunlockcache(TCBDB *bdb); static bool tcbdbcurfirstimpl(BDBCUR *cur); static bool tcbdbcurlastimpl(BDBCUR *cur); static bool tcbdbcurjumpimpl(BDBCUR *cur, const char *kbuf, int ksiz, bool forward); static bool tcbdbcuradjust(BDBCUR *cur, bool forward); static bool tcbdbcurprevimpl(BDBCUR *cur); static bool tcbdbcurnextimpl(BDBCUR *cur); static bool tcbdbcurputimpl(BDBCUR *cur, const char *vbuf, int vsiz, int mode); static bool tcbdbcuroutimpl(BDBCUR *cur); static bool tcbdbcurrecimpl(BDBCUR *cur, const char **kbp, int *ksp, const char **vbp, int *vsp); static bool tcbdbforeachimpl(TCBDB *bdb, TCITER iter, void *op); /* debugging function prototypes */ void tcbdbprintmeta(TCBDB *bdb); void tcbdbprintleaf(TCBDB *bdb, BDBLEAF *leaf); void tcbdbprintnode(TCBDB *bdb, BDBNODE *node); /************************************************************************************************* * API *************************************************************************************************/ /* Get the message string corresponding to an error code. */ const char *tcbdberrmsg(int ecode){ return tcerrmsg(ecode); } /* Create a B+ tree database object. */ TCBDB *tcbdbnew(void){ TCBDB *bdb; TCMALLOC(bdb, sizeof(*bdb)); tcbdbclear(bdb); bdb->hdb = tchdbnew(); TCMALLOC(bdb->hist, sizeof(*bdb->hist) * BDBLEVELMAX); tchdbtune(bdb->hdb, BDBDEFBNUM, BDBDEFAPOW, BDBDEFFPOW, 0); tchdbsetxmsiz(bdb->hdb, 0); return bdb; } /* Delete a B+ tree database object. */ void tcbdbdel(TCBDB *bdb){ assert(bdb); if(bdb->open) tcbdbclose(bdb); TCFREE(bdb->hist); tchdbdel(bdb->hdb); if(bdb->mmtx){ pthread_mutex_destroy(bdb->cmtx); pthread_rwlock_destroy(bdb->mmtx); TCFREE(bdb->cmtx); TCFREE(bdb->mmtx); } TCFREE(bdb); } /* Get the last happened error code of a B+ tree database object. */ int tcbdbecode(TCBDB *bdb){ assert(bdb); return tchdbecode(bdb->hdb); } /* Set mutual exclusion control of a B+ tree database object for threading. */ bool tcbdbsetmutex(TCBDB *bdb){ assert(bdb); if(!TCUSEPTHREAD) return true; if(bdb->mmtx || bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } TCMALLOC(bdb->mmtx, sizeof(pthread_rwlock_t)); TCMALLOC(bdb->cmtx, sizeof(pthread_mutex_t)); bool err = false; if(pthread_rwlock_init(bdb->mmtx, NULL) != 0) err = true; if(pthread_mutex_init(bdb->cmtx, NULL) != 0) err = true; if(err){ TCFREE(bdb->cmtx); TCFREE(bdb->mmtx); bdb->cmtx = NULL; bdb->mmtx = NULL; return false; } return tchdbsetmutex(bdb->hdb); } /* Set the custom comparison function of a B+ tree database object. */ bool tcbdbsetcmpfunc(TCBDB *bdb, TCCMP cmp, void *cmpop){ assert(bdb && cmp); if(bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } bdb->cmp = cmp; bdb->cmpop = cmpop; return true; } /* 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){ assert(bdb); if(bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } bdb->lmemb = (lmemb > 0) ? tclmax(lmemb, BDBMINLMEMB) : BDBDEFLMEMB; bdb->nmemb = (nmemb > 0) ? tclmax(nmemb, BDBMINNMEMB) : BDBDEFNMEMB; bdb->opts = opts; uint8_t hopts = 0; if(opts & BDBTLARGE) hopts |= HDBTLARGE; if(opts & BDBTDEFLATE) hopts |= HDBTDEFLATE; if(opts & BDBTBZIP) hopts |= HDBTBZIP; if(opts & BDBTTCBS) hopts |= HDBTTCBS; if(opts & BDBTEXCODEC) hopts |= HDBTEXCODEC; bnum = (bnum > 0) ? bnum : BDBDEFBNUM; apow = (apow >= 0) ? apow : BDBDEFAPOW; fpow = (fpow >= 0) ? fpow : BDBDEFFPOW; return tchdbtune(bdb->hdb, bnum, apow, fpow, hopts); } /* Set the caching parameters of a B+ tree database object. */ bool tcbdbsetcache(TCBDB *bdb, int32_t lcnum, int32_t ncnum){ assert(bdb); if(bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } if(lcnum > 0) bdb->lcnum = tclmax(lcnum, BDBLEVELMAX); if(ncnum > 0) bdb->ncnum = tclmax(ncnum, BDBLEVELMAX); return true; } /* Set the size of the extra mapped memory of a B+ tree database object. */ bool tcbdbsetxmsiz(TCBDB *bdb, int64_t xmsiz){ assert(bdb); if(bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } return tchdbsetxmsiz(bdb->hdb, xmsiz); } /* Set the unit step number of auto defragmentation of a B+ tree database object. */ bool tcbdbsetdfunit(TCBDB *bdb, int32_t dfunit){ assert(bdb); if(bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } return tchdbsetdfunit(bdb->hdb, dfunit); } /* Open a database file and connect a B+ tree database object. */ bool tcbdbopen(TCBDB *bdb, const char *path, int omode){ assert(bdb && path); if(!BDBLOCKMETHOD(bdb, true)) return false; if(bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbopenimpl(bdb, path, omode); BDBUNLOCKMETHOD(bdb); return rv; } /* Close a B+ tree database object. */ bool tcbdbclose(TCBDB *bdb){ assert(bdb); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbcloseimpl(bdb); BDBUNLOCKMETHOD(bdb); return rv; } /* Store a record into a B+ tree database object. */ bool tcbdbput(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDOVER); BDBUNLOCKMETHOD(bdb); return rv; } /* Store a string record into a B+ tree database object. */ bool tcbdbput2(TCBDB *bdb, const char *kstr, const char *vstr){ assert(bdb && kstr && vstr); return tcbdbput(bdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Store a new record into a B+ tree database object. */ bool tcbdbputkeep(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDKEEP); BDBUNLOCKMETHOD(bdb); return rv; } /* Store a new string record into a B+ tree database object. */ bool tcbdbputkeep2(TCBDB *bdb, const char *kstr, const char *vstr){ assert(bdb && kstr && vstr); return tcbdbputkeep(bdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* 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){ assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDCAT); BDBUNLOCKMETHOD(bdb); return rv; } /* 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){ assert(bdb && kstr && vstr); return tcbdbputcat(bdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* 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){ assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDDUP); BDBUNLOCKMETHOD(bdb); return rv; } /* 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){ assert(bdb && kstr && vstr); return tcbdbputdup(bdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* 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){ assert(bdb && kbuf && ksiz >= 0 && vals); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool err = false; int ln = TCLISTNUM(vals); for(int i = 0; i < ln; i++){ const char *vbuf; int vsiz; TCLISTVAL(vbuf, vals, i, vsiz); if(!tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDDUP)) err = true; } BDBUNLOCKMETHOD(bdb); return !err; } /* Remove a record of a B+ tree database object. */ bool tcbdbout(TCBDB *bdb, const void *kbuf, int ksiz){ assert(bdb && kbuf && ksiz >= 0); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdboutimpl(bdb, kbuf, ksiz); BDBUNLOCKMETHOD(bdb); return rv; } /* Remove a string record of a B+ tree database object. */ bool tcbdbout2(TCBDB *bdb, const char *kstr){ assert(bdb && kstr); return tcbdbout(bdb, kstr, strlen(kstr)); } /* Remove records of a B+ tree database object. */ bool tcbdbout3(TCBDB *bdb, const void *kbuf, int ksiz){ assert(bdb && kbuf && ksiz >= 0); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdboutlist(bdb, kbuf, ksiz); BDBUNLOCKMETHOD(bdb); return rv; } /* Retrieve a record in a B+ tree database object. */ void *tcbdbget(TCBDB *bdb, const void *kbuf, int ksiz, int *sp){ assert(bdb && kbuf && ksiz >= 0 && sp); if(!BDBLOCKMETHOD(bdb, false)) return NULL; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return NULL; } const char *vbuf = tcbdbgetimpl(bdb, kbuf, ksiz, sp); char *rv; if(vbuf){ TCMEMDUP(rv, vbuf, *sp); } else { rv = NULL; } bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum; BDBUNLOCKMETHOD(bdb); if(adj && BDBLOCKMETHOD(bdb, true)){ if(!bdb->tran && !tcbdbcacheadjust(bdb)){ TCFREE(rv); rv = NULL; } BDBUNLOCKMETHOD(bdb); } return rv; } /* Retrieve a string record in a B+ tree database object. */ char *tcbdbget2(TCBDB *bdb, const char *kstr){ assert(bdb && kstr); int vsiz; return tcbdbget(bdb, kstr, strlen(kstr), &vsiz); } /* Retrieve a record in a B+ tree database object and write the value into a buffer. */ const void *tcbdbget3(TCBDB *bdb, const void *kbuf, int ksiz, int *sp){ assert(bdb && kbuf && ksiz >= 0 && sp); if(!BDBLOCKMETHOD(bdb, false)) return NULL; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return NULL; } const char *rv = tcbdbgetimpl(bdb, kbuf, ksiz, sp); bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum; BDBUNLOCKMETHOD(bdb); if(adj && BDBLOCKMETHOD(bdb, true)){ if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = NULL; BDBUNLOCKMETHOD(bdb); } return rv; } /* Retrieve records in a B+ tree database object. */ TCLIST *tcbdbget4(TCBDB *bdb, const void *kbuf, int ksiz){ assert(bdb && kbuf && ksiz >= 0); if(!BDBLOCKMETHOD(bdb, false)) return NULL; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return NULL; } TCLIST *rv = tcbdbgetlist(bdb, kbuf, ksiz); bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum; BDBUNLOCKMETHOD(bdb); if(adj && BDBLOCKMETHOD(bdb, true)){ if(!bdb->tran && !tcbdbcacheadjust(bdb)){ if(rv) tclistdel(rv); rv = NULL; } BDBUNLOCKMETHOD(bdb); } return rv; } /* Get the number of records corresponding a key in a B+ tree database object. */ int tcbdbvnum(TCBDB *bdb, const void *kbuf, int ksiz){ assert(bdb && kbuf && ksiz >= 0); if(!BDBLOCKMETHOD(bdb, false)) return 0; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return 0; } int rv = tcbdbgetnum(bdb, kbuf, ksiz); bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum; BDBUNLOCKMETHOD(bdb); if(adj && BDBLOCKMETHOD(bdb, true)){ if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = 0; BDBUNLOCKMETHOD(bdb); } return rv; } /* Get the number of records corresponding a string key in a B+ tree database object. */ int tcbdbvnum2(TCBDB *bdb, const char *kstr){ assert(bdb && kstr); return tcbdbvnum(bdb, kstr, strlen(kstr)); } /* Get the size of the value of a record in a B+ tree database object. */ int tcbdbvsiz(TCBDB *bdb, const void *kbuf, int ksiz){ assert(bdb && kbuf && ksiz >= 0); int vsiz; if(!tcbdbget3(bdb, kbuf, ksiz, &vsiz)) return -1; return vsiz; } /* Get the size of the value of a string record in a B+ tree database object. */ int tcbdbvsiz2(TCBDB *bdb, const char *kstr){ assert(bdb && kstr); return tcbdbvsiz(bdb, kstr, strlen(kstr)); } /* 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){ assert(bdb); TCLIST *keys = tclistnew(); if(!BDBLOCKMETHOD(bdb, false)) return keys; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return keys; } tcbdbrangeimpl(bdb, bkbuf, bksiz, binc, ekbuf, eksiz, einc, max, keys); bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum; BDBUNLOCKMETHOD(bdb); if(adj && BDBLOCKMETHOD(bdb, true)){ tcbdbcacheadjust(bdb); BDBUNLOCKMETHOD(bdb); } return keys; } /* 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){ assert(bdb); return tcbdbrange(bdb, bkstr, bkstr ? strlen(bkstr) : 0, binc, ekstr, ekstr ? strlen(ekstr) : 0, einc, max); } /* Get forward matching keys in a B+ tree database object. */ TCLIST *tcbdbfwmkeys(TCBDB *bdb, const void *pbuf, int psiz, int max){ assert(bdb && pbuf && psiz >= 0); TCLIST *keys = tclistnew(); if(!BDBLOCKMETHOD(bdb, false)) return keys; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return keys; } tcbdbrangefwm(bdb, pbuf, psiz, max, keys); bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum; BDBUNLOCKMETHOD(bdb); if(adj && BDBLOCKMETHOD(bdb, true)){ tcbdbcacheadjust(bdb); BDBUNLOCKMETHOD(bdb); } return keys; } /* Get forward matching string keys in a B+ tree database object. */ TCLIST *tcbdbfwmkeys2(TCBDB *bdb, const char *pstr, int max){ assert(bdb && pstr); return tcbdbfwmkeys(bdb, pstr, strlen(pstr), max); } /* Add an integer to a record in a B+ tree database object. */ int tcbdbaddint(TCBDB *bdb, const void *kbuf, int ksiz, int num){ assert(bdb && kbuf && ksiz >= 0); if(!BDBLOCKMETHOD(bdb, true)) return INT_MIN; if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return INT_MIN; } bool rv = tcbdbputimpl(bdb, kbuf, ksiz, (char *)&num, sizeof(num), BDBPDADDINT); BDBUNLOCKMETHOD(bdb); return rv ? num : INT_MIN; } /* Add a real number to a record in a B+ tree database object. */ double tcbdbadddouble(TCBDB *bdb, const void *kbuf, int ksiz, double num){ assert(bdb && kbuf && ksiz >= 0); if(!BDBLOCKMETHOD(bdb, true)) return nan(""); if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return nan(""); } bool rv = tcbdbputimpl(bdb, kbuf, ksiz, (char *)&num, sizeof(num), BDBPDADDDBL); BDBUNLOCKMETHOD(bdb); return rv ? num : nan(""); } /* Synchronize updated contents of a B+ tree database object with the file and the device. */ bool tcbdbsync(TCBDB *bdb){ assert(bdb); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode || bdb->tran){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbmemsync(bdb, true); BDBUNLOCKMETHOD(bdb); return rv; } /* 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){ assert(bdb); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode || bdb->tran){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } BDBTHREADYIELD(bdb); bool rv = tcbdboptimizeimpl(bdb, lmemb, nmemb, bnum, apow, fpow, opts); BDBUNLOCKMETHOD(bdb); return rv; } /* Remove all records of a B+ tree database object. */ bool tcbdbvanish(TCBDB *bdb){ assert(bdb); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode || bdb->tran){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } BDBTHREADYIELD(bdb); bool rv = tcbdbvanishimpl(bdb); BDBUNLOCKMETHOD(bdb); return rv; } /* Copy the database file of a B+ tree database object. */ bool tcbdbcopy(TCBDB *bdb, const char *path){ assert(bdb && path); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } BDBTHREADYIELD(bdb); TCLIST *lids = tclistnew(); TCLIST *nids = tclistnew(); const char *vbuf; int vsiz; TCMAP *leafc = bdb->leafc; tcmapiterinit(leafc); while((vbuf = tcmapiternext(leafc, &vsiz)) != NULL){ TCLISTPUSH(lids, vbuf, vsiz); } TCMAP *nodec = bdb->nodec; tcmapiterinit(nodec); while((vbuf = tcmapiternext(nodec, &vsiz)) != NULL){ TCLISTPUSH(nids, vbuf, vsiz); } BDBUNLOCKMETHOD(bdb); bool err = false; int ln = TCLISTNUM(lids); for(int i = 0; i < ln; i++){ vbuf = TCLISTVALPTR(lids, i); if(BDBLOCKMETHOD(bdb, true)){ BDBTHREADYIELD(bdb); if(bdb->open){ int rsiz; BDBLEAF *leaf = (BDBLEAF *)tcmapget(bdb->leafc, vbuf, sizeof(leaf->id), &rsiz); if(leaf && leaf->dirty && !tcbdbleafsave(bdb, leaf)) err = true; } else { err = true; } BDBUNLOCKMETHOD(bdb); } else { err = true; } } ln = TCLISTNUM(nids); for(int i = 0; i < ln; i++){ vbuf = TCLISTVALPTR(nids, i); if(BDBLOCKMETHOD(bdb, true)){ if(bdb->open){ int rsiz; BDBNODE *node = (BDBNODE *)tcmapget(bdb->nodec, vbuf, sizeof(node->id), &rsiz); if(node && node->dirty && !tcbdbnodesave(bdb, node)) err = true; } else { err = true; } BDBUNLOCKMETHOD(bdb); } else { err = true; } } tclistdel(nids); tclistdel(lids); if(!tcbdbtranbegin(bdb)) err = true; if(BDBLOCKMETHOD(bdb, false)){ BDBTHREADYIELD(bdb); if(!tchdbcopy(bdb->hdb, path)) err = true; BDBUNLOCKMETHOD(bdb); } else { err = true; } if(!tcbdbtrancommit(bdb)) err = true; return !err; } /* Begin the transaction of a B+ tree database object. */ bool tcbdbtranbegin(TCBDB *bdb){ assert(bdb); for(double wsec = 1.0 / sysconf(_SC_CLK_TCK); true; wsec *= 2){ if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } if(!bdb->tran) break; BDBUNLOCKMETHOD(bdb); if(wsec > 1.0) wsec = 1.0; tcsleep(wsec); } if(!tcbdbmemsync(bdb, false)){ BDBUNLOCKMETHOD(bdb); return false; } if(!tchdbtranbegin(bdb->hdb)){ BDBUNLOCKMETHOD(bdb); return false; } bdb->tran = true; TCMEMDUP(bdb->rbopaque, bdb->opaque, BDBOPAQUESIZ); BDBUNLOCKMETHOD(bdb); return true; } /* Commit the transaction of a B+ tree database object. */ bool tcbdbtrancommit(TCBDB *bdb){ assert(bdb); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode || !bdb->tran){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } TCFREE(bdb->rbopaque); bdb->tran = false; bdb->rbopaque = NULL; bool err = false; if(!tcbdbmemsync(bdb, false)) err = true; if(!tcbdbcacheadjust(bdb)) err = true; if(err){ tchdbtranabort(bdb->hdb); } else if(!tchdbtrancommit(bdb->hdb)){ err = true; } BDBUNLOCKMETHOD(bdb); return !err; } /* Abort the transaction of a B+ tree database object. */ bool tcbdbtranabort(TCBDB *bdb){ assert(bdb); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode || !bdb->tran){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } tcbdbcachepurge(bdb); memcpy(bdb->opaque, bdb->rbopaque, BDBOPAQUESIZ); tcbdbloadmeta(bdb); TCFREE(bdb->rbopaque); bdb->tran = false; bdb->rbopaque = NULL; bdb->hleaf = 0; bdb->lleaf = 0; bdb->clock++; bool err = false; if(!tcbdbcacheadjust(bdb)) err = true; if(!tchdbtranvoid(bdb->hdb)) err = true; BDBUNLOCKMETHOD(bdb); return !err; } /* Get the file path of a B+ tree database object. */ const char *tcbdbpath(TCBDB *bdb){ assert(bdb); if(!BDBLOCKMETHOD(bdb, false)) return NULL; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return NULL; } const char *rv = tchdbpath(bdb->hdb); BDBUNLOCKMETHOD(bdb); return rv; } /* Get the number of records of a B+ tree database object. */ uint64_t tcbdbrnum(TCBDB *bdb){ assert(bdb); if(!BDBLOCKMETHOD(bdb, false)) return 0; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return 0; } uint64_t rv = bdb->rnum; BDBUNLOCKMETHOD(bdb); return rv; } /* Get the size of the database file of a B+ tree database object. */ uint64_t tcbdbfsiz(TCBDB *bdb){ assert(bdb); if(!BDBLOCKMETHOD(bdb, false)) return 0; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return 0; } uint64_t rv = tchdbfsiz(bdb->hdb); BDBUNLOCKMETHOD(bdb); return rv; } /* Create a cursor object. */ BDBCUR *tcbdbcurnew(TCBDB *bdb){ assert(bdb); BDBCUR *cur; TCMALLOC(cur, sizeof(*cur)); cur->bdb = bdb; cur->clock = 0; cur->id = 0; cur->kidx = 0; cur->vidx = 0; return cur; } /* Delete a cursor object. */ void tcbdbcurdel(BDBCUR *cur){ assert(cur); TCFREE(cur); } /* Move a cursor object to the first record. */ bool tcbdbcurfirst(BDBCUR *cur){ assert(cur); TCBDB *bdb = cur->bdb; if(!BDBLOCKMETHOD(bdb, false)) return false; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbcurfirstimpl(cur); bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum; BDBUNLOCKMETHOD(bdb); if(adj && BDBLOCKMETHOD(bdb, true)){ if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = false; BDBUNLOCKMETHOD(bdb); } return rv; } /* Move a cursor object to the last record. */ bool tcbdbcurlast(BDBCUR *cur){ assert(cur); TCBDB *bdb = cur->bdb; if(!BDBLOCKMETHOD(bdb, false)) return false; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbcurlastimpl(cur); bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum; BDBUNLOCKMETHOD(bdb); if(adj && BDBLOCKMETHOD(bdb, true)){ if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = false; BDBUNLOCKMETHOD(bdb); } return rv; } /* Move a cursor object to the front of records corresponding a key. */ bool tcbdbcurjump(BDBCUR *cur, const void *kbuf, int ksiz){ assert(cur && kbuf && ksiz >= 0); TCBDB *bdb = cur->bdb; if(!BDBLOCKMETHOD(bdb, false)) return false; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbcurjumpimpl(cur, kbuf, ksiz, true); bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum; BDBUNLOCKMETHOD(bdb); if(adj && BDBLOCKMETHOD(bdb, true)){ if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = false; BDBUNLOCKMETHOD(bdb); } return rv; } /* Move a cursor object to the front of records corresponding a key string. */ bool tcbdbcurjump2(BDBCUR *cur, const char *kstr){ assert(cur && kstr); return tcbdbcurjump(cur, kstr, strlen(kstr)); } /* Move a cursor object to the previous record. */ bool tcbdbcurprev(BDBCUR *cur){ assert(cur); TCBDB *bdb = cur->bdb; if(!BDBLOCKMETHOD(bdb, false)) return false; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } if(cur->id < 1){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbcurprevimpl(cur); bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum; BDBUNLOCKMETHOD(bdb); if(adj && BDBLOCKMETHOD(bdb, true)){ if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = false; BDBUNLOCKMETHOD(bdb); } return rv; } /* Move a cursor object to the next record. */ bool tcbdbcurnext(BDBCUR *cur){ assert(cur); TCBDB *bdb = cur->bdb; if(!BDBLOCKMETHOD(bdb, false)) return false; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } if(cur->id < 1){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbcurnextimpl(cur); bool adj = TCMAPRNUM(bdb->leafc) > bdb->lcnum || TCMAPRNUM(bdb->nodec) > bdb->ncnum; BDBUNLOCKMETHOD(bdb); if(adj && BDBLOCKMETHOD(bdb, true)){ if(!bdb->tran && !tcbdbcacheadjust(bdb)) rv = false; BDBUNLOCKMETHOD(bdb); } return rv; } /* Insert a record around a cursor object. */ bool tcbdbcurput(BDBCUR *cur, const void *vbuf, int vsiz, int cpmode){ assert(cur && vbuf && vsiz >= 0); TCBDB *bdb = cur->bdb; if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } if(cur->id < 1){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbcurputimpl(cur, vbuf, vsiz, cpmode); BDBUNLOCKMETHOD(bdb); return rv; } /* Insert a string record around a cursor object. */ bool tcbdbcurput2(BDBCUR *cur, const char *vstr, int cpmode){ assert(cur && vstr); return tcbdbcurput(cur, vstr, strlen(vstr), cpmode); } /* Delete the record where a cursor object is. */ bool tcbdbcurout(BDBCUR *cur){ assert(cur); TCBDB *bdb = cur->bdb; if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } if(cur->id < 1){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbcuroutimpl(cur); BDBUNLOCKMETHOD(bdb); return rv; } /* Get the key of the record where the cursor object is. */ void *tcbdbcurkey(BDBCUR *cur, int *sp){ assert(cur && sp); TCBDB *bdb = cur->bdb; if(!BDBLOCKMETHOD(bdb, false)) return false; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } if(cur->id < 1){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } const char *kbuf, *vbuf; int ksiz, vsiz; char *rv; if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){ TCMEMDUP(rv, kbuf, ksiz); *sp = ksiz; } else { rv = NULL; } BDBUNLOCKMETHOD(bdb); return rv; } /* Get the key string of the record where the cursor object is. */ char *tcbdbcurkey2(BDBCUR *cur){ assert(cur); int ksiz; return tcbdbcurkey(cur, &ksiz); } /* Get the key of the record where the cursor object is, as a volatile buffer. */ const void *tcbdbcurkey3(BDBCUR *cur, int *sp){ assert(cur && sp); TCBDB *bdb = cur->bdb; if(!BDBLOCKMETHOD(bdb, false)) return false; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } if(cur->id < 1){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } const char *kbuf, *vbuf; int ksiz, vsiz; const char *rv; if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){ rv = kbuf; *sp = ksiz; } else { rv = NULL; } BDBUNLOCKMETHOD(bdb); return rv; } /* Get the value of the record where the cursor object is. */ void *tcbdbcurval(BDBCUR *cur, int *sp){ assert(cur && sp); TCBDB *bdb = cur->bdb; if(!BDBLOCKMETHOD(bdb, false)) return false; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } if(cur->id < 1){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } const char *kbuf, *vbuf; int ksiz, vsiz; char *rv; if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){ TCMEMDUP(rv, vbuf, vsiz); *sp = vsiz; } else { rv = NULL; } BDBUNLOCKMETHOD(bdb); return rv; } /* Get the value string of the record where the cursor object is. */ char *tcbdbcurval2(BDBCUR *cur){ assert(cur); int vsiz; return tcbdbcurval(cur, &vsiz); } /* Get the value of the record where the cursor object is, as a volatile buffer. */ const void *tcbdbcurval3(BDBCUR *cur, int *sp){ assert(cur && sp); TCBDB *bdb = cur->bdb; if(!BDBLOCKMETHOD(bdb, false)) return false; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } if(cur->id < 1){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } const char *kbuf, *vbuf; int ksiz, vsiz; const char *rv; if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){ rv = vbuf; *sp = vsiz; } else { rv = NULL; } BDBUNLOCKMETHOD(bdb); return rv; } /* Get the key and the value of the record where the cursor object is. */ bool tcbdbcurrec(BDBCUR *cur, TCXSTR *kxstr, TCXSTR *vxstr){ assert(cur && kxstr && vxstr); TCBDB *bdb = cur->bdb; if(!BDBLOCKMETHOD(bdb, false)) return false; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } if(cur->id < 1){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } const char *kbuf, *vbuf; int ksiz, vsiz; bool rv; if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){ tcxstrclear(kxstr); TCXSTRCAT(kxstr, kbuf, ksiz); tcxstrclear(vxstr); TCXSTRCAT(vxstr, vbuf, vsiz); rv = true; } else { rv = false; } BDBUNLOCKMETHOD(bdb); return rv; } /************************************************************************************************* * features for experts *************************************************************************************************/ /* Set the error code of a B+ tree database object. */ void tcbdbsetecode(TCBDB *bdb, int ecode, const char *filename, int line, const char *func){ assert(bdb && filename && line >= 1 && func); tchdbsetecode(bdb->hdb, ecode, filename, line, func); } /* Set the file descriptor for debugging output. */ void tcbdbsetdbgfd(TCBDB *bdb, int fd){ assert(bdb && fd >= 0); tchdbsetdbgfd(bdb->hdb, fd); } /* Get the file descriptor for debugging output. */ int tcbdbdbgfd(TCBDB *bdb){ assert(bdb); return tchdbdbgfd(bdb->hdb); } /* Check whether mutual exclusion control is set to a B+ tree database object. */ bool tcbdbhasmutex(TCBDB *bdb){ assert(bdb); return bdb->mmtx != NULL; } /* Synchronize updating contents on memory of a B+ tree database object. */ bool tcbdbmemsync(TCBDB *bdb, bool phys){ assert(bdb); if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } bool err = false; bool clk = BDBLOCKCACHE(bdb); const char *vbuf; int vsiz; TCMAP *leafc = bdb->leafc; tcmapiterinit(leafc); while((vbuf = tcmapiternext(leafc, &vsiz)) != NULL){ int rsiz; BDBLEAF *leaf = (BDBLEAF *)tcmapiterval(vbuf, &rsiz); if(leaf->dirty && !tcbdbleafsave(bdb, leaf)) err = true; } TCMAP *nodec = bdb->nodec; tcmapiterinit(nodec); while((vbuf = tcmapiternext(nodec, &vsiz)) != NULL){ int rsiz; BDBNODE *node = (BDBNODE *)tcmapiterval(vbuf, &rsiz); if(node->dirty && !tcbdbnodesave(bdb, node)) err = true; } if(clk) BDBUNLOCKCACHE(bdb); tcbdbdumpmeta(bdb); if(!tchdbmemsync(bdb->hdb, phys)) err = true; return !err; } /* Get the comparison function of a B+ tree database object. */ TCCMP tcbdbcmpfunc(TCBDB *bdb){ assert(bdb); return bdb->cmp; } /* Get the opaque object for the comparison function of a B+ tree database object. */ void *tcbdbcmpop(TCBDB *bdb){ assert(bdb); return bdb->cmpop; } /* Get the maximum number of cached leaf nodes of a B+ tree database object. */ uint32_t tcbdblmemb(TCBDB *bdb){ assert(bdb); return bdb->lmemb; } /* Get the maximum number of cached non-leaf nodes of a B+ tree database object. */ uint32_t tcbdbnmemb(TCBDB *bdb){ assert(bdb); return bdb->nmemb; } /* Get the number of the leaf nodes of B+ tree database object. */ uint64_t tcbdblnum(TCBDB *bdb){ assert(bdb); if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return bdb->lnum; } /* Get the number of the non-leaf nodes of B+ tree database object. */ uint64_t tcbdbnnum(TCBDB *bdb){ assert(bdb); if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return bdb->nnum; } /* Get the number of elements of the bucket array of a B+ tree database object. */ uint64_t tcbdbbnum(TCBDB *bdb){ assert(bdb); if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tchdbbnum(bdb->hdb); } /* Get the record alignment of a B+ tree database object. */ uint32_t tcbdbalign(TCBDB *bdb){ assert(bdb); if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tchdbalign(bdb->hdb); } /* Get the maximum number of the free block pool of a B+ tree database object. */ uint32_t tcbdbfbpmax(TCBDB *bdb){ assert(bdb); if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tchdbfbpmax(bdb->hdb); } /* Get the inode number of the database file of a B+ tree database object. */ uint64_t tcbdbinode(TCBDB *bdb){ assert(bdb); if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tchdbinode(bdb->hdb); } /* Get the modification time of the database file of a B+ tree database object. */ time_t tcbdbmtime(TCBDB *bdb){ assert(bdb); if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tchdbmtime(bdb->hdb); } /* Get the additional flags of a B+ tree database object. */ uint8_t tcbdbflags(TCBDB *bdb){ assert(bdb); if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tchdbflags(bdb->hdb); } /* Get the options of a B+ tree database object. */ uint8_t tcbdbopts(TCBDB *bdb){ assert(bdb); if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return bdb->opts; } /* Get the pointer to the opaque field of a B+ tree database object. */ char *tcbdbopaque(TCBDB *bdb){ assert(bdb); if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return NULL; } return bdb->opaque + BDBOPAQUESIZ; } /* Get the number of used elements of the bucket array of a B+ tree database object. */ uint64_t tcbdbbnumused(TCBDB *bdb){ assert(bdb); if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return tchdbbnumused(bdb->hdb); } /* Set the maximum size of each leaf node. */ bool tcbdbsetlsmax(TCBDB *bdb, uint32_t lsmax){ assert(bdb); if(bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } bdb->lsmax = (lsmax > 0) ? tclmax(lsmax, BDBMINLSMAX) : BDBDEFLSMAX; return true; } /* Set the capacity number of records. */ bool tcbdbsetcapnum(TCBDB *bdb, uint64_t capnum){ assert(bdb); if(bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); return false; } bdb->capnum = capnum; return true; } /* Set the custom codec functions of a B+ tree database object. */ bool tcbdbsetcodecfunc(TCBDB *bdb, TCCODEC enc, void *encop, TCCODEC dec, void *decop){ assert(bdb && enc && dec); if(!BDBLOCKMETHOD(bdb, true)) return false; if(bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tchdbsetcodecfunc(bdb->hdb, enc, encop, dec, decop); BDBUNLOCKMETHOD(bdb); return rv; } /* Get the unit step number of auto defragmentation of a B+ tree database object. */ uint32_t tcbdbdfunit(TCBDB *bdb){ assert(bdb); return tchdbdfunit(bdb->hdb); } /* Perform dynamic defragmentation of a B+ tree database object. */ bool tcbdbdefrag(TCBDB *bdb, int64_t step){ assert(bdb); if(!BDBLOCKMETHOD(bdb, false)) return false; if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tchdbdefrag(bdb->hdb, step); BDBUNLOCKMETHOD(bdb); return rv; } /* Clear the cache of a B+ tree database object. */ bool tcbdbcacheclear(TCBDB *bdb){ assert(bdb); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } BDBTHREADYIELD(bdb); bool err = false; bool tran = bdb->tran; if(TCMAPRNUM(bdb->leafc) > 0){ bool clk = BDBLOCKCACHE(bdb); TCMAP *leafc = bdb->leafc; tcmapiterinit(leafc); int rsiz; const void *buf; while((buf = tcmapiternext(leafc, &rsiz)) != NULL){ BDBLEAF *leaf = (BDBLEAF *)tcmapiterval(buf, &rsiz); if(!(tran && leaf->dirty) && !tcbdbleafcacheout(bdb, leaf)) err = true; } if(clk) BDBUNLOCKCACHE(bdb); } if(TCMAPRNUM(bdb->nodec) > 0){ bool clk = BDBLOCKCACHE(bdb); TCMAP *nodec = bdb->nodec; tcmapiterinit(nodec); int rsiz; const void *buf; while((buf = tcmapiternext(nodec, &rsiz)) != NULL){ BDBNODE *node = (BDBNODE *)tcmapiterval(buf, &rsiz); if(!(tran && node->dirty) && !tcbdbnodecacheout(bdb, node)) err = true; } if(clk) BDBUNLOCKCACHE(bdb); } BDBUNLOCKMETHOD(bdb); return !err; } /* Store a new record into a B+ tree database object with backward duplication. */ bool tcbdbputdupback(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(bdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDDUPB); BDBUNLOCKMETHOD(bdb); return rv; } /* Store a record into a B+ tree database object with a duplication handler. */ bool tcbdbputproc(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op){ assert(bdb && kbuf && ksiz >= 0 && proc); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open || !bdb->wmode){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } BDBPDPROCOP procop; procop.proc = proc; procop.op = op; BDBPDPROCOP *procptr = &procop; tcgeneric_t stack[(TCNUMBUFSIZ*2)/sizeof(tcgeneric_t)+1]; char *rbuf; if(ksiz <= sizeof(stack) - sizeof(procptr)){ rbuf = (char *)stack; } else { TCMALLOC(rbuf, ksiz + sizeof(procptr)); } char *wp = rbuf; memcpy(wp, &procptr, sizeof(procptr)); wp += sizeof(procptr); memcpy(wp, kbuf, ksiz); kbuf = rbuf + sizeof(procptr); bool rv = tcbdbputimpl(bdb, kbuf, ksiz, vbuf, vsiz, BDBPDPROC); if(rbuf != (char *)stack) TCFREE(rbuf); BDBUNLOCKMETHOD(bdb); return rv; } /* Store a new string record into a B+ tree database object with backward duplication. */ bool tcbdbputdupback2(TCBDB *bdb, const char *kstr, const char *vstr){ assert(bdb && kstr && vstr); return tcbdbputdupback(bdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Move a cursor object to the rear of records corresponding a key. */ bool tcbdbcurjumpback(BDBCUR *cur, const void *kbuf, int ksiz){ assert(cur && kbuf && ksiz >= 0); TCBDB *bdb = cur->bdb; if(!BDBLOCKMETHOD(bdb, false)) return false; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } bool rv = tcbdbcurjumpimpl(cur, kbuf, ksiz, false); BDBUNLOCKMETHOD(bdb); return rv; } /* Move a cursor object to the rear of records corresponding a key string. */ bool tcbdbcurjumpback2(BDBCUR *cur, const char *kstr){ assert(cur && kstr); return tcbdbcurjumpback(cur, kstr, strlen(kstr)); } /* Process each record atomically of a B+ tree database object. */ bool tcbdbforeach(TCBDB *bdb, TCITER iter, void *op){ assert(bdb && iter); if(!BDBLOCKMETHOD(bdb, true)) return false; if(!bdb->open){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); BDBUNLOCKMETHOD(bdb); return false; } BDBTHREADYIELD(bdb); bool rv = tcbdbforeachimpl(bdb, iter, op); BDBUNLOCKMETHOD(bdb); return rv; } /************************************************************************************************* * private features *************************************************************************************************/ /* Clear all members. `bdb' specifies the B+ tree database object. */ static void tcbdbclear(TCBDB *bdb){ assert(bdb); bdb->mmtx = NULL; bdb->cmtx = NULL; bdb->hdb = NULL; bdb->opaque = NULL; bdb->open = false; bdb->wmode = false; bdb->lmemb = BDBDEFLMEMB; bdb->nmemb = BDBDEFNMEMB; bdb->opts = 0; bdb->root = 0; bdb->first = 0; bdb->last = 0; bdb->lnum = 0; bdb->nnum = 0; bdb->rnum = 0; bdb->leafc = NULL; bdb->nodec = NULL; bdb->cmp = NULL; bdb->cmpop = NULL; bdb->lcnum = BDBDEFLCNUM; bdb->ncnum = BDBDEFNCNUM; bdb->lsmax = BDBDEFLSMAX; bdb->lschk = 0; bdb->capnum = 0; bdb->hist = NULL; bdb->hnum = 0; bdb->hleaf = 0; bdb->lleaf = 0; bdb->tran = false; bdb->rbopaque = NULL; bdb->clock = 0; bdb->cnt_saveleaf = -1; bdb->cnt_loadleaf = -1; bdb->cnt_killleaf = -1; bdb->cnt_adjleafc = -1; bdb->cnt_savenode = -1; bdb->cnt_loadnode = -1; bdb->cnt_adjnodec = -1; TCDODEBUG(bdb->cnt_saveleaf = 0); TCDODEBUG(bdb->cnt_loadleaf = 0); TCDODEBUG(bdb->cnt_killleaf = 0); TCDODEBUG(bdb->cnt_adjleafc = 0); TCDODEBUG(bdb->cnt_savenode = 0); TCDODEBUG(bdb->cnt_loadnode = 0); TCDODEBUG(bdb->cnt_adjnodec = 0); } /* Serialize meta data into the opaque field. `bdb' specifies the B+ tree database object. */ static void tcbdbdumpmeta(TCBDB *bdb){ assert(bdb); memset(bdb->opaque, 0, 64); char *wp = bdb->opaque; if(bdb->cmp == tccmplexical){ *(uint8_t *)(wp++) = 0x0; } else if(bdb->cmp == tccmpdecimal){ *(uint8_t *)(wp++) = 0x1; } else if(bdb->cmp == tccmpint32){ *(uint8_t *)(wp++) = 0x2; } else if(bdb->cmp == tccmpint64){ *(uint8_t *)(wp++) = 0x3; } else { *(uint8_t *)(wp++) = 0xff; } wp += 7; uint32_t lnum; lnum = bdb->lmemb; lnum = TCHTOIL(lnum); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); lnum = bdb->nmemb; lnum = TCHTOIL(lnum); memcpy(wp, &lnum, sizeof(lnum)); wp += sizeof(lnum); uint64_t llnum; llnum = bdb->root; llnum = TCHTOILL(llnum); memcpy(wp, &llnum, sizeof(llnum)); wp += sizeof(llnum); llnum = bdb->first; llnum = TCHTOILL(llnum); memcpy(wp, &llnum, sizeof(llnum)); wp += sizeof(llnum); llnum = bdb->last; llnum = TCHTOILL(llnum); memcpy(wp, &llnum, sizeof(llnum)); wp += sizeof(llnum); llnum = bdb->lnum; llnum = TCHTOILL(llnum); memcpy(wp, &llnum, sizeof(llnum)); wp += sizeof(llnum); llnum = bdb->nnum; llnum = TCHTOILL(llnum); memcpy(wp, &llnum, sizeof(llnum)); wp += sizeof(llnum); llnum = bdb->rnum; llnum = TCHTOILL(llnum); memcpy(wp, &llnum, sizeof(llnum)); wp += sizeof(llnum); } /* Deserialize meta data from the opaque field. `bdb' specifies the B+ tree database object. */ static void tcbdbloadmeta(TCBDB *bdb){ const char *rp = bdb->opaque; uint8_t cnum = *(uint8_t *)(rp++); if(cnum == 0x0){ bdb->cmp = tccmplexical; } else if(cnum == 0x1){ bdb->cmp = tccmpdecimal; } else if(cnum == 0x2){ bdb->cmp = tccmpint32; } else if(cnum == 0x3){ bdb->cmp = tccmpint64; } rp += 7; uint32_t lnum; memcpy(&lnum, rp, sizeof(lnum)); rp += sizeof(lnum); bdb->lmemb = TCITOHL(lnum); memcpy(&lnum, rp, sizeof(lnum)); rp += sizeof(lnum); bdb->nmemb = TCITOHL(lnum); uint64_t llnum; memcpy(&llnum, rp, sizeof(llnum)); bdb->root = TCITOHLL(llnum); rp += sizeof(llnum); memcpy(&llnum, rp, sizeof(llnum)); bdb->first = TCITOHLL(llnum); rp += sizeof(llnum); memcpy(&llnum, rp, sizeof(llnum)); bdb->last = TCITOHLL(llnum); rp += sizeof(llnum); memcpy(&llnum, rp, sizeof(llnum)); bdb->lnum = TCITOHLL(llnum); rp += sizeof(llnum); memcpy(&llnum, rp, sizeof(llnum)); bdb->nnum = TCITOHLL(llnum); rp += sizeof(llnum); memcpy(&llnum, rp, sizeof(llnum)); bdb->rnum = TCITOHLL(llnum); rp += sizeof(llnum); } /* Create a new leaf. `bdb' specifies the B+ tree database object. `prev' specifies the ID number of the previous leaf. `next' specifies the ID number of the next leaf. The return value is the new leaf object. */ static BDBLEAF *tcbdbleafnew(TCBDB *bdb, uint64_t prev, uint64_t next){ assert(bdb); BDBLEAF lent; lent.id = ++bdb->lnum; lent.recs = tcptrlistnew2(bdb->lmemb + 1); lent.size = 0; lent.prev = prev; lent.next = next; lent.dirty = true; lent.dead = false; tcmapputkeep(bdb->leafc, &(lent.id), sizeof(lent.id), &lent, sizeof(lent)); int rsiz; return (BDBLEAF *)tcmapget(bdb->leafc, &(lent.id), sizeof(lent.id), &rsiz); } /* Remove a leaf from the cache. `bdb' specifies the B+ tree database object. `leaf' specifies the leaf object. If successful, the return value is true, else, it is false. */ static bool tcbdbleafcacheout(TCBDB *bdb, BDBLEAF *leaf){ assert(bdb && leaf); bool err = false; if(leaf->dirty && !tcbdbleafsave(bdb, leaf)) err = true; TCPTRLIST *recs = leaf->recs; int ln = TCPTRLISTNUM(recs); for(int i = 0; i < ln; i++){ BDBREC *rec = TCPTRLISTVAL(recs, i); if(rec->rest) tclistdel(rec->rest); TCFREE(rec); } tcptrlistdel(recs); tcmapout(bdb->leafc, &(leaf->id), sizeof(leaf->id)); return !err; } /* Save a leaf into the internal database. `bdb' specifies the B+ tree database object. `leaf' specifies the leaf object. If successful, the return value is true, else, it is false. */ static bool tcbdbleafsave(TCBDB *bdb, BDBLEAF *leaf){ assert(bdb && leaf); TCDODEBUG(bdb->cnt_saveleaf++); TCXSTR *rbuf = tcxstrnew3(BDBPAGEBUFSIZ); char hbuf[(sizeof(uint64_t)+1)*3]; char *wp = hbuf; uint64_t llnum; int step; llnum = leaf->prev; TCSETVNUMBUF64(step, wp, llnum); wp += step; llnum = leaf->next; TCSETVNUMBUF64(step, wp, llnum); wp += step; TCXSTRCAT(rbuf, hbuf, wp - hbuf); TCPTRLIST *recs = leaf->recs; int ln = TCPTRLISTNUM(recs); for(int i = 0; i < ln; i++){ BDBREC *rec = TCPTRLISTVAL(recs, i); char *dbuf = (char *)rec + sizeof(*rec); int lnum; wp = hbuf; lnum = rec->ksiz; TCSETVNUMBUF(step, wp, lnum); wp += step; lnum = rec->vsiz; TCSETVNUMBUF(step, wp, lnum); wp += step; TCLIST *rest = rec->rest; int rnum = rest ? TCLISTNUM(rest) : 0; TCSETVNUMBUF(step, wp, rnum); wp += step; TCXSTRCAT(rbuf, hbuf, wp - hbuf); TCXSTRCAT(rbuf, dbuf, rec->ksiz); TCXSTRCAT(rbuf, dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz), rec->vsiz); for(int j = 0; j < rnum; j++){ const char *vbuf; int vsiz; TCLISTVAL(vbuf, rest, j, vsiz); TCSETVNUMBUF(step, hbuf, vsiz); TCXSTRCAT(rbuf, hbuf, step); TCXSTRCAT(rbuf, vbuf, vsiz); } } bool err = false; step = sprintf(hbuf, "%llx", (unsigned long long)leaf->id); if(ln < 1 && !tchdbout(bdb->hdb, hbuf, step) && tchdbecode(bdb->hdb) != TCENOREC) err = true; if(!leaf->dead && !tchdbput(bdb->hdb, hbuf, step, TCXSTRPTR(rbuf), TCXSTRSIZE(rbuf))) err = true; tcxstrdel(rbuf); leaf->dirty = false; leaf->dead = false; return !err; } /* Load a leaf from the internal database. `bdb' specifies the B+ tree database object. `id' specifies the ID number of the leaf. The return value is the leaf object or `NULL' on failure. */ static BDBLEAF *tcbdbleafload(TCBDB *bdb, uint64_t id){ assert(bdb && id > 0); bool clk = BDBLOCKCACHE(bdb); int rsiz; BDBLEAF *leaf = (BDBLEAF *)tcmapget3(bdb->leafc, &id, sizeof(id), &rsiz); if(leaf){ if(clk) BDBUNLOCKCACHE(bdb); return leaf; } if(clk) BDBUNLOCKCACHE(bdb); TCDODEBUG(bdb->cnt_loadleaf++); char hbuf[(sizeof(uint64_t)+1)*3]; int step; step = sprintf(hbuf, "%llx", (unsigned long long)id); char *rbuf = NULL; char wbuf[BDBPAGEBUFSIZ]; const char *rp = NULL; rsiz = tchdbget3(bdb->hdb, hbuf, step, wbuf, BDBPAGEBUFSIZ); if(rsiz < 1){ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__); return false; } else if(rsiz < BDBPAGEBUFSIZ){ rp = wbuf; } else { if(!(rbuf = tchdbget(bdb->hdb, hbuf, step, &rsiz))){ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__); return false; } rp = rbuf; } BDBLEAF lent; lent.id = id; uint64_t llnum; TCREADVNUMBUF64(rp, llnum, step); lent.prev = llnum; rp += step; rsiz -= step; TCREADVNUMBUF64(rp, llnum, step); lent.next = llnum; rp += step; rsiz -= step; lent.dirty = false; lent.dead = false; lent.recs = tcptrlistnew2(bdb->lmemb + 1); lent.size = 0; bool err = false; while(rsiz >= 3){ int ksiz; TCREADVNUMBUF(rp, ksiz, step); rp += step; rsiz -= step; int vsiz; TCREADVNUMBUF(rp, vsiz, step); rp += step; rsiz -= step; int rnum; TCREADVNUMBUF(rp, rnum, step); rp += step; rsiz -= step; if(rsiz < ksiz + vsiz + rnum){ err = true; break; } int psiz = TCALIGNPAD(ksiz); BDBREC *nrec; TCMALLOC(nrec, sizeof(*nrec) + ksiz + psiz + vsiz + 1); char *dbuf = (char *)nrec + sizeof(*nrec); memcpy(dbuf, rp, ksiz); dbuf[ksiz] = '\0'; nrec->ksiz = ksiz; rp += ksiz; rsiz -= ksiz; memcpy(dbuf + ksiz + psiz, rp, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; nrec->vsiz = vsiz; rp += vsiz; rsiz -= vsiz; lent.size += ksiz; lent.size += vsiz; if(rnum > 0){ nrec->rest = tclistnew2(rnum); while(rnum-- > 0 && rsiz > 0){ TCREADVNUMBUF(rp, vsiz, step); rp += step; rsiz -= step; if(rsiz < vsiz){ err = true; break; } TCLISTPUSH(nrec->rest, rp, vsiz); rp += vsiz; rsiz -= vsiz; lent.size += vsiz; } } else { nrec->rest = NULL; } TCPTRLISTPUSH(lent.recs, nrec); } TCFREE(rbuf); if(err || rsiz != 0){ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__); return NULL; } clk = BDBLOCKCACHE(bdb); if(!tcmapputkeep(bdb->leafc, &(lent.id), sizeof(lent.id), &lent, sizeof(lent))){ int ln = TCPTRLISTNUM(lent.recs); for(int i = 0; i < ln; i++){ BDBREC *rec = TCPTRLISTVAL(lent.recs, i); if(rec->rest) tclistdel(rec->rest); TCFREE(rec); } tcptrlistdel(lent.recs); } leaf = (BDBLEAF *)tcmapget(bdb->leafc, &(lent.id), sizeof(lent.id), &rsiz); if(clk) BDBUNLOCKCACHE(bdb); return leaf; } /* Check existence of a leaf in the internal database. `bdb' specifies the B+ tree database object. `id' specifies the ID number of the leaf. The return value is true if the leaf exists, else, it is false. */ static bool tcbdbleafcheck(TCBDB *bdb, uint64_t id){ assert(bdb && id > 0); bool clk = BDBLOCKCACHE(bdb); int rsiz; BDBLEAF *leaf = (BDBLEAF *)tcmapget(bdb->leafc, &id, sizeof(id), &rsiz); if(clk) BDBUNLOCKCACHE(bdb); if(leaf) return true; char hbuf[(sizeof(uint64_t)+1)*3]; int step = sprintf(hbuf, "%llx", (unsigned long long)id); return tchdbvsiz(bdb->hdb, hbuf, step) > 0; } /* Load the historical leaf from the internal database. `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. `id' specifies the ID number of the historical leaf. If successful, the return value is the pointer to the leaf, else, it is `NULL'. */ static BDBLEAF *tcbdbgethistleaf(TCBDB *bdb, const char *kbuf, int ksiz, uint64_t id){ assert(bdb && kbuf && ksiz >= 0 && id > 0); BDBLEAF *leaf = tcbdbleafload(bdb, id); if(!leaf) return NULL; int ln = TCPTRLISTNUM(leaf->recs); if(ln < 2) return NULL; BDBREC *rec = TCPTRLISTVAL(leaf->recs, 0); char *dbuf = (char *)rec + sizeof(*rec); int rv; if(bdb->cmp == tccmplexical){ TCCMPLEXICAL(rv, kbuf, ksiz, dbuf, rec->ksiz); } else { rv = bdb->cmp(kbuf, ksiz, dbuf, rec->ksiz, bdb->cmpop); } if(rv == 0) return leaf; if(rv < 0) return NULL; rec = TCPTRLISTVAL(leaf->recs, ln - 1); dbuf = (char *)rec + sizeof(*rec); if(bdb->cmp == tccmplexical){ TCCMPLEXICAL(rv, kbuf, ksiz, dbuf, rec->ksiz); } else { rv = bdb->cmp(kbuf, ksiz, dbuf, rec->ksiz, bdb->cmpop); } if(rv <= 0 || leaf->next < 1) return leaf; return NULL; } /* Add a record to a leaf. `bdb' specifies the B+ tree database object. `leaf' specifies the leaf object. `dmode' specifies behavior when the key overlaps. `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. */ static bool tcbdbleafaddrec(TCBDB *bdb, BDBLEAF *leaf, int dmode, const char *kbuf, int ksiz, const char *vbuf, int vsiz){ assert(bdb && leaf && kbuf && ksiz >= 0); TCCMP cmp = bdb->cmp; void *cmpop = bdb->cmpop; TCPTRLIST *recs = leaf->recs; int ln = TCPTRLISTNUM(recs); int left = 0; int right = ln; int i = (left + right) / 2; while(right >= left && i < ln){ BDBREC *rec = TCPTRLISTVAL(recs, i); char *dbuf = (char *)rec + sizeof(*rec); int rv; if(cmp == tccmplexical){ TCCMPLEXICAL(rv, kbuf, ksiz, dbuf, rec->ksiz); } else { rv = cmp(kbuf, ksiz, dbuf, rec->ksiz, cmpop); } if(rv == 0){ break; } else if(rv <= 0){ right = i - 1; } else { left = i + 1; } i = (left + right) / 2; } while(i < ln){ BDBREC *rec = TCPTRLISTVAL(recs, i); char *dbuf = (char *)rec + sizeof(*rec); int rv; if(cmp == tccmplexical){ TCCMPLEXICAL(rv, kbuf, ksiz, dbuf, rec->ksiz); } else { rv = cmp(kbuf, ksiz, dbuf, rec->ksiz, cmpop); } if(rv == 0){ int psiz = TCALIGNPAD(rec->ksiz); BDBREC *orec = rec; BDBPDPROCOP *procptr; int nvsiz; char *nvbuf; switch(dmode){ case BDBPDKEEP: tcbdbsetecode(bdb, TCEKEEP, __FILE__, __LINE__, __func__); return false; case BDBPDCAT: leaf->size += vsiz; TCREALLOC(rec, rec, sizeof(*rec) + rec->ksiz + psiz + rec->vsiz + vsiz + 1); if(rec != orec){ tcptrlistover(recs, i, rec); dbuf = (char *)rec + sizeof(*rec); } memcpy(dbuf + rec->ksiz + psiz + rec->vsiz, vbuf, vsiz); rec->vsiz += vsiz; dbuf[rec->ksiz+psiz+rec->vsiz] = '\0'; break; case BDBPDDUP: leaf->size += vsiz; if(!rec->rest) rec->rest = tclistnew2(1); TCLISTPUSH(rec->rest, vbuf, vsiz); bdb->rnum++; break; case BDBPDDUPB: leaf->size += vsiz; if(!rec->rest) rec->rest = tclistnew2(1); tclistunshift(rec->rest, dbuf + rec->ksiz + psiz, rec->vsiz); if(vsiz > rec->vsiz){ TCREALLOC(rec, rec, sizeof(*rec) + rec->ksiz + psiz + vsiz + 1); if(rec != orec){ tcptrlistover(recs, i, rec); dbuf = (char *)rec + sizeof(*rec); } } memcpy(dbuf + rec->ksiz + psiz, vbuf, vsiz); dbuf[rec->ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; bdb->rnum++; break; case BDBPDADDINT: if(rec->vsiz != sizeof(int)){ tcbdbsetecode(bdb, TCEKEEP, __FILE__, __LINE__, __func__); return false; } if(*(int *)vbuf == 0){ *(int *)vbuf = *(int *)(dbuf + rec->ksiz + psiz); return true; } *(int *)(dbuf + rec->ksiz + psiz) += *(int *)vbuf; *(int *)vbuf = *(int *)(dbuf + rec->ksiz + psiz); break; case BDBPDADDDBL: if(rec->vsiz != sizeof(double)){ tcbdbsetecode(bdb, TCEKEEP, __FILE__, __LINE__, __func__); return false; } if(*(double *)vbuf == 0.0){ *(double *)vbuf = *(double *)(dbuf + rec->ksiz + psiz); return true; } *(double *)(dbuf + rec->ksiz + psiz) += *(double *)vbuf; *(double *)vbuf = *(double *)(dbuf + rec->ksiz + psiz); break; case BDBPDPROC: procptr = *(BDBPDPROCOP **)((char *)kbuf - sizeof(procptr)); nvbuf = procptr->proc(dbuf + rec->ksiz + psiz, rec->vsiz, &nvsiz, procptr->op); if(nvbuf == (void *)-1){ tcbdbremoverec(bdb, leaf, rec, i); } else if(nvbuf){ leaf->size += nvsiz - rec->vsiz; if(nvsiz > rec->vsiz){ TCREALLOC(rec, rec, sizeof(*rec) + rec->ksiz + psiz + nvsiz + 1); if(rec != orec){ tcptrlistover(recs, i, rec); dbuf = (char *)rec + sizeof(*rec); } } memcpy(dbuf + rec->ksiz + psiz, nvbuf, nvsiz); dbuf[rec->ksiz+psiz+nvsiz] = '\0'; rec->vsiz = nvsiz; TCFREE(nvbuf); } else { tcbdbsetecode(bdb, TCEKEEP, __FILE__, __LINE__, __func__); return false; } break; default: leaf->size += vsiz - rec->vsiz; if(vsiz > rec->vsiz){ TCREALLOC(rec, rec, sizeof(*rec) + rec->ksiz + psiz + vsiz + 1); if(rec != orec){ tcptrlistover(recs, i, rec); dbuf = (char *)rec + sizeof(*rec); } } memcpy(dbuf + rec->ksiz + psiz, vbuf, vsiz); dbuf[rec->ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; break; } break; } else if(rv < 0){ if(!vbuf){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } leaf->size += ksiz + vsiz; int psiz = TCALIGNPAD(ksiz); BDBREC *nrec; TCMALLOC(nrec, sizeof(*nrec) + ksiz + psiz + vsiz + 1); char *dbuf = (char *)nrec + sizeof(*nrec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; nrec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; nrec->vsiz = vsiz; nrec->rest = NULL; TCPTRLISTINSERT(recs, i, nrec); bdb->rnum++; break; } i++; } if(i >= ln){ if(!vbuf){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } leaf->size += ksiz + vsiz; int psiz = TCALIGNPAD(ksiz); BDBREC *nrec; TCMALLOC(nrec, sizeof(*nrec) + ksiz + psiz + vsiz + 1); char *dbuf = (char *)nrec + sizeof(*nrec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; nrec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; nrec->vsiz = vsiz; nrec->rest = NULL; TCPTRLISTPUSH(recs, nrec); bdb->rnum++; } leaf->dirty = true; return true; } /* Divide a leaf into two. `bdb' specifies the B+ tree database object. `leaf' specifies the leaf object. The return value is the new leaf object or `NULL' on failure. */ static BDBLEAF *tcbdbleafdivide(TCBDB *bdb, BDBLEAF *leaf){ assert(bdb && leaf); bdb->hleaf = 0; TCPTRLIST *recs = leaf->recs; int mid = TCPTRLISTNUM(recs) / 2; BDBLEAF *newleaf = tcbdbleafnew(bdb, leaf->id, leaf->next); if(newleaf->next > 0){ BDBLEAF *nextleaf = tcbdbleafload(bdb, newleaf->next); if(!nextleaf) return NULL; nextleaf->prev = newleaf->id; nextleaf->dirty = true; } leaf->next = newleaf->id; leaf->dirty = true; int ln = TCPTRLISTNUM(recs); TCPTRLIST *newrecs = newleaf->recs; int nsiz = 0; for(int i = mid; i < ln; i++){ BDBREC *rec = TCPTRLISTVAL(recs, i); nsiz += rec->ksiz + rec->vsiz; if(rec->rest){ TCLIST *rest = rec->rest; int rnum = TCLISTNUM(rest); for(int j = 0; j < rnum; j++){ nsiz += TCLISTVALSIZ(rest, j); } } TCPTRLISTPUSH(newrecs, rec); } TCPTRLISTTRUNC(recs, TCPTRLISTNUM(recs) - TCPTRLISTNUM(newrecs)); leaf->size -= nsiz; newleaf->size = nsiz; return newleaf; } /* Cut off the path to a leaf and mark it dead. `bdb' specifies the B+ tree database object. `leaf' specifies the leaf object. If successful, the return value is true, else, it is false. */ static bool tcbdbleafkill(TCBDB *bdb, BDBLEAF *leaf){ assert(bdb && leaf); BDBNODE *node = tcbdbnodeload(bdb, bdb->hist[--bdb->hnum]); if(!node) return false; if(tcbdbnodesubidx(bdb, node, leaf->id)){ TCDODEBUG(bdb->cnt_killleaf++); if(bdb->hleaf == leaf->id) bdb->hleaf = 0; if(leaf->prev > 0){ BDBLEAF *tleaf = tcbdbleafload(bdb, leaf->prev); if(!tleaf) return false; tleaf->next = leaf->next; tleaf->dirty = true; if(bdb->last == leaf->id) bdb->last = leaf->prev; } if(leaf->next > 0){ BDBLEAF *tleaf = tcbdbleafload(bdb, leaf->next); if(!tleaf) return false; tleaf->prev = leaf->prev; tleaf->dirty = true; if(bdb->first == leaf->id) bdb->first = leaf->next; } leaf->dead = true; } bdb->clock++; return true; } /* Create a new node. `bdb' specifies the B+ tree database object. `heir' specifies the ID of the child before the first index. The return value is the new node object. */ static BDBNODE *tcbdbnodenew(TCBDB *bdb, uint64_t heir){ assert(bdb && heir > 0); BDBNODE nent; nent.id = ++bdb->nnum + BDBNODEIDBASE; nent.idxs = tcptrlistnew2(bdb->nmemb + 1); nent.heir = heir; nent.dirty = true; nent.dead = false; tcmapputkeep(bdb->nodec, &(nent.id), sizeof(nent.id), &nent, sizeof(nent)); int rsiz; return (BDBNODE *)tcmapget(bdb->nodec, &(nent.id), sizeof(nent.id), &rsiz); } /* Remove a node from the cache. `bdb' specifies the B+ tree database object. `node' specifies the node object. If successful, the return value is true, else, it is false. */ static bool tcbdbnodecacheout(TCBDB *bdb, BDBNODE *node){ assert(bdb && node); bool err = false; if(node->dirty && !tcbdbnodesave(bdb, node)) err = true; TCPTRLIST *idxs = node->idxs; int ln = TCPTRLISTNUM(idxs); for(int i = 0; i < ln; i++){ BDBIDX *idx = TCPTRLISTVAL(idxs, i); TCFREE(idx); } tcptrlistdel(idxs); tcmapout(bdb->nodec, &(node->id), sizeof(node->id)); return !err; } /* Save a node into the internal database. `bdb' specifies the B+ tree database object. `node' specifies the node object. If successful, the return value is true, else, it is false. */ static bool tcbdbnodesave(TCBDB *bdb, BDBNODE *node){ assert(bdb && node); TCDODEBUG(bdb->cnt_savenode++); TCXSTR *rbuf = tcxstrnew3(BDBPAGEBUFSIZ); char hbuf[(sizeof(uint64_t)+1)*2]; uint64_t llnum; int step; llnum = node->heir; TCSETVNUMBUF64(step, hbuf, llnum); TCXSTRCAT(rbuf, hbuf, step); TCPTRLIST *idxs = node->idxs; int ln = TCPTRLISTNUM(idxs); for(int i = 0; i < ln; i++){ BDBIDX *idx = TCPTRLISTVAL(idxs, i); char *ebuf = (char *)idx + sizeof(*idx); char *wp = hbuf; llnum = idx->pid; TCSETVNUMBUF64(step, wp, llnum); wp += step; uint32_t lnum = idx->ksiz; TCSETVNUMBUF(step, wp, lnum); wp += step; TCXSTRCAT(rbuf, hbuf, wp - hbuf); TCXSTRCAT(rbuf, ebuf, idx->ksiz); } bool err = false; step = sprintf(hbuf, "#%llx", (unsigned long long)(node->id - BDBNODEIDBASE)); if(ln < 1 && !tchdbout(bdb->hdb, hbuf, step) && tchdbecode(bdb->hdb) != TCENOREC) err = true; if(!node->dead && !tchdbput(bdb->hdb, hbuf, step, TCXSTRPTR(rbuf), TCXSTRSIZE(rbuf))) err = true; tcxstrdel(rbuf); node->dirty = false; node->dead = false; return !err; } /* Load a node from the internal database. `bdb' specifies the B+ tree database object. `id' specifies the ID number of the node. The return value is the node object or `NULL' on failure. */ static BDBNODE *tcbdbnodeload(TCBDB *bdb, uint64_t id){ assert(bdb && id > BDBNODEIDBASE); bool clk = BDBLOCKCACHE(bdb); int rsiz; BDBNODE *node = (BDBNODE *)tcmapget3(bdb->nodec, &id, sizeof(id), &rsiz); if(node){ if(clk) BDBUNLOCKCACHE(bdb); return node; } if(clk) BDBUNLOCKCACHE(bdb); TCDODEBUG(bdb->cnt_loadnode++); char hbuf[(sizeof(uint64_t)+1)*2]; int step; step = sprintf(hbuf, "#%llx", (unsigned long long)(id - BDBNODEIDBASE)); char *rbuf = NULL; char wbuf[BDBPAGEBUFSIZ]; const char *rp = NULL; rsiz = tchdbget3(bdb->hdb, hbuf, step, wbuf, BDBPAGEBUFSIZ); if(rsiz < 1){ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__); return NULL; } else if(rsiz < BDBPAGEBUFSIZ){ rp = wbuf; } else { if(!(rbuf = tchdbget(bdb->hdb, hbuf, step, &rsiz))){ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__); return NULL; } rp = rbuf; } BDBNODE nent; nent.id = id; uint64_t llnum; TCREADVNUMBUF64(rp, llnum, step); nent.heir = llnum; rp += step; rsiz -= step; nent.dirty = false; nent.dead = false; nent.idxs = tcptrlistnew2(bdb->nmemb + 1); bool err = false; while(rsiz >= 2){ uint64_t pid; TCREADVNUMBUF64(rp, pid, step); rp += step; rsiz -= step; int ksiz; TCREADVNUMBUF(rp, ksiz, step); rp += step; rsiz -= step; if(rsiz < ksiz){ err = true; break; } BDBIDX *nidx; TCMALLOC(nidx, sizeof(*nidx) + ksiz + 1); nidx->pid = pid; char *ebuf = (char *)nidx + sizeof(*nidx); memcpy(ebuf, rp, ksiz); ebuf[ksiz] = '\0'; nidx->ksiz = ksiz; rp += ksiz; rsiz -= ksiz; TCPTRLISTPUSH(nent.idxs, nidx); } TCFREE(rbuf); if(err || rsiz != 0){ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__); return NULL; } clk = BDBLOCKCACHE(bdb); if(!tcmapputkeep(bdb->nodec, &(nent.id), sizeof(nent.id), &nent, sizeof(nent))){ int ln = TCPTRLISTNUM(nent.idxs); for(int i = 0; i < ln; i++){ BDBIDX *idx = TCPTRLISTVAL(nent.idxs, i); TCFREE(idx); } tcptrlistdel(nent.idxs); } node = (BDBNODE *)tcmapget(bdb->nodec, &(nent.id), sizeof(nent.id), &rsiz); if(clk) BDBUNLOCKCACHE(bdb); return node; } /* Add an index to a node. `bdb' specifies the B+ tree database object. `node' specifies the node object. `order' specifies whether the calling sequence is orderd or not. `pid' specifies the ID number of referred page. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. */ static void tcbdbnodeaddidx(TCBDB *bdb, BDBNODE *node, bool order, uint64_t pid, const char *kbuf, int ksiz){ assert(bdb && node && pid > 0 && kbuf && ksiz >= 0); BDBIDX *nidx; TCMALLOC(nidx, sizeof(*nidx) + ksiz + 1); nidx->pid = pid; char *ebuf = (char *)nidx + sizeof(*nidx); memcpy(ebuf, kbuf, ksiz); ebuf[ksiz] = '\0'; nidx->ksiz = ksiz; TCCMP cmp = bdb->cmp; void *cmpop = bdb->cmpop; TCPTRLIST *idxs = node->idxs; if(order){ TCPTRLISTPUSH(idxs, nidx); } else { int ln = TCPTRLISTNUM(idxs); int left = 0; int right = ln; int i = (left + right) / 2; while(right >= left && i < ln){ BDBIDX *idx = TCPTRLISTVAL(idxs, i); char *ebuf = (char *)idx + sizeof(*idx); int rv; if(cmp == tccmplexical){ TCCMPLEXICAL(rv, kbuf, ksiz, ebuf, idx->ksiz); } else { rv = cmp(kbuf, ksiz, ebuf, idx->ksiz, cmpop); } if(rv == 0){ break; } else if(rv <= 0){ right = i - 1; } else { left = i + 1; } i = (left + right) / 2; } while(i < ln){ BDBIDX *idx = TCPTRLISTVAL(idxs, i); char *ebuf = (char *)idx + sizeof(*idx); int rv; if(cmp == tccmplexical){ TCCMPLEXICAL(rv, kbuf, ksiz, ebuf, idx->ksiz); } else { rv = cmp(kbuf, ksiz, ebuf, idx->ksiz, cmpop); } if(rv < 0){ TCPTRLISTINSERT(idxs, i, nidx); break; } i++; } if(i >= ln) TCPTRLISTPUSH(idxs, nidx); } node->dirty = true; } /* Subtract an index from a node. `bdb' specifies the B+ tree database object. `node' specifies the node object. `pid' specifies the ID number of referred page. The return value is whether the subtraction is completed. */ static bool tcbdbnodesubidx(TCBDB *bdb, BDBNODE *node, uint64_t pid){ assert(bdb && node && pid > 0); node->dirty = true; TCPTRLIST *idxs = node->idxs; if(node->heir == pid){ if(TCPTRLISTNUM(idxs) > 0){ BDBIDX *idx = tcptrlistshift(idxs); assert(idx); node->heir = idx->pid; TCFREE(idx); return true; } else if(bdb->hnum > 0){ BDBNODE *pnode = tcbdbnodeload(bdb, bdb->hist[--bdb->hnum]); if(!pnode){ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__); return false; } node->dead = true; return tcbdbnodesubidx(bdb, pnode, node->id); } node->dead = true; bdb->root = pid; while(pid > BDBNODEIDBASE){ node = tcbdbnodeload(bdb, pid); if(!node){ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__); return false; } if(node->dead){ pid = node->heir; bdb->root = pid; } else { pid = 0; } } return false; } int ln = TCPTRLISTNUM(idxs); for(int i = 0; i < ln; i++){ BDBIDX *idx = TCPTRLISTVAL(idxs, i); if(idx->pid == pid){ TCFREE(tcptrlistremove(idxs, i)); return true; } } tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__); return false; } /* Search the leaf object corresponding to a key. `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. The return value is the ID number of the leaf object or 0 on failure. */ static uint64_t tcbdbsearchleaf(TCBDB *bdb, const char *kbuf, int ksiz){ assert(bdb && kbuf && ksiz >= 0); TCCMP cmp = bdb->cmp; void *cmpop = bdb->cmpop; uint64_t *hist = bdb->hist; uint64_t pid = bdb->root; int hnum = 0; bdb->hleaf = 0; while(pid > BDBNODEIDBASE){ BDBNODE *node = tcbdbnodeload(bdb, pid); if(!node){ tcbdbsetecode(bdb, TCEMISC, __FILE__, __LINE__, __func__); return 0; } hist[hnum++] = node->id; TCPTRLIST *idxs = node->idxs; int ln = TCPTRLISTNUM(idxs); if(ln > 0){ int left = 0; int right = ln; int i = (left + right) / 2; BDBIDX *idx = NULL; while(right >= left && i < ln){ idx = TCPTRLISTVAL(idxs, i); char *ebuf = (char *)idx + sizeof(*idx); int rv; if(cmp == tccmplexical){ TCCMPLEXICAL(rv, kbuf, ksiz, ebuf, idx->ksiz); } else { rv = cmp(kbuf, ksiz, ebuf, idx->ksiz, cmpop); } if(rv == 0){ break; } else if(rv <= 0){ right = i - 1; } else { left = i + 1; } i = (left + right) / 2; } if(i > 0) i--; while(i < ln){ idx = TCPTRLISTVAL(idxs, i); char *ebuf = (char *)idx + sizeof(*idx); int rv; if(cmp == tccmplexical){ TCCMPLEXICAL(rv, kbuf, ksiz, ebuf, idx->ksiz); } else { rv = cmp(kbuf, ksiz, ebuf, idx->ksiz, cmpop); } if(rv < 0){ if(i == 0){ pid = node->heir; break; } idx = TCPTRLISTVAL(idxs, i - 1); pid = idx->pid; break; } i++; } if(i >= ln) pid = idx->pid; } else { pid = node->heir; } } if(bdb->lleaf == pid) bdb->hleaf = pid; bdb->lleaf = pid; bdb->hnum = hnum; return pid; } /* Search a record of a leaf. `bdb' specifies the B+ tree database object. `leaf' specifies the leaf object. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. `ip' specifies the pointer to a variable to fetch the index of the correspnding record. The return value is the pointer to a corresponding record or `NULL' on failure. */ static BDBREC *tcbdbsearchrec(TCBDB *bdb, BDBLEAF *leaf, const char *kbuf, int ksiz, int *ip){ assert(bdb && leaf && kbuf && ksiz >= 0); TCCMP cmp = bdb->cmp; void *cmpop = bdb->cmpop; TCPTRLIST *recs = leaf->recs; int ln = TCPTRLISTNUM(recs); int left = 0; int right = ln; int i = (left + right) / 2; while(right >= left && i < ln){ BDBREC *rec = TCPTRLISTVAL(recs, i); char *dbuf = (char *)rec + sizeof(*rec); int rv; if(cmp == tccmplexical){ TCCMPLEXICAL(rv, kbuf, ksiz, dbuf, rec->ksiz); } else { rv = cmp(kbuf, ksiz, dbuf, rec->ksiz, cmpop); } if(rv == 0){ if(ip) *ip = i; return rec; } else if(rv <= 0){ right = i - 1; } else { left = i + 1; } i = (left + right) / 2; } if(ip) *ip = i; return NULL; } /* Remove a record from a leaf. `bdb' specifies the B+ tree database object. `rec' specifies the record object. `ri' specifies the index of the record. */ static void tcbdbremoverec(TCBDB *bdb, BDBLEAF *leaf, BDBREC *rec, int ri){ assert(bdb && leaf && rec && ri >= 0); if(rec->rest){ leaf->size -= rec->vsiz; int vsiz; char *vbuf = tclistshift(rec->rest, &vsiz); int psiz = TCALIGNPAD(rec->ksiz); if(vsiz > rec->vsiz){ BDBREC *orec = rec; TCREALLOC(rec, rec, sizeof(*rec) + rec->ksiz + psiz + vsiz + 1); if(rec != orec) tcptrlistover(leaf->recs, ri, rec); } char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf + rec->ksiz + psiz, vbuf, vsiz); dbuf[rec->ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; TCFREE(vbuf); if(TCLISTNUM(rec->rest) < 1){ tclistdel(rec->rest); rec->rest = NULL; } } else { leaf->size -= rec->ksiz + rec->vsiz; TCFREE(tcptrlistremove(leaf->recs, ri)); } bdb->rnum--; } /* Adjust the caches for leaves and nodes. `bdb' specifies the B+ tree database object. The return value is true if successful, else, it is false. */ static bool tcbdbcacheadjust(TCBDB *bdb){ bool err = false; if(TCMAPRNUM(bdb->leafc) > bdb->lcnum){ TCDODEBUG(bdb->cnt_adjleafc++); int ecode = tchdbecode(bdb->hdb); bool clk = BDBLOCKCACHE(bdb); TCMAP *leafc = bdb->leafc; tcmapiterinit(leafc); int dnum = tclmax(TCMAPRNUM(bdb->leafc) - bdb->lcnum, BDBCACHEOUT); for(int i = 0; i < dnum; i++){ int rsiz; if(!tcbdbleafcacheout(bdb, (BDBLEAF *)tcmapiterval(tcmapiternext(leafc, &rsiz), &rsiz))) err = true; } if(clk) BDBUNLOCKCACHE(bdb); if(!err && tchdbecode(bdb->hdb) != ecode) tcbdbsetecode(bdb, ecode, __FILE__, __LINE__, __func__); } if(TCMAPRNUM(bdb->nodec) > bdb->ncnum){ TCDODEBUG(bdb->cnt_adjnodec++); int ecode = tchdbecode(bdb->hdb); bool clk = BDBLOCKCACHE(bdb); TCMAP *nodec = bdb->nodec; tcmapiterinit(nodec); int dnum = tclmax(TCMAPRNUM(bdb->nodec) - bdb->ncnum, BDBCACHEOUT); for(int i = 0; i < dnum; i++){ int rsiz; if(!tcbdbnodecacheout(bdb, (BDBNODE *)tcmapiterval(tcmapiternext(nodec, &rsiz), &rsiz))) err = true; } if(clk) BDBUNLOCKCACHE(bdb); if(!err && tchdbecode(bdb->hdb) != ecode) tcbdbsetecode(bdb, ecode, __FILE__, __LINE__, __func__); } return !err; } /* Purge dirty pages of caches for leaves and nodes. `bdb' specifies the B+ tree database object. */ static void tcbdbcachepurge(TCBDB *bdb){ bool clk = BDBLOCKCACHE(bdb); int tsiz; const char *tmp; tcmapiterinit(bdb->leafc); while((tmp = tcmapiternext(bdb->leafc, &tsiz)) != NULL){ int lsiz; BDBLEAF *leaf = (BDBLEAF *)tcmapiterval(tmp, &lsiz); if(!leaf->dirty) continue; TCPTRLIST *recs = leaf->recs; int ln = TCPTRLISTNUM(recs); for(int i = 0; i < ln; i++){ BDBREC *rec = TCPTRLISTVAL(recs, i); if(rec->rest) tclistdel(rec->rest); TCFREE(rec); } tcptrlistdel(recs); tcmapout(bdb->leafc, tmp, tsiz); } tcmapiterinit(bdb->nodec); while((tmp = tcmapiternext(bdb->nodec, &tsiz)) != NULL){ int nsiz; BDBNODE *node = (BDBNODE *)tcmapiterval(tmp, &nsiz); if(!node->dirty) continue; TCPTRLIST *idxs = node->idxs; int ln = TCPTRLISTNUM(idxs); for(int i = 0; i < ln; i++){ BDBIDX *idx = TCPTRLISTVAL(idxs, i); TCFREE(idx); } tcptrlistdel(idxs); tcmapout(bdb->nodec, tmp, tsiz); } if(clk) BDBUNLOCKCACHE(bdb); } /* Open a database file and connect a B+ tree database object. `bdb' specifies the B+ tree database object. `path' specifies the path of the internal database file. `omode' specifies the connection mode. If successful, the return value is true, else, it is false. */ static bool tcbdbopenimpl(TCBDB *bdb, const char *path, int omode){ assert(bdb && path); int homode = HDBOREADER; if(omode & BDBOWRITER){ homode = HDBOWRITER; if(omode & BDBOCREAT) homode |= HDBOCREAT; if(omode & BDBOTRUNC) homode |= HDBOTRUNC; bdb->wmode = true; } else { bdb->wmode = false; } if(omode & BDBONOLCK) homode |= HDBONOLCK; if(omode & BDBOLCKNB) homode |= HDBOLCKNB; if(omode & BDBOTSYNC) homode |= HDBOTSYNC; tchdbsettype(bdb->hdb, TCDBTBTREE); if(!tchdbopen(bdb->hdb, path, homode)) return false; bdb->root = 0; bdb->first = 0; bdb->last = 0; bdb->lnum = 0; bdb->nnum = 0; bdb->rnum = 0; bdb->opaque = tchdbopaque(bdb->hdb); bdb->leafc = tcmapnew2(bdb->lcnum * 2 + 1); bdb->nodec = tcmapnew2(bdb->ncnum * 2 + 1); if(bdb->wmode && tchdbrnum(bdb->hdb) < 1){ BDBLEAF *leaf = tcbdbleafnew(bdb, 0, 0); bdb->root = leaf->id; bdb->first = leaf->id; bdb->last = leaf->id; bdb->lnum = 1; bdb->nnum = 0; bdb->rnum = 0; if(!bdb->cmp){ bdb->cmp = tccmplexical; bdb->cmpop = NULL; } tcbdbdumpmeta(bdb); if(!tcbdbleafsave(bdb, leaf)){ tcmapdel(bdb->nodec); tcmapdel(bdb->leafc); tchdbclose(bdb->hdb); return false; } } tcbdbloadmeta(bdb); if(!bdb->cmp){ tcbdbsetecode(bdb, TCEINVALID, __FILE__, __LINE__, __func__); tcmapdel(bdb->nodec); tcmapdel(bdb->leafc); tchdbclose(bdb->hdb); return false; } if(bdb->lmemb < BDBMINLMEMB || bdb->nmemb < BDBMINNMEMB || bdb->root < 1 || bdb->first < 1 || bdb->last < 1 || bdb->lnum < 0 || bdb->nnum < 0 || bdb->rnum < 0){ tcbdbsetecode(bdb, TCEMETA, __FILE__, __LINE__, __func__); tcmapdel(bdb->nodec); tcmapdel(bdb->leafc); tchdbclose(bdb->hdb); return false; } bdb->open = true; uint8_t hopts = tchdbopts(bdb->hdb); uint8_t opts = 0; if(hopts & HDBTLARGE) opts |= BDBTLARGE; if(hopts & HDBTDEFLATE) opts |= BDBTDEFLATE; if(hopts & HDBTBZIP) opts |= BDBTBZIP; if(hopts & HDBTTCBS) opts |= BDBTTCBS; if(hopts & HDBTEXCODEC) opts |= BDBTEXCODEC; bdb->opts = opts; bdb->hleaf = 0; bdb->lleaf = 0; bdb->tran = false; bdb->rbopaque = NULL; bdb->clock = 1; return true; } /* Close a B+ tree database object. `bdb' specifies the B+ tree database object. If successful, the return value is true, else, it is false. */ static bool tcbdbcloseimpl(TCBDB *bdb){ assert(bdb); bool err = false; if(bdb->tran){ tcbdbcachepurge(bdb); memcpy(bdb->opaque, bdb->rbopaque, BDBOPAQUESIZ); tcbdbloadmeta(bdb); TCFREE(bdb->rbopaque); bdb->tran = false; bdb->rbopaque = NULL; if(!tchdbtranvoid(bdb->hdb)) err = true; } bdb->open = false; const char *vbuf; int vsiz; TCMAP *leafc = bdb->leafc; tcmapiterinit(leafc); while((vbuf = tcmapiternext(leafc, &vsiz)) != NULL){ if(!tcbdbleafcacheout(bdb, (BDBLEAF *)tcmapiterval(vbuf, &vsiz))) err = true; } TCMAP *nodec = bdb->nodec; tcmapiterinit(nodec); while((vbuf = tcmapiternext(nodec, &vsiz)) != NULL){ if(!tcbdbnodecacheout(bdb, (BDBNODE *)tcmapiterval(vbuf, &vsiz))) err = true; } if(bdb->wmode) tcbdbdumpmeta(bdb); tcmapdel(bdb->nodec); tcmapdel(bdb->leafc); if(!tchdbclose(bdb->hdb)) err = true; return !err; } /* Store a record into a B+ tree database object. `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. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. `dmode' specifies behavior when the key overlaps. If successful, the return value is true, else, it is false. */ static bool tcbdbputimpl(TCBDB *bdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, int dmode){ assert(bdb && kbuf && ksiz >= 0); BDBLEAF *leaf = NULL; uint64_t hlid = bdb->hleaf; if(hlid < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz, hlid))){ uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz); if(pid < 1) return false; if(!(leaf = tcbdbleafload(bdb, pid))) return false; hlid = 0; } if(!tcbdbleafaddrec(bdb, leaf, dmode, kbuf, ksiz, vbuf, vsiz)){ if(!bdb->tran) tcbdbcacheadjust(bdb); return false; } int rnum = TCPTRLISTNUM(leaf->recs); if(rnum > bdb->lmemb || (rnum > 1 && leaf->size > bdb->lsmax)){ if(hlid > 0 && hlid != tcbdbsearchleaf(bdb, kbuf, ksiz)) return false; bdb->lschk = 0; BDBLEAF *newleaf = tcbdbleafdivide(bdb, leaf); if(!newleaf) return false; if(leaf->id == bdb->last) bdb->last = newleaf->id; uint64_t heir = leaf->id; uint64_t pid = newleaf->id; BDBREC *rec = TCPTRLISTVAL(newleaf->recs, 0); char *dbuf = (char *)rec + sizeof(*rec); int ksiz = rec->ksiz; char *kbuf; TCMEMDUP(kbuf, dbuf, ksiz); while(true){ BDBNODE *node; if(bdb->hnum < 1){ node = tcbdbnodenew(bdb, heir); tcbdbnodeaddidx(bdb, node, true, pid, kbuf, ksiz); bdb->root = node->id; TCFREE(kbuf); break; } uint64_t parent = bdb->hist[--bdb->hnum]; if(!(node = tcbdbnodeload(bdb, parent))){ TCFREE(kbuf); return false; } tcbdbnodeaddidx(bdb, node, false, pid, kbuf, ksiz); TCFREE(kbuf); TCPTRLIST *idxs = node->idxs; int ln = TCPTRLISTNUM(idxs); if(ln <= bdb->nmemb) break; int mid = ln / 2; BDBIDX *idx = TCPTRLISTVAL(idxs, mid); BDBNODE *newnode = tcbdbnodenew(bdb, idx->pid); heir = node->id; pid = newnode->id; char *ebuf = (char *)idx + sizeof(*idx); TCMEMDUP(kbuf, ebuf, idx->ksiz); ksiz = idx->ksiz; for(int i = mid + 1; i < ln; i++){ idx = TCPTRLISTVAL(idxs, i); char *ebuf = (char *)idx + sizeof(*idx); tcbdbnodeaddidx(bdb, newnode, true, idx->pid, ebuf, idx->ksiz); } ln = TCPTRLISTNUM(newnode->idxs); for(int i = 0; i <= ln; i++){ idx = tcptrlistpop(idxs); TCFREE(idx); } node->dirty = true; } if(bdb->capnum > 0 && bdb->rnum > bdb->capnum){ uint64_t xnum = bdb->rnum - bdb->capnum; BDBCUR *cur = tcbdbcurnew(bdb); while((xnum--) > 0){ if((cur->id < 1 || cur->clock != bdb->clock) && !tcbdbcurfirstimpl(cur)){ tcbdbcurdel(cur); return false; } if(!tcbdbcuroutimpl(cur)){ tcbdbcurdel(cur); return false; } } tcbdbcurdel(cur); } } else if(rnum < 1){ if(hlid > 0 && hlid != tcbdbsearchleaf(bdb, kbuf, ksiz)) return false; if(bdb->hnum > 0 && !tcbdbleafkill(bdb, leaf)) return false; } if(!bdb->tran && !tcbdbcacheadjust(bdb)) return false; return true; } /* Remove a record of a B+ tree database object. `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 true, else, it is false. */ static bool tcbdboutimpl(TCBDB *bdb, const char *kbuf, int ksiz){ assert(bdb && kbuf && ksiz >= 0); BDBLEAF *leaf = NULL; uint64_t hlid = bdb->hleaf; if(hlid < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz, hlid))){ uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz); if(pid < 1) return false; if(!(leaf = tcbdbleafload(bdb, pid))) return false; hlid = 0; } int ri; BDBREC *rec = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, &ri); if(!rec){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } tcbdbremoverec(bdb, leaf, rec, ri); leaf->dirty = true; if(TCPTRLISTNUM(leaf->recs) < 1){ if(hlid > 0 && hlid != tcbdbsearchleaf(bdb, kbuf, ksiz)) return false; if(bdb->hnum > 0 && !tcbdbleafkill(bdb, leaf)) return false; } if(!bdb->tran && !tcbdbcacheadjust(bdb)) return false; return true; } /* Remove records of a B+ tree database object. `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 true, else, it is false. */ static bool tcbdboutlist(TCBDB *bdb, const char *kbuf, int ksiz){ assert(bdb && kbuf && ksiz >= 0); BDBLEAF *leaf = NULL; uint64_t hlid = bdb->hleaf; if(hlid < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz, hlid))){ uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz); if(pid < 1) return false; if(!(leaf = tcbdbleafload(bdb, pid))) return false; hlid = 0; } int ri; BDBREC *rec = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, &ri); if(!rec){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } int rnum = 1; int rsiz = rec->ksiz + rec->vsiz; if(rec->rest){ TCLIST *rest = rec->rest; int ln = TCLISTNUM(rec->rest); rnum += ln; for(int i = 0; i < ln; i++){ rsiz += TCLISTVALSIZ(rest, i); } tclistdel(rest); } TCFREE(tcptrlistremove(leaf->recs, ri)); leaf->size -= rsiz; leaf->dirty = true; bdb->rnum -= rnum; if(TCPTRLISTNUM(leaf->recs) < 1){ if(hlid > 0 && hlid != tcbdbsearchleaf(bdb, kbuf, ksiz)) return false; if(bdb->hnum > 0 && !tcbdbleafkill(bdb, leaf)) return false; } if(!bdb->tran && !tcbdbcacheadjust(bdb)) return false; return true; } /* Retrieve a record in a B+ tree database object. `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. */ static const char *tcbdbgetimpl(TCBDB *bdb, const char *kbuf, int ksiz, int *sp){ assert(bdb && kbuf && ksiz >= 0 && sp); BDBLEAF *leaf = NULL; uint64_t hlid = bdb->hleaf; if(hlid < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz, hlid))){ uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz); if(pid < 1) return NULL; if(!(leaf = tcbdbleafload(bdb, pid))) return NULL; } BDBREC *rec = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, NULL); if(!rec){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); return NULL; } *sp = rec->vsiz; return (char *)rec + sizeof(*rec) + rec->ksiz + TCALIGNPAD(rec->ksiz); } /* Get the number of records corresponding a key in a B+ tree database object. `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. */ static int tcbdbgetnum(TCBDB *bdb, const char *kbuf, int ksiz){ assert(bdb && kbuf && ksiz >= 0); BDBLEAF *leaf = NULL; uint64_t hlid = bdb->hleaf; if(hlid < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz, hlid))){ uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz); if(pid < 1) return 0; if(!(leaf = tcbdbleafload(bdb, pid))) return 0; } BDBREC *rec = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, NULL); if(!rec){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); return 0; } return rec->rest ? TCLISTNUM(rec->rest) + 1 : 1; } /* Retrieve records in a B+ tree database object. `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. */ static TCLIST *tcbdbgetlist(TCBDB *bdb, const char *kbuf, int ksiz){ assert(bdb && kbuf && ksiz >= 0); BDBLEAF *leaf = NULL; uint64_t hlid = bdb->hleaf; if(hlid < 1 || !(leaf = tcbdbgethistleaf(bdb, kbuf, ksiz, hlid))){ uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz); if(pid < 1) return NULL; if(!(leaf = tcbdbleafload(bdb, pid))) return NULL; } BDBREC *rec = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, NULL); if(!rec){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); return NULL; } TCLIST *vals; TCLIST *rest = rec->rest; if(rest){ int ln = TCLISTNUM(rest); vals = tclistnew2(ln + 1); TCLISTPUSH(vals, (char *)rec + sizeof(*rec) + rec->ksiz + TCALIGNPAD(rec->ksiz), rec->vsiz); for(int i = 0; i < ln; i++){ const char *vbuf; int vsiz; TCLISTVAL(vbuf, rest, i, vsiz); TCLISTPUSH(vals, vbuf, vsiz); } } else { vals = tclistnew2(1); TCLISTPUSH(vals, (char *)rec + sizeof(*rec) + rec->ksiz + TCALIGNPAD(rec->ksiz), rec->vsiz); } return vals; } /* Get keys of ranged records in a B+ tree database object. `bdb' specifies the B+ tree database object. `bkbuf' specifies the pointer to the region of the key of the beginning border. `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. `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. `keys' specifies a list object to store the result. If successful, the return value is true, else, it is false. */ static bool tcbdbrangeimpl(TCBDB *bdb, const char *bkbuf, int bksiz, bool binc, const char *ekbuf, int eksiz, bool einc, int max, TCLIST *keys){ assert(bdb && keys); bool err = false; BDBCUR *cur = tcbdbcurnew(bdb); if(bkbuf){ tcbdbcurjumpimpl(cur, bkbuf, bksiz, true); } else { tcbdbcurfirstimpl(cur); } TCCMP cmp = bdb->cmp; void *cmpop = bdb->cmpop; const char *lbuf = NULL; int lsiz = 0; while(cur->id > 0){ const char *kbuf, *vbuf; int ksiz, vsiz; if(!tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){ if(tchdbecode(bdb->hdb) != TCEINVALID && tchdbecode(bdb->hdb) != TCENOREC) err = true; break; } if(bkbuf && !binc){ if(cmp(kbuf, ksiz, bkbuf, bksiz, cmpop) == 0){ tcbdbcurnextimpl(cur); continue; } bkbuf = NULL; } if(ekbuf){ if(einc){ if(cmp(kbuf, ksiz, ekbuf, eksiz, cmpop) > 0) break; } else { if(cmp(kbuf, ksiz, ekbuf, eksiz, cmpop) >= 0) break; } } if(!lbuf || lsiz != ksiz || memcmp(kbuf, lbuf, ksiz)){ TCLISTPUSH(keys, kbuf, ksiz); if(max >= 0 && TCLISTNUM(keys) >= max) break; lbuf = kbuf; lsiz = ksiz; } tcbdbcurnextimpl(cur); } tcbdbcurdel(cur); return !err; } /* Get forward matching keys in a B+ tree database object. `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. `keys' specifies a list object to store the result. If successful, the return value is true, else, it is false. */ static bool tcbdbrangefwm(TCBDB *bdb, const char *pbuf, int psiz, int max, TCLIST *keys){ assert(bdb && pbuf && psiz >= 0 && keys); bool err = false; if(max < 0) max = INT_MAX; if(max < 1) return true; BDBCUR *cur = tcbdbcurnew(bdb); tcbdbcurjumpimpl(cur, pbuf, psiz, true); const char *lbuf = NULL; int lsiz = 0; while(cur->id > 0){ const char *kbuf, *vbuf; int ksiz, vsiz; if(!tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){ if(tchdbecode(bdb->hdb) != TCEINVALID && tchdbecode(bdb->hdb) != TCENOREC) err = true; break; } if(ksiz < psiz || memcmp(kbuf, pbuf, psiz)) break; if(!lbuf || lsiz != ksiz || memcmp(kbuf, lbuf, ksiz)){ TCLISTPUSH(keys, kbuf, ksiz); if(TCLISTNUM(keys) >= max) break; lbuf = kbuf; lsiz = ksiz; } tcbdbcurnextimpl(cur); } tcbdbcurdel(cur); return !err; } /* Optimize the file of a B+ tree database object. `bdb' specifies the B+ tree database object. `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 elements of the bucket array. `apow' specifies the size of record alignment by power of 2. `fpow' specifies the maximum number of elements of the free block pool by power of 2. `opts' specifies options by bitwise-or. If successful, the return value is true, else, it is false. */ static bool tcbdboptimizeimpl(TCBDB *bdb, int32_t lmemb, int32_t nmemb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts){ assert(bdb); const char *path = tchdbpath(bdb->hdb); char *tpath = tcsprintf("%s%ctmp%c%llu", path, MYEXTCHR, MYEXTCHR, tchdbinode(bdb->hdb)); TCBDB *tbdb = tcbdbnew(); int dbgfd = tchdbdbgfd(bdb->hdb); if(dbgfd >= 0) tcbdbsetdbgfd(tbdb, dbgfd); tcbdbsetcmpfunc(tbdb, bdb->cmp, bdb->cmpop); TCCODEC enc, dec; void *encop, *decop; tchdbcodecfunc(bdb->hdb, &enc, &encop, &dec, &decop); if(enc && dec) tcbdbsetcodecfunc(tbdb, enc, encop, dec, decop); if(lmemb < 1) lmemb = bdb->lmemb; if(nmemb < 1) nmemb = bdb->nmemb; if(bnum < 1) bnum = tchdbrnum(bdb->hdb) * 2 + 1; if(apow < 0) apow = tclog2l(tchdbalign(bdb->hdb)); if(fpow < 0) fpow = tclog2l(tchdbfbpmax(bdb->hdb)); if(opts == UINT8_MAX) opts = bdb->opts; tcbdbtune(tbdb, lmemb, nmemb, bnum, apow, fpow, opts); tcbdbsetcache(tbdb, 1, 1); tcbdbsetlsmax(tbdb, bdb->lsmax); uint32_t lcnum = bdb->lcnum; uint32_t ncnum = bdb->ncnum; bdb->lcnum = BDBLEVELMAX; bdb->ncnum = BDBCACHEOUT * 2; tbdb->lcnum = BDBLEVELMAX; tbdb->ncnum = BDBCACHEOUT * 2; if(!tcbdbopen(tbdb, tpath, BDBOWRITER | BDBOCREAT | BDBOTRUNC)){ tcbdbsetecode(bdb, tcbdbecode(tbdb), __FILE__, __LINE__, __func__); tcbdbdel(tbdb); TCFREE(tpath); return false; } memcpy(tcbdbopaque(tbdb), tcbdbopaque(bdb), BDBLEFTOPQSIZ); bool err = false; BDBCUR *cur = tcbdbcurnew(bdb); tcbdbcurfirstimpl(cur); const char *kbuf, *vbuf; int ksiz, vsiz; int cnt = 0; while(!err && cur->id > 0 && tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){ if(!tcbdbputdup(tbdb, kbuf, ksiz, vbuf, vsiz)){ tcbdbsetecode(bdb, tcbdbecode(tbdb), __FILE__, __LINE__, __func__); err = true; } tcbdbcurnextimpl(cur); if((++cnt % 0xf == 0) && !tcbdbcacheadjust(bdb)) err = true; } tcbdbcurdel(cur); if(!tcbdbclose(tbdb)){ tcbdbsetecode(bdb, tcbdbecode(tbdb), __FILE__, __LINE__, __func__); err = true; } bdb->lcnum = lcnum; bdb->ncnum = ncnum; tcbdbdel(tbdb); if(unlink(path) == -1){ tcbdbsetecode(bdb, TCEUNLINK, __FILE__, __LINE__, __func__); err = true; } if(rename(tpath, path) == -1){ tcbdbsetecode(bdb, TCERENAME, __FILE__, __LINE__, __func__); err = true; } TCFREE(tpath); if(err) return false; tpath = tcstrdup(path); int omode = (tchdbomode(bdb->hdb) & ~BDBOCREAT) & ~BDBOTRUNC; if(!tcbdbcloseimpl(bdb)){ TCFREE(tpath); return false; } bool rv = tcbdbopenimpl(bdb, tpath, omode); TCFREE(tpath); return rv; } /* Remove all records of a B+ tree database object. `bdb' specifies the B+ tree database object. If successful, the return value is true, else, it is false. */ static bool tcbdbvanishimpl(TCBDB *bdb){ assert(bdb); char *path = tcstrdup(tchdbpath(bdb->hdb)); int omode = tchdbomode(bdb->hdb); bool err = false; if(!tcbdbcloseimpl(bdb)) err = true; if(!tcbdbopenimpl(bdb, path, BDBOTRUNC | omode)) err = true; TCFREE(path); return !err; } /* Lock a method of the B+ tree database object. `bdb' specifies the B+ tree database object. `wr' specifies whether the lock is writer or not. If successful, the return value is true, else, it is false. */ static bool tcbdblockmethod(TCBDB *bdb, bool wr){ assert(bdb); if(wr ? pthread_rwlock_wrlock(bdb->mmtx) != 0 : pthread_rwlock_rdlock(bdb->mmtx) != 0){ tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Unlock a method of the B+ tree database object. `bdb' specifies the B+ tree database object. If successful, the return value is true, else, it is false. */ static bool tcbdbunlockmethod(TCBDB *bdb){ assert(bdb); if(pthread_rwlock_unlock(bdb->mmtx) != 0){ tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Lock the cache of the B+ tree database object. `bdb' specifies the B+ tree database object. If successful, the return value is true, else, it is false. */ static bool tcbdblockcache(TCBDB *bdb){ assert(bdb); if(pthread_mutex_lock(bdb->cmtx) != 0){ tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Unlock the cache of the B+ tree database object. `bdb' specifies the B+ tree database object. If successful, the return value is true, else, it is false. */ static bool tcbdbunlockcache(TCBDB *bdb){ assert(bdb); if(pthread_mutex_unlock(bdb->cmtx) != 0){ tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true; } /* Move a cursor object to the first record. `cur' specifies the cursor object. If successful, the return value is true, else, it is false. */ static bool tcbdbcurfirstimpl(BDBCUR *cur){ assert(cur); TCBDB *bdb = cur->bdb; cur->clock = bdb->clock; cur->id = bdb->first; cur->kidx = 0; cur->vidx = 0; return tcbdbcuradjust(cur, true); } /* Move a cursor object to the last record. `cur' specifies the cursor object. If successful, the return value is true, else, it is false. */ static bool tcbdbcurlastimpl(BDBCUR *cur){ assert(cur); TCBDB *bdb = cur->bdb; cur->clock = bdb->clock; cur->id = bdb->last; cur->kidx = INT_MAX; cur->vidx = INT_MAX; return tcbdbcuradjust(cur, false); } /* Move a cursor object to around records corresponding a key. `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. `forward' specifies whether the cursor is to be the front of records. If successful, the return value is true, else, it is false. */ static bool tcbdbcurjumpimpl(BDBCUR *cur, const char *kbuf, int ksiz, bool forward){ assert(cur && kbuf && ksiz >= 0); TCBDB *bdb = cur->bdb; cur->clock = bdb->clock; uint64_t pid = tcbdbsearchleaf(bdb, kbuf, ksiz); if(pid < 1){ cur->id = 0; cur->kidx = 0; cur->vidx = 0; return false; } BDBLEAF *leaf = tcbdbleafload(bdb, pid); if(!leaf){ cur->id = 0; cur->kidx = 0; cur->vidx = 0; return false; } if(leaf->dead || TCPTRLISTNUM(leaf->recs) < 1){ cur->id = pid; cur->kidx = 0; cur->vidx = 0; return forward ? tcbdbcurnextimpl(cur) : tcbdbcurprevimpl(cur); } int ri; BDBREC *rec = tcbdbsearchrec(bdb, leaf, kbuf, ksiz, &ri); if(rec){ cur->id = pid; cur->kidx = ri; if(forward){ cur->vidx = 0; } else { cur->vidx = rec->rest ? TCLISTNUM(rec->rest) : 0; } return true; } cur->id = leaf->id; if(ri > 0 && ri >= TCPTRLISTNUM(leaf->recs)) ri = TCPTRLISTNUM(leaf->recs) - 1; cur->kidx = ri; rec = TCPTRLISTVAL(leaf->recs, ri); char *dbuf = (char *)rec + sizeof(*rec); if(forward){ int rv; if(bdb->cmp == tccmplexical){ TCCMPLEXICAL(rv, kbuf, ksiz, dbuf, rec->ksiz); } else { rv = bdb->cmp(kbuf, ksiz, dbuf, rec->ksiz, bdb->cmpop); } if(rv < 0){ cur->vidx = 0; return true; } cur->vidx = rec->rest ? TCLISTNUM(rec->rest) : 0; return tcbdbcurnextimpl(cur); } int rv; if(bdb->cmp == tccmplexical){ TCCMPLEXICAL(rv, kbuf, ksiz, dbuf, rec->ksiz); } else { rv = bdb->cmp(kbuf, ksiz, dbuf, rec->ksiz, bdb->cmpop); } if(rv > 0){ cur->vidx = rec->rest ? TCLISTNUM(rec->rest) : 0; return true; } cur->vidx = 0; return tcbdbcurprevimpl(cur); } /* Adjust a cursor object forward to the suitable record. `cur' specifies the cursor object. `forward' specifies the direction is forward or not. If successful, the return value is true, else, it is false. */ static bool tcbdbcuradjust(BDBCUR *cur, bool forward){ assert(cur); TCBDB *bdb = cur->bdb; if(cur->clock != bdb->clock){ if(!tcbdbleafcheck(bdb, cur->id)){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); cur->id = 0; cur->kidx = 0; cur->vidx = 0; return false; } cur->clock = bdb->clock; } while(true){ if(cur->id < 1){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); cur->id = 0; cur->kidx = 0; cur->vidx = 0; return false; } BDBLEAF *leaf = tcbdbleafload(bdb, cur->id); if(!leaf) return false; TCPTRLIST *recs = leaf->recs; int knum = TCPTRLISTNUM(recs); if(leaf->dead){ if(forward){ cur->id = leaf->next; cur->kidx = 0; cur->vidx = 0; } else { cur->id = leaf->prev; cur->kidx = INT_MAX; cur->vidx = INT_MAX; } } else if(cur->kidx < 0){ if(forward){ cur->kidx = 0; cur->vidx = 0; } else { cur->id = leaf->prev; cur->kidx = INT_MAX; cur->vidx = INT_MAX; } } else if(cur->kidx >= knum){ if(forward){ cur->id = leaf->next; cur->kidx = 0; cur->vidx = 0; } else { cur->kidx = knum - 1; cur->vidx = INT_MAX; } } else { BDBREC *rec = TCPTRLISTVAL(recs, cur->kidx); int vnum = rec->rest ? TCLISTNUM(rec->rest) + 1 : 1; if(cur->vidx < 0){ if(forward){ cur->vidx = 0; } else { cur->kidx--; cur->vidx = INT_MAX; } } else if(cur->vidx >= vnum){ if(forward){ cur->kidx++; cur->vidx = 0; if(cur->kidx >= knum){ cur->id = leaf->next; cur->kidx = 0; cur->vidx = 0; } else { break; } } else { cur->vidx = vnum - 1; if(cur->vidx >= 0) break; } } else { break; } } } return true; } /* Move a cursor object to the previous record. `cur' specifies the cursor object. If successful, the return value is true, else, it is false. */ static bool tcbdbcurprevimpl(BDBCUR *cur){ assert(cur); cur->vidx--; return tcbdbcuradjust(cur, false); } /* Move a cursor object to the next record. `cur' specifies the cursor object. If successful, the return value is true, else, it is false. */ static bool tcbdbcurnextimpl(BDBCUR *cur){ assert(cur); cur->vidx++; return tcbdbcuradjust(cur, true); } /* Insert a record around a cursor object. `cur' specifies the cursor object. `vbuf' specifies the pointer to the region of the value. `vsiz' specifies the size of the region of the value. `cpmode' specifies detail adjustment. If successful, the return value is true, else, it is false. */ static bool tcbdbcurputimpl(BDBCUR *cur, const char *vbuf, int vsiz, int cpmode){ assert(cur && vbuf && vsiz >= 0); TCBDB *bdb = cur->bdb; if(cur->clock != bdb->clock){ if(!tcbdbleafcheck(bdb, cur->id)){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); cur->id = 0; cur->kidx = 0; cur->vidx = 0; return false; } cur->clock = bdb->clock; } BDBLEAF *leaf = tcbdbleafload(bdb, cur->id); if(!leaf) return false; TCPTRLIST *recs = leaf->recs; if(cur->kidx >= TCPTRLISTNUM(recs)){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } BDBREC *rec = TCPTRLISTVAL(recs, cur->kidx); int vnum = rec->rest ? TCLISTNUM(rec->rest) + 1 : 1; if(cur->vidx >= vnum){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } char *dbuf = (char *)rec + sizeof(*rec); int psiz = TCALIGNPAD(rec->ksiz); BDBREC *orec = rec; switch(cpmode){ case BDBCPCURRENT: if(cur->vidx < 1){ leaf->size += vsiz - rec->vsiz; if(vsiz > rec->vsiz){ TCREALLOC(rec, rec, sizeof(*rec) + rec->ksiz + psiz + vsiz + 1); if(rec != orec){ tcptrlistover(recs, cur->kidx, rec); dbuf = (char *)rec + sizeof(*rec); } } memcpy(dbuf + rec->ksiz + psiz, vbuf, vsiz); dbuf[rec->ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; } else { leaf->size += vsiz - TCLISTVALSIZ(rec->rest, cur->vidx - 1); tclistover(rec->rest, cur->vidx - 1, vbuf, vsiz); } break; case BDBCPBEFORE: leaf->size += vsiz; if(cur->vidx < 1){ if(!rec->rest) rec->rest = tclistnew2(1); tclistunshift(rec->rest, dbuf + rec->ksiz + psiz, rec->vsiz); if(vsiz > rec->vsiz){ TCREALLOC(rec, rec, sizeof(*rec) + rec->ksiz + psiz + vsiz + 1); if(rec != orec){ tcptrlistover(recs, cur->kidx, rec); dbuf = (char *)rec + sizeof(*rec); } } memcpy(dbuf + rec->ksiz + psiz, vbuf, vsiz); dbuf[rec->ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; } else { TCLISTINSERT(rec->rest, cur->vidx - 1, vbuf, vsiz); } bdb->rnum++; break; case BDBCPAFTER: leaf->size += vsiz; if(!rec->rest) rec->rest = tclistnew2(1); TCLISTINSERT(rec->rest, cur->vidx, vbuf, vsiz); cur->vidx++; bdb->rnum++; break; } leaf->dirty = true; return true; } /* Delete the record where a cursor object is. `cur' specifies the cursor object. If successful, the return value is true, else, it is false. */ static bool tcbdbcuroutimpl(BDBCUR *cur){ assert(cur); TCBDB *bdb = cur->bdb; if(cur->clock != bdb->clock){ if(!tcbdbleafcheck(bdb, cur->id)){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); cur->id = 0; cur->kidx = 0; cur->vidx = 0; return false; } cur->clock = bdb->clock; } BDBLEAF *leaf = tcbdbleafload(bdb, cur->id); if(!leaf) return false; TCPTRLIST *recs = leaf->recs; if(cur->kidx >= TCPTRLISTNUM(recs)){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } BDBREC *rec = TCPTRLISTVAL(recs, cur->kidx); char *dbuf = (char *)rec + sizeof(*rec); int vnum = rec->rest ? TCLISTNUM(rec->rest) + 1 : 1; if(cur->vidx >= vnum){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } if(rec->rest){ if(cur->vidx < 1){ leaf->size -= rec->vsiz; int vsiz; char *vbuf = tclistshift(rec->rest, &vsiz); int psiz = TCALIGNPAD(rec->ksiz); if(vsiz > rec->vsiz){ BDBREC *orec = rec; TCREALLOC(rec, rec, sizeof(*rec) + rec->ksiz + psiz + vsiz + 1); if(rec != orec){ tcptrlistover(leaf->recs, cur->kidx, rec); dbuf = (char *)rec + sizeof(*rec); } } memcpy(dbuf + rec->ksiz + psiz, vbuf, vsiz); dbuf[rec->ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; TCFREE(vbuf); } else { int vsiz; char *vbuf = tclistremove(rec->rest, cur->vidx - 1, &vsiz); leaf->size -= vsiz; TCFREE(vbuf); } if(TCLISTNUM(rec->rest) < 1){ tclistdel(rec->rest); rec->rest = NULL; } } else { leaf->size -= rec->ksiz + rec->vsiz; if(TCPTRLISTNUM(recs) < 2){ uint64_t pid = tcbdbsearchleaf(bdb, dbuf, rec->ksiz); if(pid < 1) return false; if(bdb->hnum > 0){ if(!(leaf = tcbdbleafload(bdb, pid))) return false; if(!tcbdbleafkill(bdb, leaf)) return false; if(leaf->next != 0){ cur->id = leaf->next; cur->kidx = 0; cur->vidx = 0; cur->clock = bdb->clock; } } } TCFREE(tcptrlistremove(leaf->recs, cur->kidx)); } bdb->rnum--; leaf->dirty = true; return tcbdbcuradjust(cur, true) || tchdbecode(bdb->hdb) == TCENOREC; } /* Get the key and the value of the current record of the cursor object. `cur' specifies the cursor object. `kbp' specifies the pointer to the variable into which the pointer to the region of the key is assgined. `ksp' specifies the pointer to the variable into which the size of the key region is assigned. `vbp' specifies the pointer to the variable into which the pointer to the region of the value is assgined. `vsp' specifies the pointer to the variable into which the size of the value region is assigned. */ static bool tcbdbcurrecimpl(BDBCUR *cur, const char **kbp, int *ksp, const char **vbp, int *vsp){ assert(cur && kbp && ksp && vbp && vsp); TCBDB *bdb = cur->bdb; if(cur->clock != bdb->clock){ if(!tcbdbleafcheck(bdb, cur->id)){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); cur->id = 0; cur->kidx = 0; cur->vidx = 0; return false; } cur->clock = bdb->clock; } BDBLEAF *leaf = tcbdbleafload(bdb, cur->id); if(!leaf) return false; TCPTRLIST *recs = leaf->recs; if(cur->kidx >= TCPTRLISTNUM(recs)){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } BDBREC *rec = TCPTRLISTVAL(recs, cur->kidx); char *dbuf = (char *)rec + sizeof(*rec); int vnum = rec->rest ? TCLISTNUM(rec->rest) + 1 : 1; if(cur->vidx >= vnum){ tcbdbsetecode(bdb, TCENOREC, __FILE__, __LINE__, __func__); return false; } *kbp = dbuf; *ksp = rec->ksiz; if(cur->vidx > 0){ *vbp = tclistval(rec->rest, cur->vidx - 1, vsp); } else { *vbp = dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz); *vsp = rec->vsiz; } return true; } /* Process each record atomically of a B+ tree database object. `func' specifies the pointer to the iterator function called for each record. `op' specifies an arbitrary pointer to be given as a parameter of the iterator function. If successful, the return value is true, else, it is false. */ static bool tcbdbforeachimpl(TCBDB *bdb, TCITER iter, void *op){ assert(bdb && iter); bool err = false; BDBCUR *cur = tcbdbcurnew(bdb); tcbdbcurfirstimpl(cur); const char *kbuf, *vbuf; int ksiz, vsiz; while(cur->id > 0){ if(tcbdbcurrecimpl(cur, &kbuf, &ksiz, &vbuf, &vsiz)){ if(!iter(kbuf, ksiz, vbuf, vsiz, op)) break; tcbdbcurnextimpl(cur); if(bdb->tran){ if(cur->id > 0){ BDBLEAF *leaf = tcbdbleafload(bdb, cur->id); if(!leaf){ err = true; break; } if(!leaf->dirty && !tcbdbleafcacheout(bdb, leaf)){ err = false; break; } } } else if(TCMAPRNUM(bdb->leafc) > bdb->lcnum && !tcbdbcacheadjust(bdb)){ err = true; break; } } else { if(tchdbecode(bdb->hdb) != TCEINVALID && tchdbecode(bdb->hdb) != TCENOREC) err = true; break; } } tcbdbcurdel(cur); return !err; } /************************************************************************************************* * debugging functions *************************************************************************************************/ /* Print meta data of the header into the debugging output. `bdb' specifies the B+ tree database object. */ void tcbdbprintmeta(TCBDB *bdb){ assert(bdb); int dbgfd = tchdbdbgfd(bdb->hdb); if(dbgfd < 0) return; if(dbgfd == UINT16_MAX) dbgfd = 1; char buf[BDBPAGEBUFSIZ]; char *wp = buf; wp += sprintf(wp, "META:"); wp += sprintf(wp, " mmtx=%p", (void *)bdb->mmtx); wp += sprintf(wp, " cmtx=%p", (void *)bdb->cmtx); wp += sprintf(wp, " hdb=%p", (void *)bdb->hdb); wp += sprintf(wp, " opaque=%p", (void *)bdb->opaque); wp += sprintf(wp, " open=%d", bdb->open); wp += sprintf(wp, " wmode=%d", bdb->wmode); wp += sprintf(wp, " lmemb=%u", bdb->lmemb); wp += sprintf(wp, " nmemb=%u", bdb->nmemb); wp += sprintf(wp, " opts=%u", bdb->opts); wp += sprintf(wp, " root=%llx", (unsigned long long)bdb->root); wp += sprintf(wp, " first=%llx", (unsigned long long)bdb->first); wp += sprintf(wp, " last=%llx", (unsigned long long)bdb->last); wp += sprintf(wp, " lnum=%llu", (unsigned long long)bdb->lnum); wp += sprintf(wp, " nnum=%llu", (unsigned long long)bdb->nnum); wp += sprintf(wp, " rnum=%llu", (unsigned long long)bdb->rnum); wp += sprintf(wp, " leafc=%p", (void *)bdb->leafc); wp += sprintf(wp, " nodec=%p", (void *)bdb->nodec); wp += sprintf(wp, " cmp=%p", (void *)(intptr_t)bdb->cmp); wp += sprintf(wp, " cmpop=%p", (void *)bdb->cmpop); wp += sprintf(wp, " lcnum=%u", bdb->lcnum); wp += sprintf(wp, " ncnum=%u", bdb->ncnum); wp += sprintf(wp, " lsmax=%u", bdb->lsmax); wp += sprintf(wp, " lschk=%u", bdb->lschk); wp += sprintf(wp, " capnum=%llu", (unsigned long long)bdb->capnum); wp += sprintf(wp, " hist=%p", (void *)bdb->hist); wp += sprintf(wp, " hnum=%d", bdb->hnum); wp += sprintf(wp, " hleaf=%llu", (unsigned long long)bdb->hleaf); wp += sprintf(wp, " lleaf=%llu", (unsigned long long)bdb->lleaf); wp += sprintf(wp, " tran=%d", bdb->tran); wp += sprintf(wp, " rbopaque=%p", (void *)bdb->rbopaque); wp += sprintf(wp, " clock=%llu", (unsigned long long)bdb->clock); wp += sprintf(wp, " cnt_saveleaf=%lld", (long long)bdb->cnt_saveleaf); wp += sprintf(wp, " cnt_loadleaf=%lld", (long long)bdb->cnt_loadleaf); wp += sprintf(wp, " cnt_killleaf=%lld", (long long)bdb->cnt_killleaf); wp += sprintf(wp, " cnt_adjleafc=%lld", (long long)bdb->cnt_adjleafc); wp += sprintf(wp, " cnt_savenode=%lld", (long long)bdb->cnt_savenode); wp += sprintf(wp, " cnt_loadnode=%lld", (long long)bdb->cnt_loadnode); wp += sprintf(wp, " cnt_adjnodec=%lld", (long long)bdb->cnt_adjnodec); *(wp++) = '\n'; tcwrite(dbgfd, buf, wp - buf); } /* Print records of a leaf object into the debugging output. `bdb' specifies the B+ tree database object. `leaf' specifies the leaf object. */ void tcbdbprintleaf(TCBDB *bdb, BDBLEAF *leaf){ assert(bdb && leaf); int dbgfd = tchdbdbgfd(bdb->hdb); TCPTRLIST *recs = leaf->recs; if(dbgfd < 0) return; if(dbgfd == UINT16_MAX) dbgfd = 1; char buf[BDBPAGEBUFSIZ]; char *wp = buf; wp += sprintf(wp, "LEAF:"); wp += sprintf(wp, " id:%llx", (unsigned long long)leaf->id); wp += sprintf(wp, " size:%u", leaf->size); wp += sprintf(wp, " prev:%llx", (unsigned long long)leaf->prev); wp += sprintf(wp, " next:%llx", (unsigned long long)leaf->next); wp += sprintf(wp, " dirty:%d", leaf->dirty); wp += sprintf(wp, " dead:%d", leaf->dead); wp += sprintf(wp, " rnum:%d", TCPTRLISTNUM(recs)); *(wp++) = ' '; for(int i = 0; i < TCPTRLISTNUM(recs); i++){ tcwrite(dbgfd, buf, wp - buf); wp = buf; BDBREC *rec = TCPTRLISTVAL(recs, i); char *dbuf = (char *)rec + sizeof(*rec); wp += sprintf(wp, " [%s:%s]", dbuf, dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz)); TCLIST *rest = rec->rest; if(rest){ for(int j = 0; j < TCLISTNUM(rest); j++){ wp += sprintf(wp, ":%s", (char *)TCLISTVALPTR(rest, j)); } } } *(wp++) = '\n'; tcwrite(dbgfd, buf, wp - buf); } /* Print indices of a node object into the debugging output. `bdb' specifies the B+ tree database object. `node' specifies the node object. */ void tcbdbprintnode(TCBDB *bdb, BDBNODE *node){ assert(bdb && node); int dbgfd = tchdbdbgfd(bdb->hdb); TCPTRLIST *idxs = node->idxs; if(dbgfd < 0) return; if(dbgfd == UINT16_MAX) dbgfd = 1; char buf[BDBPAGEBUFSIZ]; char *wp = buf; wp += sprintf(wp, "NODE:"); wp += sprintf(wp, " id:%llx", (unsigned long long)node->id); wp += sprintf(wp, " heir:%llx", (unsigned long long)node->heir); wp += sprintf(wp, " dirty:%d", node->dirty); wp += sprintf(wp, " dead:%d", node->dead); wp += sprintf(wp, " rnum:%d", TCPTRLISTNUM(idxs)); *(wp++) = ' '; for(int i = 0; i < TCPTRLISTNUM(idxs); i++){ tcwrite(dbgfd, buf, wp - buf); wp = buf; BDBIDX *idx = TCPTRLISTVAL(idxs, i); char *ebuf = (char *)idx + sizeof(*idx); wp += sprintf(wp, " [%llx:%s]", (unsigned long long)idx->pid, ebuf); } *(wp++) = '\n'; tcwrite(dbgfd, buf, wp - buf); } // END OF FILE tokyocabinet-1.4.48/tctdb.h0000644000175000017500000016100712013574446014571 0ustar mikiomikio/************************************************************************************************* * The table database API of Tokyo Cabinet * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #ifndef _TCTDB_H /* duplication check */ #define _TCTDB_H #if defined(__cplusplus) #define __TCTDB_CLINKAGEBEGIN extern "C" { #define __TCTDB_CLINKAGEEND } #else #define __TCTDB_CLINKAGEBEGIN #define __TCTDB_CLINKAGEEND #endif __TCTDB_CLINKAGEBEGIN #include #include #include /************************************************************************************************* * API *************************************************************************************************/ typedef struct { /* type of structure for a column index */ char *name; /* column name */ int type; /* data type */ void *db; /* internal database object */ void *cc; /* internal cache object */ } TDBIDX; typedef struct { /* type of structure for a table database */ void *mmtx; /* mutex for method */ TCHDB *hdb; /* internal database object */ bool open; /* whether the internal database is opened */ bool wmode; /* whether to be writable */ uint8_t opts; /* options */ int32_t lcnum; /* max number of cached leaves */ int32_t ncnum; /* max number of cached nodes */ int64_t iccmax; /* maximum size of the inverted cache */ double iccsync; /* synchronization ratio of the inverted cache */ TDBIDX *idxs; /* column indices */ int inum; /* number of column indices */ bool tran; /* whether in the transaction */ } TCTDB; enum { /* enumeration for additional flags */ TDBFOPEN = HDBFOPEN, /* whether opened */ TDBFFATAL = HDBFFATAL /* whether with fatal error */ }; enum { /* enumeration for tuning options */ TDBTLARGE = 1 << 0, /* use 64-bit bucket array */ TDBTDEFLATE = 1 << 1, /* compress each page with Deflate */ TDBTBZIP = 1 << 2, /* compress each record with BZIP2 */ TDBTTCBS = 1 << 3, /* compress each page with TCBS */ TDBTEXCODEC = 1 << 4 /* compress each record with outer functions */ }; enum { /* enumeration for open modes */ TDBOREADER = 1 << 0, /* open as a reader */ TDBOWRITER = 1 << 1, /* open as a writer */ TDBOCREAT = 1 << 2, /* writer creating */ TDBOTRUNC = 1 << 3, /* writer truncating */ TDBONOLCK = 1 << 4, /* open without locking */ TDBOLCKNB = 1 << 5, /* lock without blocking */ TDBOTSYNC = 1 << 6 /* synchronize every transaction */ }; enum { /* enumeration for index types */ TDBITLEXICAL, /* lexical string */ TDBITDECIMAL, /* decimal string */ TDBITTOKEN, /* token inverted index */ TDBITQGRAM, /* q-gram inverted index */ TDBITOPT = 9998, /* optimize */ TDBITVOID = 9999, /* void */ TDBITKEEP = 1 << 24 /* keep existing index */ }; typedef struct { /* type of structure for a condition */ char *name; /* column name */ int nsiz; /* size of the column name */ int op; /* operation type */ bool sign; /* positive sign */ bool noidx; /* no index flag */ char *expr; /* operand expression */ int esiz; /* size of the operand expression */ void *regex; /* regular expression object */ void *ftsunits; /* full-text search units */ int ftsnum; /* number of full-text search units */ bool alive; /* alive flag */ } TDBCOND; typedef struct { /* type of structure for a query */ TCTDB *tdb; /* database object */ TDBCOND *conds; /* condition objects */ int cnum; /* number of conditions */ char *oname; /* column name for ordering */ int otype; /* type of order */ int max; /* max number of retrieval */ int skip; /* skipping number of retrieval */ TCXSTR *hint; /* hint string */ int count; /* count of corresponding records */ } TDBQRY; enum { /* enumeration for query conditions */ TDBQCSTREQ, /* string is equal to */ TDBQCSTRINC, /* string is included in */ TDBQCSTRBW, /* string begins with */ TDBQCSTREW, /* string ends with */ TDBQCSTRAND, /* string includes all tokens in */ TDBQCSTROR, /* string includes at least one token in */ TDBQCSTROREQ, /* string is equal to at least one token in */ TDBQCSTRRX, /* string matches regular expressions of */ TDBQCNUMEQ, /* number is equal to */ TDBQCNUMGT, /* number is greater than */ TDBQCNUMGE, /* number is greater than or equal to */ TDBQCNUMLT, /* number is less than */ TDBQCNUMLE, /* number is less than or equal to */ TDBQCNUMBT, /* number is between two tokens of */ TDBQCNUMOREQ, /* number is equal to at least one token in */ TDBQCFTSPH, /* full-text search with the phrase of */ TDBQCFTSAND, /* full-text search with all tokens in */ TDBQCFTSOR, /* full-text search with at least one token in */ TDBQCFTSEX, /* full-text search with the compound expression of */ TDBQCNEGATE = 1 << 24, /* negation flag */ TDBQCNOIDX = 1 << 25 /* no index flag */ }; enum { /* enumeration for order types */ TDBQOSTRASC, /* string ascending */ TDBQOSTRDESC, /* string descending */ TDBQONUMASC, /* number ascending */ TDBQONUMDESC /* number descending */ }; enum { /* enumeration for set operation types */ TDBMSUNION, /* union */ TDBMSISECT, /* intersection */ TDBMSDIFF /* difference */ }; enum { /* enumeration for post treatments */ TDBQPPUT = 1 << 0, /* modify the record */ TDBQPOUT = 1 << 1, /* remove the record */ TDBQPSTOP = 1 << 24 /* stop the iteration */ }; /* type of the pointer to a iterator function for each table record. `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. `op' specifies the pointer to the optional opaque object. The return value is flags of the post treatment by bitwise-or: `TDBQPPUT' to modify the record, `TDBQPOUT' to remove the record, `TDBQPSTOP' to stop the iteration. */ typedef int (*TDBQRYPROC)(const void *pkbuf, int pksiz, TCMAP *cols, void *op); /* Get the message string corresponding to an error code. `ecode' specifies the error code. The return value is the message string of the error code. */ const char *tctdberrmsg(int ecode); /* Create a table database object. The return value is the new table database object. */ TCTDB *tctdbnew(void); /* Delete a table database object. `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. */ void tctdbdel(TCTDB *tdb); /* Get the last happened error code of a table database object. `tdb' specifies the table database object. The return value is the last happened error code. The following error code is 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. */ int tctdbecode(TCTDB *tdb); /* Set mutual exclusion control of a table database object for threading. `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. */ bool tctdbsetmutex(TCTDB *tdb); /* Set the tuning parameters of a table database object. `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. */ bool tctdbtune(TCTDB *tdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts); /* Set the caching parameters of a table database object. `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. */ bool tctdbsetcache(TCTDB *tdb, int32_t rcnum, int32_t lcnum, int32_t ncnum); /* Set the size of the extra mapped memory of a table database object. `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. */ bool tctdbsetxmsiz(TCTDB *tdb, int64_t xmsiz); /* Set the unit step number of auto defragmentation of a table database object. `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. */ bool tctdbsetdfunit(TCTDB *tdb, int32_t dfunit); /* Open a database file and connect a table database object. `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. */ bool tctdbopen(TCTDB *tdb, const char *path, int omode); /* Close a table database object. `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. */ bool tctdbclose(TCTDB *tdb); /* Store a record into a table database object. `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. */ bool tctdbput(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols); /* Store a string record into a table database object with a zero separated column string. `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. */ bool tctdbput2(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz); /* Store a string record into a table database object with a tab separated column string. `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. */ bool tctdbput3(TCTDB *tdb, const char *pkstr, const char *cstr); /* Store a new record into a table database object. `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. */ bool tctdbputkeep(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols); /* Store a new string record into a table database object with a zero separated column string. `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. */ bool tctdbputkeep2(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz); /* Store a new string record into a table database object with a tab separated column string. `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. */ bool tctdbputkeep3(TCTDB *tdb, const char *pkstr, const char *cstr); /* Concatenate columns of the existing record in a table database object. `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. */ bool tctdbputcat(TCTDB *tdb, const void *pkbuf, int pksiz, TCMAP *cols); /* Concatenate columns in a table database object with a zero separated column string. `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. */ bool tctdbputcat2(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz); /* Concatenate columns in a table database object with with a tab separated column string. `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. */ bool tctdbputcat3(TCTDB *tdb, const char *pkstr, const char *cstr); /* Remove a record of a table database object. `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. */ bool tctdbout(TCTDB *tdb, const void *pkbuf, int pksiz); /* Remove a string record of a table database object. `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. */ bool tctdbout2(TCTDB *tdb, const char *pkstr); /* Retrieve a record in a table database object. `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. */ TCMAP *tctdbget(TCTDB *tdb, const void *pkbuf, int pksiz); /* Retrieve a record in a table database object as a zero separated column string. `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. */ char *tctdbget2(TCTDB *tdb, const void *pkbuf, int pksiz, int *sp); /* Retrieve a string record in a table database object as a tab separated column string. `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. */ char *tctdbget3(TCTDB *tdb, const char *pkstr); /* Get the size of the value of a record in a table database object. `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. */ int tctdbvsiz(TCTDB *tdb, const void *pkbuf, int pksiz); /* Get the size of the value of a string record in a table database object. `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. */ int tctdbvsiz2(TCTDB *tdb, const char *pkstr); /* Initialize the iterator of a table database object. `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. */ bool tctdbiterinit(TCTDB *tdb); /* Get the next primary key of the iterator of a table database object. `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. */ void *tctdbiternext(TCTDB *tdb, int *sp); /* Get the next primary key string of the iterator of a table database object. `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. */ char *tctdbiternext2(TCTDB *tdb); /* Get the columns of the next record of the iterator of a table database object. `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. */ TCMAP *tctdbiternext3(TCTDB *tdb); /* Get forward matching primary keys in a table database object. `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. */ TCLIST *tctdbfwmkeys(TCTDB *tdb, const void *pbuf, int psiz, int max); /* Get forward matching string primary keys in a table database object. `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. */ TCLIST *tctdbfwmkeys2(TCTDB *tdb, const char *pstr, int max); /* Add an integer to a column of a record in a table database object. `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. */ int tctdbaddint(TCTDB *tdb, const void *pkbuf, int pksiz, int num); /* Add a real number to a column of a record in a table database object. `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. */ double tctdbadddouble(TCTDB *tdb, const void *pkbuf, int pksiz, double num); /* Synchronize updated contents of a table database object with the file and the device. `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. */ bool tctdbsync(TCTDB *tdb); /* Optimize the file of a table database object. `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: `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 record is compressed with BZIP2 encoding, `BDBTTCBS' 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. */ bool tctdboptimize(TCTDB *tdb, int64_t bnum, int8_t apow, int8_t fpow, uint8_t opts); /* Remove all records of a table database object. `tdb' specifies the table database object connected as a writer. If successful, the return value is true, else, it is false. */ bool tctdbvanish(TCTDB *tdb); /* Copy the database file of a table database object. `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. */ bool tctdbcopy(TCTDB *tdb, const char *path); /* Begin the transaction of a table database object. `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. */ bool tctdbtranbegin(TCTDB *tdb); /* Commit the transaction of a table database object. `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. */ bool tctdbtrancommit(TCTDB *tdb); /* Abort the transaction of a table database object. `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. */ bool tctdbtranabort(TCTDB *tdb); /* Get the file path of a table database object. `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. */ const char *tctdbpath(TCTDB *tdb); /* Get the number of records ccccof a table database object. `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. */ uint64_t tctdbrnum(TCTDB *tdb); /* Get the size of the database file of a table database object. `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. */ uint64_t tctdbfsiz(TCTDB *tdb); /* Set a column index to a table database object. `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. */ bool tctdbsetindex(TCTDB *tdb, const char *name, int type); /* Generate a unique ID number of a table database object. `tdb' specifies the table database object connected as a writer. The return value is the new unique ID number or -1 on failure. */ int64_t tctdbgenuid(TCTDB *tdb); /* Create a query object. `tdb' specifies the table database object. The return value is the new query object. */ TDBQRY *tctdbqrynew(TCTDB *tdb); /* Delete a query object. `qry' specifies the query object. */ void tctdbqrydel(TDBQRY *qry); /* Add a narrowing condition to a query object. `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. */ void tctdbqryaddcond(TDBQRY *qry, const char *name, int op, const char *expr); /* Set the order of a query object. `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. */ void tctdbqrysetorder(TDBQRY *qry, const char *name, int type); /* Set the limit number of records of the result of a query object. `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. */ void tctdbqrysetlimit(TDBQRY *qry, int max, int skip); /* Execute the search of a query object. `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. */ TCLIST *tctdbqrysearch(TDBQRY *qry); /* Remove each record corresponding to a query object. `qry' specifies the query object of the database connected as a writer. If successful, the return value is true, else, it is false. */ bool tctdbqrysearchout(TDBQRY *qry); /* Process each record corresponding to a query object. `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. */ bool tctdbqryproc(TDBQRY *qry, TDBQRYPROC proc, void *op); /* Get the hint string of a query object. `qry' specifies the query object. The return value is the hint string. This function should be called after the query execution by `tctdbqrysearch' and so on. The region of the return value is overwritten when this function is called again. */ const char *tctdbqryhint(TDBQRY *qry); /* Retrieve records with multiple query objects and get the set of the result. `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. */ TCLIST *tctdbmetasearch(TDBQRY **qrys, int num, int type); /************************************************************************************************* * features for experts *************************************************************************************************/ /* Set the error code of a table database object. `tdb' specifies the table database object. `ecode' specifies the error code. `file' specifies the file name of the code. `line' specifies the line number of the code. `func' specifies the function name of the code. */ void tctdbsetecode(TCTDB *tdb, int ecode, const char *filename, int line, const char *func); /* Set the file descriptor for debugging output. `tdb' specifies the table database object. `fd' specifies the file descriptor for debugging output. */ void tctdbsetdbgfd(TCTDB *tdb, int fd); /* Get the file descriptor for debugging output. `tdb' specifies the table database object. The return value is the file descriptor for debugging output. */ int tctdbdbgfd(TCTDB *tdb); /* Check whether mutual exclusion control is set to a table database object. `tdb' specifies the table database object. If mutual exclusion control is set, it is true, else it is false. */ bool tctdbhasmutex(TCTDB *tdb); /* Synchronize updating contents on memory of a table database object. `tdb' specifies the table database object connected as a writer. `phys' specifies whether to synchronize physically. If successful, the return value is true, else, it is false. */ bool tctdbmemsync(TCTDB *tdb, bool phys); /* Get the number of elements of the bucket array of a table database object. `tdb' specifies the table database object. The return value is the number of elements of the bucket array or 0 if the object does not connect to any database file. */ uint64_t tctdbbnum(TCTDB *tdb); /* Get the record alignment of a table database object. `tdb' specifies the table database object. The return value is the record alignment or 0 if the object does not connect to any database file. */ uint32_t tctdbalign(TCTDB *tdb); /* Get the maximum number of the free block pool of a table database object. `tdb' specifies the table database object. The return value is the maximum number of the free block pool or 0 if the object does not connect to any database file. */ uint32_t tctdbfbpmax(TCTDB *tdb); /* Get the inode number of the database file of a table database object. `tdb' specifies the table database object. The return value is the inode number of the database file or 0 if the object does not connect to any database file. */ uint64_t tctdbinode(TCTDB *tdb); /* Get the modification time of the database file of a table database object. `tdb' specifies the table database object. The return value is the inode number of the database file or 0 if the object does not connect to any database file. */ time_t tctdbmtime(TCTDB *tdb); /* Get the additional flags of a table database object. `tdb' specifies the table database object. The return value is the additional flags. */ uint8_t tctdbflags(TCTDB *tdb); /* Get the options of a table database object. `tdb' specifies the table database object. The return value is the options. */ uint8_t tctdbopts(TCTDB *tdb); /* Get the pointer to the opaque field of a table database object. `tdb' specifies the table database object. The return value is the pointer to the opaque field whose size is 128 bytes. */ char *tctdbopaque(TCTDB *tdb); /* Get the number of used elements of the bucket array of a table database object. `tdb' specifies the table database object. The return value is the number of used elements of the bucket array or 0 if the object does not connect to any database file. */ uint64_t tctdbbnumused(TCTDB *tdb); /* Get the number of column indices of a table database object. `tdb' specifies the table database object. The return value is the number of column indices or 0 if the object does not connect to any database file. */ int tctdbinum(TCTDB *tdb); /* Get the seed of unique ID unumbers of a table database object. `tdb' specifies the table database object. The return value is the seed of unique ID numbers or -1 on failure. */ int64_t tctdbuidseed(TCTDB *tdb); /* Set the seed of unique ID unumbers of a table database object. `tdb' specifies the table database object connected as a writer. If successful, the return value is true, else, it is false. */ bool tctdbsetuidseed(TCTDB *tdb, int64_t seed); /* Set the parameters of the inverted cache of a table database object. `tdb' specifies the table database object. `iccmax' specifies the maximum size. If it is not more than 0, the default value is specified. The default value is 67108864. `iccsync' specifies synchronization ratio. If it is not more than 0, the default value is specified. The default value is 0.01. If successful, the return value is true, else, it is false. Note that the caching parameters should be set before the database is opened. */ bool tctdbsetinvcache(TCTDB *tdb, int64_t iccmax, double iccsync); /* Set the custom codec functions of a table database object. `tdb' specifies the table database object. `enc' specifies the pointer to the custom encoding function. It receives four parameters. The first parameter is the pointer to the region. The second parameter is the size of the region. The third parameter is the pointer to the variable into which the size of the region of the return value is assigned. The fourth parameter is the pointer to the optional opaque object. It returns the pointer to the result object allocated with `malloc' call if successful, else, it returns `NULL'. `encop' specifies an arbitrary pointer to be given as a parameter of the encoding function. If it is not needed, `NULL' can be specified. `dec' specifies the pointer to the custom decoding function. `decop' specifies an arbitrary pointer to be given as a parameter of the decoding function. If it is not needed, `NULL' can be specified. If successful, the return value is true, else, it is false. Note that the custom codec functions should be set before the database is opened and should be set every time the database is being opened. */ bool tctdbsetcodecfunc(TCTDB *tdb, TCCODEC enc, void *encop, TCCODEC dec, void *decop); /* Get the unit step number of auto defragmentation of a table database object. `tdb' specifies the table database object. The return value is the unit step number of auto defragmentation. */ uint32_t tctdbdfunit(TCTDB *tdb); /* Perform dynamic defragmentation of a table database object. `tdb' specifies the table database object connected as a writer. `step' specifie the number of steps. If it is not more than 0, the whole file is defragmented gradually without keeping a continuous lock. If successful, the return value is true, else, it is false. */ bool tctdbdefrag(TCTDB *tdb, int64_t step); /* Clear the cache of a table tree database object. `tdb' specifies the table tree database object. If successful, the return value is true, else, it is false. */ bool tctdbcacheclear(TCTDB *tdb); /* Store a record into a table database object with a duplication handler. `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. `NULL' means that record addition is ommited if there is no corresponding record. `csiz' specifies the size of the region of the column string. `proc' specifies the pointer to the callback function to process duplication. It receives four parameters. The first parameter is the pointer to the region of the value. The second parameter is the size of the region of the value. The third parameter is the pointer to the variable into which the size of the region of the return value is assigned. The fourth parameter is the pointer to the optional opaque object. It returns the pointer to the result object allocated with `malloc'. It is released by the caller. If it is `NULL', the record is not modified. If it is `(void *)-1', the record is removed. `op' specifies an arbitrary pointer to be given as a parameter of the callback function. If it is not needed, `NULL' can be specified. If successful, the return value is true, else, it is false. Note that the callback function can not perform any database operation because the function is called in the critical section guarded by the same locks of database operations. */ bool tctdbputproc(TCTDB *tdb, const void *pkbuf, int pksiz, const void *cbuf, int csiz, TCPDPROC proc, void *op); /* Retrieve the value of a column of a record in a table database object. `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. `nbuf' specifies the pointer to the region of the column name. `nsiz' specifies the size of the region of the column name. `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 column of the corresponding record. `NULL' is returned if no record corresponds or there is no column. 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. */ char *tctdbget4(TCTDB *tdb, const void *pkbuf, int pksiz, const void *nbuf, int nsiz, int *sp); /* Move the iterator to the record corresponding a key of a table database object. `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 true, else, it is false. False is returned if there is no record corresponding the condition. */ bool tctdbiterinit2(TCTDB *tdb, const void *pkbuf, int pksiz); /* Move the iterator to the record corresponding a key string of a table database object. `tdb' specifies the table database object. `kstr' specifies the string of the primary key. If successful, the return value is true, else, it is false. False is returned if there is no record corresponding the condition. */ bool tctdbiterinit3(TCTDB *tdb, const char *kstr); /* Process each record atomically of a table database object. `tdb' specifies the table database object. `iter' specifies the pointer to the iterator function called for each record. It receives five parameters. The first parameter is the pointer to the region of the key. The second parameter is the size of the region of the key. The third parameter is the pointer to the region of the value. The fourth parameter is the size of the region of the value. The fifth parameter is the pointer to the optional opaque object. It returns true to continue iteration or false to stop 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. Note that the callback function can not perform any database operation because the function is called in the critical section guarded by the same locks of database operations. */ bool tctdbforeach(TCTDB *tdb, TCITER iter, void *op); /* Process each record corresponding to a query object with non-atomic fashion. `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. */ bool tctdbqryproc2(TDBQRY *qry, TDBQRYPROC proc, void *op); /* Remove each record corresponding to a query object with non-atomic fashion. `qry' specifies the query object of the database connected as a writer. If successful, the return value is true, else, it is false. */ bool tctdbqrysearchout2(TDBQRY *qry); /* Convert a string into the index type number. `str' specifies a string. The return value is the index type number or -1 on failure. */ int tctdbstrtoindextype(const char *str); /* Convert a string into the meta search type number. `str' specifies a string. The return value is the meta search type number or -1 on failure. */ int tctdbstrtometasearcytype(const char *str); /* Get the count of corresponding records of a query object. `qry' specifies the query object. The return value is the count of corresponding records. */ int tctdbqrycount(TDBQRY *qry); /* Generate keyword-in-context strings from a query object. `qry' specifies the query object. `cols' specifies a map object containing columns. `name' specifies the name of a column. If it is `NULL', the first column of the query is specified. `width' specifies the width of strings picked up around each keyword. `opts' specifies options by bitwise-or: `TCKWMUTAB' specifies that each keyword is marked up between two tab characters, `TCKWMUCTRL' specifies that each keyword is marked up by the STX (0x02) code and the ETX (0x03) code, `TCKWMUBRCT' specifies that each keyword is marked up by the two square brackets, `TCKWNOOVER' specifies that each context does not overlap, `TCKWPULEAD' specifies that the lead string is picked up forcibly. The return value is the list object whose elements are strings around keywords. 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. */ TCLIST *tctdbqrykwic(TDBQRY *qry, TCMAP *cols, const char *name, int width, int opts); /* Convert a string into the query operation number. `str' specifies a string. The return value is the query operation number or -1 on failure. */ int tctdbqrystrtocondop(const char *str); /* Convert a string into the query order type number. `str' specifies a string. The return value is the query order type or -1 on failure. */ int tctdbqrystrtoordertype(const char *str); /* Convert a string into the set operation type number. `str' specifies a string. The return value is the set operation type or -1 on failure. */ int tctdbmetastrtosettype(const char *str); __TCTDB_CLINKAGEEND #endif /* duplication check */ /* END OF FILE */ tokyocabinet-1.4.48/tchmttest.c0000644000175000017500000014644312013574446015512 0ustar mikiomikio/************************************************************************************************* * The test cases of the hash database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" #define RECBUFSIZ 48 // buffer for records typedef struct { // type of structure for write thread TCHDB *hdb; int rnum; bool as; bool rnd; int id; } TARGWRITE; typedef struct { // type of structure for read thread TCHDB *hdb; int rnum; bool wb; bool rnd; int id; } TARGREAD; typedef struct { // type of structure for remove thread TCHDB *hdb; int rnum; bool rnd; int id; } TARGREMOVE; typedef struct { // type of structure for wicked thread TCHDB *hdb; int rnum; bool nc; int id; TCMAP *map; } TARGWICKED; typedef struct { // type of structure for typical thread TCHDB *hdb; int rnum; bool nc; int rratio; int id; } TARGTYPICAL; typedef struct { // type of structure for race thread TCHDB *hdb; int rnum; int id; } TARGRACE; /* global variables */ const char *g_progname; // program name unsigned int g_randseed; // random seed int g_dbgfd; // debugging output /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void iprintf(const char *format, ...); static void iputchar(int c); static void eprint(TCHDB *hdb, int line, const char *func); static void mprint(TCHDB *hdb); static void sysprint(void); static int myrand(int range); static int myrandnd(int range); static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op); static int runwrite(int argc, char **argv); static int runread(int argc, char **argv); static int runremove(int argc, char **argv); static int runwicked(int argc, char **argv); static int runtypical(int argc, char **argv); static int runrace(int argc, char **argv); static int procwrite(const char *path, int tnum, int rnum, int bnum, int apow, int fpow, int opts, int rcnum, int xmsiz, int dfunit, int omode, bool as, bool rnd); static int procread(const char *path, int tnum, int rcnum, int xmsiz, int dfunit, int omode, bool wb, bool rnd); static int procremove(const char *path, int tnum, int rcnum, int xmsiz, int dfunit, int omode, bool rnd); static int procwicked(const char *path, int tnum, int rnum, int opts, int omode, bool nc); static int proctypical(const char *path, int tnum, int rnum, int bnum, int apow, int fpow, int opts, int rcnum, int xmsiz, int dfunit, int omode, bool nc, int rratio); static int procrace(const char *path, int tnum, int rnum, int bnum, int apow, int fpow, int opts, int xmsiz, int dfunit, int omode); static void *threadwrite(void *targ); static void *threadread(void *targ); static void *threadremove(void *targ); static void *threadwicked(void *targ); static void *threadtypical(void *targ); static void *threadrace(void *targ); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; const char *ebuf = getenv("TCRNDSEED"); g_randseed = ebuf ? tcatoix(ebuf) : tctime() * 1000; srand(g_randseed); ebuf = getenv("TCDBGFD"); g_dbgfd = ebuf ? tcatoix(ebuf) : UINT16_MAX; if(argc < 2) usage(); int 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], "remove")){ rv = runremove(argc, argv); } else if(!strcmp(argv[1], "wicked")){ rv = runwicked(argc, argv); } else if(!strcmp(argv[1], "typical")){ rv = runtypical(argc, argv); } else if(!strcmp(argv[1], "race")){ rv = runrace(argc, argv); } else { usage(); } if(rv != 0){ printf("FAILED: TCRNDSEED=%u PID=%d", g_randseed, (int)getpid()); for(int i = 0; i < argc; i++){ printf(" %s", argv[i]); } printf("\n\n"); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: test cases of the hash database API of Tokyo Cabinet\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s write [-tl] [-td|-tb|-tt|-tx] [-rc num] [-xm num] [-df num]" " [-nl|-nb] [-as] [-rnd] path tnum rnum [bnum [apow [fpow]]]\n", g_progname); fprintf(stderr, " %s read [-rc num] [-xm num] [-df num] [-nl|-nb] [-wb] [-rnd] path tnum\n", g_progname); fprintf(stderr, " %s remove [-rc num] [-xm num] [-df num] [-nl|-nb] [-rnd] path tnum\n", g_progname); fprintf(stderr, " %s wicked [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] [-nc]" " path tnum rnum\n", g_progname); fprintf(stderr, " %s typical [-tl] [-td|-tb|-tt|-tx] [-rc num] [-xm num] [-df num]" " [-nl|-nb] [-nc] [-rr num] path tnum rnum [bnum [apow [fpow]]]\n", g_progname); fprintf(stderr, " %s race [-tl] [-td|-tb|-tt|-tx] [-xm num] [-df num] [-nl|-nb]" " path tnum rnum [bnum [apow [fpow]]]\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); } /* print a character and flush the buffer */ static void iputchar(int c){ putchar(c); fflush(stdout); } /* print error message of hash database */ static void eprint(TCHDB *hdb, int line, const char *func){ const char *path = tchdbpath(hdb); int ecode = tchdbecode(hdb); fprintf(stderr, "%s: %s: %d: %s: error: %d: %s\n", g_progname, path ? path : "-", line, func, ecode, tchdberrmsg(ecode)); } /* print members of hash database */ static void mprint(TCHDB *hdb){ if(hdb->cnt_writerec < 0) return; iprintf("bucket number: %lld\n", (long long)tchdbbnum(hdb)); iprintf("used bucket number: %lld\n", (long long)tchdbbnumused(hdb)); iprintf("cnt_writerec: %lld\n", (long long)hdb->cnt_writerec); iprintf("cnt_reuserec: %lld\n", (long long)hdb->cnt_reuserec); iprintf("cnt_moverec: %lld\n", (long long)hdb->cnt_moverec); iprintf("cnt_readrec: %lld\n", (long long)hdb->cnt_readrec); iprintf("cnt_searchfbp: %lld\n", (long long)hdb->cnt_searchfbp); iprintf("cnt_insertfbp: %lld\n", (long long)hdb->cnt_insertfbp); iprintf("cnt_splicefbp: %lld\n", (long long)hdb->cnt_splicefbp); iprintf("cnt_dividefbp: %lld\n", (long long)hdb->cnt_dividefbp); iprintf("cnt_mergefbp: %lld\n", (long long)hdb->cnt_mergefbp); iprintf("cnt_reducefbp: %lld\n", (long long)hdb->cnt_reducefbp); iprintf("cnt_appenddrp: %lld\n", (long long)hdb->cnt_appenddrp); iprintf("cnt_deferdrp: %lld\n", (long long)hdb->cnt_deferdrp); iprintf("cnt_flushdrp: %lld\n", (long long)hdb->cnt_flushdrp); iprintf("cnt_adjrecc: %lld\n", (long long)hdb->cnt_adjrecc); iprintf("cnt_defrag: %lld\n", (long long)hdb->cnt_defrag); iprintf("cnt_shiftrec: %lld\n", (long long)hdb->cnt_shiftrec); iprintf("cnt_trunc: %lld\n", (long long)hdb->cnt_trunc); } /* print system information */ static void sysprint(void){ TCMAP *info = tcsysinfo(); if(info){ tcmapiterinit(info); const char *kbuf; while((kbuf = tcmapiternext2(info)) != NULL){ iprintf("sys_%s: %s\n", kbuf, tcmapiterval2(kbuf)); } tcmapdel(info); } } /* get a random number */ static int myrand(int range){ if(range < 2) return 0; int high = (unsigned int)rand() >> 4; int low = range * (rand() / (RAND_MAX + 1.0)); low &= (unsigned int)INT_MAX >> 4; return (high + low) % range; } /* get a random number based on normal distribution */ static int myrandnd(int range){ int num = (int)tcdrandnd(range >> 1, range / 10); return (num < 0 || num >= range) ? 0 : num; } /* iterator function */ static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op){ unsigned int sum = 0; while(--ksiz >= 0){ sum += ((char *)kbuf)[ksiz]; } while(--vsiz >= 0){ sum += ((char *)vbuf)[vsiz]; } return myrand(100 + (sum & 0xff)) > 0; } /* parse arguments of write command */ static int runwrite(int argc, char **argv){ char *path = NULL; char *tstr = NULL; char *rstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; int opts = 0; int rcnum = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; bool as = false; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tl")){ opts |= HDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= HDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= HDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= HDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= HDBTEXCODEC; } else if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else if(!strcmp(argv[i], "-as")){ as = true; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = procwrite(path, tnum, rnum, bnum, apow, fpow, opts, rcnum, xmsiz, dfunit, omode, as, rnd); return rv; } /* parse arguments of read command */ static int runread(int argc, char **argv){ char *path = NULL; char *tstr = NULL; int rcnum = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; bool wb = false; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else if(!strcmp(argv[i], "-wb")){ wb = true; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else { usage(); } } if(!path || !tstr) usage(); int tnum = tcatoix(tstr); if(tnum < 1) usage(); int rv = procread(path, tnum, rcnum, xmsiz, dfunit, omode, wb, rnd); return rv; } /* parse arguments of remove command */ static int runremove(int argc, char **argv){ char *path = NULL; char *tstr = NULL; int rcnum = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else { usage(); } } if(!path || !tstr) usage(); int tnum = tcatoix(tstr); if(tnum < 1) usage(); int rv = procremove(path, tnum, rcnum, xmsiz, dfunit, omode, rnd); return rv; } /* parse arguments of wicked command */ static int runwicked(int argc, char **argv){ char *path = NULL; char *tstr = NULL; char *rstr = NULL; int opts = 0; int omode = 0; bool nc = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tl")){ opts |= HDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= HDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= HDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= HDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= HDBTEXCODEC; } else if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else if(!strcmp(argv[i], "-nc")){ nc = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!path || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int rv = procwicked(path, tnum, rnum, opts, omode, nc); return rv; } /* parse arguments of typical command */ static int runtypical(int argc, char **argv){ char *path = NULL; char *tstr = NULL; char *rstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; int opts = 0; int rcnum = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; int rratio = -1; bool nc = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tl")){ opts |= HDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= HDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= HDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= HDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= HDBTEXCODEC; } else if(!strcmp(argv[i], "-rc")){ if(++i >= argc) usage(); rcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else if(!strcmp(argv[i], "-nc")){ nc = true; } else if(!strcmp(argv[i], "-rr")){ if(++i >= argc) usage(); rratio = tcatoix(argv[i]); } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = proctypical(path, tnum, rnum, bnum, apow, fpow, opts, rcnum, xmsiz, dfunit, omode, nc, rratio); return rv; } /* parse arguments of race command */ static int runrace(int argc, char **argv){ char *path = NULL; char *tstr = NULL; char *rstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; int opts = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-tl")){ opts |= HDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= HDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= HDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= HDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= HDBTEXCODEC; } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= HDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= HDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = procrace(path, tnum, rnum, bnum, apow, fpow, opts, xmsiz, dfunit, omode); return rv; } /* perform write command */ static int procwrite(const char *path, int tnum, int rnum, int bnum, int apow, int fpow, int opts, int rcnum, int xmsiz, int dfunit, int omode, bool as, bool rnd){ iprintf("\n seed=%u path=%s tnum=%d rnum=%d bnum=%d apow=%d fpow=%d" " opts=%d rcnum=%d xmsiz=%d dfunit=%d omode=%d as=%d rnd=%d\n\n", g_randseed, path, tnum, rnum, bnum, apow, fpow, opts, rcnum, xmsiz, dfunit, omode, as, rnd); bool err = false; double stime = tctime(); TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(!tchdbsetmutex(hdb)){ eprint(hdb, __LINE__, "tchdbsetmutex"); err = true; } if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(hdb, __LINE__, "tchdbsetcodecfunc"); err = true; } if(!tchdbtune(hdb, bnum, apow, fpow, opts)){ eprint(hdb, __LINE__, "tchdbtune"); err = true; } if(!tchdbsetcache(hdb, rcnum)){ eprint(hdb, __LINE__, "tchdbsetcache"); err = true; } if(xmsiz >= 0 && !tchdbsetxmsiz(hdb, xmsiz)){ eprint(hdb, __LINE__, "tchdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tchdbsetdfunit(hdb, dfunit)){ eprint(hdb, __LINE__, "tchdbsetdfunit"); err = true; } if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){ eprint(hdb, __LINE__, "tchdbopen"); err = true; } TARGWRITE targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].hdb = hdb; targs[0].rnum = rnum; targs[0].as = as; targs[0].rnd = rnd; targs[0].id = 0; if(threadwrite(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].hdb = hdb; targs[i].rnum = rnum; targs[i].as = as; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadwrite, targs + i) != 0){ eprint(hdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(hdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb)); iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb)); mprint(hdb); sysprint(); if(!tchdbclose(hdb)){ eprint(hdb, __LINE__, "tchdbclose"); err = true; } tchdbdel(hdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform read command */ static int procread(const char *path, int tnum, int rcnum, int xmsiz, int dfunit, int omode, bool wb, bool rnd){ iprintf("\n seed=%u path=%s tnum=%d rcnum=%d xmsiz=%d dfunit=%d omode=%d" " wb=%d rnd=%d\n\n", g_randseed, path, tnum, rcnum, xmsiz, dfunit, omode, wb, rnd); bool err = false; double stime = tctime(); TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(!tchdbsetmutex(hdb)){ eprint(hdb, __LINE__, "tchdbsetmutex"); err = true; } if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(hdb, __LINE__, "tchdbsetcodecfunc"); err = true; } if(!tchdbsetcache(hdb, rcnum)){ eprint(hdb, __LINE__, "tchdbsetcache"); err = true; } if(xmsiz >= 0 && !tchdbsetxmsiz(hdb, xmsiz)){ eprint(hdb, __LINE__, "tchdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tchdbsetdfunit(hdb, dfunit)){ eprint(hdb, __LINE__, "tchdbsetdfunit"); err = true; } if(!tchdbopen(hdb, path, HDBOREADER | omode)){ eprint(hdb, __LINE__, "tchdbopen"); err = true; } int rnum = tchdbrnum(hdb) / tnum; TARGREAD targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].hdb = hdb; targs[0].rnum = rnum; targs[0].wb = wb; targs[0].rnd = rnd; targs[0].id = 0; if(threadread(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].hdb = hdb; targs[i].rnum = rnum; targs[i].wb = wb; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){ eprint(hdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(hdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb)); iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb)); mprint(hdb); sysprint(); if(!tchdbclose(hdb)){ eprint(hdb, __LINE__, "tchdbclose"); err = true; } tchdbdel(hdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform remove command */ static int procremove(const char *path, int tnum, int rcnum, int xmsiz, int dfunit, int omode, bool rnd){ iprintf("\n seed=%u path=%s tnum=%d rcnum=%d xmsiz=%d dfunit=%d omode=%d" " rnd=%d\n\n", g_randseed, path, tnum, rcnum, xmsiz, dfunit, omode, rnd); bool err = false; double stime = tctime(); TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(!tchdbsetmutex(hdb)){ eprint(hdb, __LINE__, "tchdbsetmutex"); err = true; } if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(hdb, __LINE__, "tchdbsetcodecfunc"); err = true; } if(!tchdbsetcache(hdb, rcnum)){ eprint(hdb, __LINE__, "tchdbsetcache"); err = true; } if(xmsiz >= 0 && !tchdbsetxmsiz(hdb, xmsiz)){ eprint(hdb, __LINE__, "tchdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tchdbsetdfunit(hdb, dfunit)){ eprint(hdb, __LINE__, "tchdbsetdfunit"); err = true; } if(!tchdbopen(hdb, path, HDBOWRITER | omode)){ eprint(hdb, __LINE__, "tchdbopen"); err = true; } int rnum = tchdbrnum(hdb) / tnum; TARGREMOVE targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].hdb = hdb; targs[0].rnum = rnum; targs[0].rnd = rnd; targs[0].id = 0; if(threadremove(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].hdb = hdb; targs[i].rnum = rnum; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadremove, targs + i) != 0){ eprint(hdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(hdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb)); iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb)); mprint(hdb); sysprint(); if(!tchdbclose(hdb)){ eprint(hdb, __LINE__, "tchdbclose"); err = true; } tchdbdel(hdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform wicked command */ static int procwicked(const char *path, int tnum, int rnum, int opts, int omode, bool nc){ iprintf("\n seed=%u path=%s tnum=%d rnum=%d opts=%d omode=%d nc=%d\n\n", g_randseed, path, tnum, rnum, opts, omode, nc); bool err = false; double stime = tctime(); TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(!tchdbsetmutex(hdb)){ eprint(hdb, __LINE__, "tchdbsetmutex"); err = true; } if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(hdb, __LINE__, "tchdbsetcodecfunc"); err = true; } if(!tchdbtune(hdb, rnum / 50, 2, -1, opts)){ eprint(hdb, __LINE__, "tchdbtune"); err = true; } if(!tchdbsetcache(hdb, rnum / 2)){ eprint(hdb, __LINE__, "tchdbsetcache"); err = true; } if(!tchdbsetxmsiz(hdb, rnum * sizeof(int))){ eprint(hdb, __LINE__, "tchdbsetxmsiz"); err = true; } if(!tchdbsetdfunit(hdb, 8)){ eprint(hdb, __LINE__, "tchdbsetdfunit"); err = true; } if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){ eprint(hdb, __LINE__, "tchdbopen"); err = true; } if(!tchdbiterinit(hdb)){ eprint(hdb, __LINE__, "tchdbiterinit"); err = true; } TARGWICKED targs[tnum]; pthread_t threads[tnum]; TCMAP *map = tcmapnew(); if(tnum == 1){ targs[0].hdb = hdb; targs[0].rnum = rnum; targs[0].nc = nc; targs[0].id = 0; targs[0].map = map; if(threadwicked(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].hdb = hdb; targs[i].rnum = rnum; targs[i].nc = nc; targs[i].id = i; targs[i].map = map; if(pthread_create(threads + i, NULL, threadwicked, targs + i) != 0){ eprint(hdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(hdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } if(!nc){ if(!tchdbsync(hdb)){ eprint(hdb, __LINE__, "tchdbsync"); err = true; } if(tchdbrnum(hdb) != tcmaprnum(map)){ eprint(hdb, __LINE__, "(validation)"); err = true; } int end = rnum * tnum; for(int i = 1; i <= end && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", i - 1); int vsiz; const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz); int rsiz; char *rbuf = tchdbget(hdb, kbuf, ksiz, &rsiz); if(vbuf){ iputchar('.'); if(!rbuf){ eprint(hdb, __LINE__, "tchdbget"); err = true; } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(hdb, __LINE__, "(validation)"); err = true; } } else { iputchar('*'); if(rbuf || tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "(validation)"); err = true; } } tcfree(rbuf); if(i % 50 == 0) iprintf(" (%08d)\n", i); } if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum); } tcmapdel(map); iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb)); iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb)); mprint(hdb); sysprint(); if(!tchdbclose(hdb)){ eprint(hdb, __LINE__, "tchdbclose"); err = true; } tchdbdel(hdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform typical command */ static int proctypical(const char *path, int tnum, int rnum, int bnum, int apow, int fpow, int opts, int rcnum, int xmsiz, int dfunit, int omode, bool nc, int rratio){ iprintf("\n seed=%u path=%s tnum=%d rnum=%d bnum=%d apow=%d" " fpow=%d opts=%d rcnum=%d xmsiz=%d dfunit=%d omode=%d nc=%d rratio=%d\n\n", g_randseed, path, tnum, rnum, bnum, apow, fpow, opts, rcnum, xmsiz, dfunit, omode, nc, rratio); bool err = false; double stime = tctime(); TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(!tchdbsetmutex(hdb)){ eprint(hdb, __LINE__, "tchdbsetmutex"); err = true; } if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(hdb, __LINE__, "tchdbsetcodecfunc"); err = true; } if(!tchdbtune(hdb, bnum, apow, fpow, opts)){ eprint(hdb, __LINE__, "tchdbtune"); err = true; } if(!tchdbsetcache(hdb, rcnum)){ eprint(hdb, __LINE__, "tchdbsetcache"); err = true; } if(xmsiz >= 0 && !tchdbsetxmsiz(hdb, xmsiz)){ eprint(hdb, __LINE__, "tchdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tchdbsetdfunit(hdb, dfunit)){ eprint(hdb, __LINE__, "tchdbsetdfunit"); err = true; } if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){ eprint(hdb, __LINE__, "tchdbopen"); err = true; } TARGTYPICAL targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].hdb = hdb; targs[0].rnum = rnum; targs[0].nc = nc; targs[0].rratio = rratio; targs[0].id = 0; if(threadtypical(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].hdb = hdb; targs[i].rnum = rnum; targs[i].nc = nc; targs[i].rratio= rratio; targs[i].id = i; if(pthread_create(threads + i, NULL, threadtypical, targs + i) != 0){ eprint(hdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(hdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb)); iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb)); mprint(hdb); sysprint(); if(!tchdbclose(hdb)){ eprint(hdb, __LINE__, "tchdbclose"); err = true; } tchdbdel(hdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform race command */ static int procrace(const char *path, int tnum, int rnum, int bnum, int apow, int fpow, int opts, int xmsiz, int dfunit, int omode){ iprintf("\n seed=%u path=%s tnum=%d rnum=%d bnum=%d apow=%d" " fpow=%d opts=%d xmsiz=%d dfunit=%d omode=%d\n\n", g_randseed, path, tnum, rnum, bnum, apow, fpow, opts, xmsiz, dfunit, omode); bool err = false; double stime = tctime(); TCHDB *hdb = tchdbnew(); if(g_dbgfd >= 0) tchdbsetdbgfd(hdb, g_dbgfd); if(!tchdbsetmutex(hdb)){ eprint(hdb, __LINE__, "tchdbsetmutex"); err = true; } if(!tchdbsetcodecfunc(hdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(hdb, __LINE__, "tchdbsetcodecfunc"); err = true; } if(!tchdbtune(hdb, bnum, apow, fpow, opts)){ eprint(hdb, __LINE__, "tchdbtune"); err = true; } if(xmsiz >= 0 && !tchdbsetxmsiz(hdb, xmsiz)){ eprint(hdb, __LINE__, "tchdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tchdbsetdfunit(hdb, dfunit)){ eprint(hdb, __LINE__, "tchdbsetdfunit"); err = true; } if(!tchdbopen(hdb, path, HDBOWRITER | HDBOCREAT | HDBOTRUNC | omode)){ eprint(hdb, __LINE__, "tchdbopen"); err = true; } TARGRACE targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].hdb = hdb; targs[0].rnum = rnum; targs[0].id = 0; if(threadrace(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].hdb = hdb; targs[i].rnum = rnum; targs[i].id = i; if(pthread_create(threads + i, NULL, threadrace, targs + i) != 0){ eprint(hdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(hdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tchdbrnum(hdb)); iprintf("size: %llu\n", (unsigned long long)tchdbfsiz(hdb)); mprint(hdb); sysprint(); if(!tchdbclose(hdb)){ eprint(hdb, __LINE__, "tchdbclose"); err = true; } tchdbdel(hdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* thread the write function */ static void *threadwrite(void *targ){ TCHDB *hdb = ((TARGWRITE *)targ)->hdb; int rnum = ((TARGWRITE *)targ)->rnum; bool as = ((TARGWRITE *)targ)->as; bool rnd = ((TARGWRITE *)targ)->rnd; int id = ((TARGWRITE *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) : i)); if(as){ if(!tchdbputasync(hdb, buf, len, buf, len)){ eprint(hdb, __LINE__, "tchdbputasync"); err = true; break; } } else { if(!tchdbput(hdb, buf, len, buf, len)){ eprint(hdb, __LINE__, "tchdbput"); err = true; break; } } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the read function */ static void *threadread(void *targ){ TCHDB *hdb = ((TARGREAD *)targ)->hdb; int rnum = ((TARGREAD *)targ)->rnum; bool wb = ((TARGREAD *)targ)->wb; bool rnd = ((TARGREAD *)targ)->rnd; int id = ((TARGREAD *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrandnd(i) : i)); int vsiz; if(wb){ char vbuf[RECBUFSIZ]; int vsiz = tchdbget3(hdb, kbuf, ksiz, vbuf, RECBUFSIZ); if(vsiz < 0 && (!rnd || tchdbecode(hdb) != TCENOREC)){ eprint(hdb, __LINE__, "tchdbget3"); err = true; } } else { char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz); if(!vbuf && (!rnd || tchdbecode(hdb) != TCENOREC)){ eprint(hdb, __LINE__, "tchdbget"); err = true; } tcfree(vbuf); } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the remove function */ static void *threadremove(void *targ){ TCHDB *hdb = ((TARGREMOVE *)targ)->hdb; int rnum = ((TARGREMOVE *)targ)->rnum; bool rnd = ((TARGREMOVE *)targ)->rnd; int id = ((TARGREMOVE *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrand(i + 1) : i)); if(!tchdbout(hdb, kbuf, ksiz) && (!rnd || tchdbecode(hdb) != TCENOREC)){ eprint(hdb, __LINE__, "tchdbout"); err = true; break; } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the wicked function */ static void *threadwicked(void *targ){ TCHDB *hdb = ((TARGWICKED *)targ)->hdb; int rnum = ((TARGWICKED *)targ)->rnum; bool nc = ((TARGWICKED *)targ)->nc; int id = ((TARGWICKED *)targ)->id; TCMAP *map = ((TARGWICKED *)targ)->map; bool err = false; for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum * (id + 1))); char vbuf[RECBUFSIZ]; int vsiz = myrand(RECBUFSIZ); memset(vbuf, '*', vsiz); vbuf[vsiz] = '\0'; char *rbuf; if(!nc) tcglobalmutexlock(); switch(myrand(16)){ case 0: if(id == 0) iputchar('0'); if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: if(id == 0) iputchar('1'); if(!tchdbput2(hdb, kbuf, vbuf)){ eprint(hdb, __LINE__, "tchdbput2"); err = true; } if(!nc) tcmapput2(map, kbuf, vbuf); break; case 2: if(id == 0) iputchar('2'); if(!tchdbputkeep(hdb, kbuf, ksiz, vbuf, vsiz) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbputkeep"); err = true; } if(!nc) tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 3: if(id == 0) iputchar('3'); if(!tchdbputkeep2(hdb, kbuf, vbuf) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbputkeep2"); err = true; } if(!nc) tcmapputkeep2(map, kbuf, vbuf); break; case 4: if(id == 0) iputchar('4'); if(!tchdbputcat(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbputcat"); err = true; } if(!nc) tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 5: if(id == 0) iputchar('5'); if(!tchdbputcat2(hdb, kbuf, vbuf)){ eprint(hdb, __LINE__, "tchdbputcat2"); err = true; } if(!nc) tcmapputcat2(map, kbuf, vbuf); break; case 6: if(id == 0) iputchar('6'); if(i > rnum / 4 * 3){ if(!tchdbputasync(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbputasync"); err = true; } } else { if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } } if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 7: if(id == 0) iputchar('7'); if(i > rnum / 4 * 3){ if(!tchdbputasync2(hdb, kbuf, vbuf)){ eprint(hdb, __LINE__, "tchdbputasync2"); err = true; } } else { if(!tchdbput2(hdb, kbuf, vbuf)){ eprint(hdb, __LINE__, "tchdbput2"); err = true; } } if(!nc) tcmapput2(map, kbuf, vbuf); break; case 8: if(id == 0) iputchar('8'); if(myrand(2) == 0){ if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbout"); err = true; } if(!nc) tcmapout(map, kbuf, ksiz); } break; case 9: if(id == 0) iputchar('9'); if(myrand(2) == 0){ if(!tchdbout2(hdb, kbuf) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbout2"); err = true; } if(!nc) tcmapout2(map, kbuf); } break; case 10: if(id == 0) iputchar('A'); if(!(rbuf = tchdbget(hdb, kbuf, ksiz, &vsiz))){ if(tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbget"); err = true; } rbuf = tcsprintf("[%d]", myrand(i + 1)); vsiz = strlen(rbuf); } vsiz += myrand(vsiz); if(myrand(3) == 0) vsiz += PATH_MAX; rbuf = tcrealloc(rbuf, vsiz + 1); for(int j = 0; j < vsiz; j++){ rbuf[j] = myrand(0x100); } if(!tchdbput(hdb, kbuf, ksiz, rbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } if(!nc) tcmapput(map, kbuf, ksiz, rbuf, vsiz); tcfree(rbuf); break; case 11: if(id == 0) iputchar('B'); if(!(rbuf = tchdbget(hdb, kbuf, ksiz, &vsiz)) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbget"); err = true; } tcfree(rbuf); break; case 12: if(id == 0) iputchar('C'); if(!(rbuf = tchdbget2(hdb, kbuf)) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbget2"); err = true; } tcfree(rbuf); break; case 13: if(id == 0) iputchar('D'); if(myrand(1) == 0) vsiz = 1; if((vsiz = tchdbget3(hdb, kbuf, ksiz, vbuf, vsiz)) < 0 && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbget3"); err = true; } break; case 14: if(id == 0) iputchar('E'); if(myrand(rnum / 50) == 0){ if(!tchdbiterinit(hdb)){ eprint(hdb, __LINE__, "tchdbiterinit"); err = true; } } TCXSTR *ikey = tcxstrnew(); TCXSTR *ival = tcxstrnew(); for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){ if(j % 3 == 0){ if(!tchdbiternext3(hdb, ikey, ival)){ int ecode = tchdbecode(hdb); if(ecode != TCEINVALID && ecode != TCENOREC){ eprint(hdb, __LINE__, "tchdbiternext3"); err = true; } } } else { int iksiz; char *ikbuf = tchdbiternext(hdb, &iksiz); if(ikbuf){ tcfree(ikbuf); } else { int ecode = tchdbecode(hdb); if(ecode != TCEINVALID && ecode != TCENOREC){ eprint(hdb, __LINE__, "tchdbiternext"); err = true; } } } } tcxstrdel(ival); tcxstrdel(ikey); break; default: if(id == 0) iputchar('@'); if(tchdbtranbegin(hdb)){ if(myrand(2) == 0){ if(!tchdbput(hdb, kbuf, ksiz, vbuf, vsiz)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz); } else { if(!tchdbout(hdb, kbuf, ksiz) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbout"); err = true; } if(!nc) tcmapout(map, kbuf, ksiz); } if(nc && myrand(2) == 0){ if(!tchdbtranabort(hdb)){ eprint(hdb, __LINE__, "tchdbtranabort"); err = true; } } else { if(!tchdbtrancommit(hdb)){ eprint(hdb, __LINE__, "tchdbtrancommit"); err = true; } } } else { eprint(hdb, __LINE__, "tchdbtranbegin"); err = true; } if(myrand(1000) == 0){ if(!tchdbforeach(hdb, iterfunc, NULL)){ eprint(hdb, __LINE__, "tchdbforeach"); err = true; } } if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX); break; } if(!nc) tcglobalmutexunlock(); if(id == 0){ if(i % 50 == 0) iprintf(" (%08d)\n", i); if(id == 0 && i == rnum / 4){ if(!tchdboptimize(hdb, rnum / 50, -1, -1, -1) && tchdbecode(hdb) != TCEINVALID){ eprint(hdb, __LINE__, "tchdboptimize"); err = true; } if(!tchdbiterinit(hdb)){ eprint(hdb, __LINE__, "tchdbiterinit"); err = true; } } } } return err ? "error" : NULL; } /* thread the typical function */ static void *threadtypical(void *targ){ TCHDB *hdb = ((TARGTYPICAL *)targ)->hdb; int rnum = ((TARGTYPICAL *)targ)->rnum; bool nc = ((TARGTYPICAL *)targ)->nc; int rratio = ((TARGTYPICAL *)targ)->rratio; int id = ((TARGTYPICAL *)targ)->id; bool err = false; TCMAP *map = (!nc && id == 0) ? tcmapnew2(rnum + 1) : NULL; int base = id * rnum; int mrange = tclmax(50 + rratio, 100); for(int i = 1; !err && i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", base + myrandnd(i)); int rnd = myrand(mrange); if(rnd < 10){ if(!tchdbput(hdb, buf, len, buf, len)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } if(map) tcmapput(map, buf, len, buf, len); } else if(rnd < 15){ if(!tchdbputkeep(hdb, buf, len, buf, len) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbputkeep"); err = true; } if(map) tcmapputkeep(map, buf, len, buf, len); } else if(rnd < 20){ if(!tchdbputcat(hdb, buf, len, buf, len)){ eprint(hdb, __LINE__, "tchdbputcat"); err = true; } if(map) tcmapputcat(map, buf, len, buf, len); } else if(rnd < 25){ if(i > rnum / 10 * 9){ if(!tchdbputasync(hdb, buf, len, buf, len)){ eprint(hdb, __LINE__, "tchdbputasync"); err = true; } } else { if(!tchdbput(hdb, buf, len, buf, len)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } } if(map) tcmapput(map, buf, len, buf, len); } else if(rnd < 30){ if(!tchdbout(hdb, buf, len) && tchdbecode(hdb) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbout"); err = true; } if(map) tcmapout(map, buf, len); } else if(rnd < 31){ if(myrand(10) == 0 && !tchdbiterinit(hdb) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbiterinit"); err = true; } for(int j = 0; !err && j < 10; j++){ int ksiz; char *kbuf = tchdbiternext(hdb, &ksiz); if(kbuf){ tcfree(kbuf); } else if(tchdbecode(hdb) != TCEINVALID && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbiternext"); err = true; } } } else { int vsiz; char *vbuf = tchdbget(hdb, buf, len, &vsiz); if(vbuf){ if(map){ int msiz; const char *mbuf = tcmapget(map, buf, len, &msiz); if(!mbuf || msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){ eprint(hdb, __LINE__, "(validation)"); err = true; } } tcfree(vbuf); } else { if(tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbget"); err = true; } if(map && tcmapget(map, buf, len, &vsiz)){ eprint(hdb, __LINE__, "(validation)"); err = true; } } } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(map){ tcmapiterinit(map); int ksiz; const char *kbuf; while(!err && (kbuf = tcmapiternext(map, &ksiz)) != NULL){ int vsiz; char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz); if(vbuf){ int msiz; const char *mbuf = tcmapget(map, kbuf, ksiz, &msiz); if(!mbuf || msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){ eprint(hdb, __LINE__, "(validation)"); err = true; } tcfree(vbuf); } else { eprint(hdb, __LINE__, "(validation)"); err = true; } } tcmapdel(map); } return err ? "error" : NULL; } /* thread the race function */ static void *threadrace(void *targ){ TCHDB *hdb = ((TARGRACE *)targ)->hdb; int rnum = ((TARGRACE *)targ)->rnum; int id = ((TARGRACE *)targ)->id; bool err = false; int mid = rnum * 2; for(int i = 1; !err && i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%d", myrandnd(i)); int rnd = myrand(100); if(rnd < 10){ if(!tchdbputkeep(hdb, buf, len, buf, len) && tchdbecode(hdb) != TCEKEEP){ eprint(hdb, __LINE__, "tchdbputkeep"); err = true; } } else if(rnd < 20){ if(!tchdbputcat(hdb, buf, len, buf, len)){ eprint(hdb, __LINE__, "tchdbputcat"); err = true; } } else if(rnd < 30){ if(!tchdbout(hdb, buf, len) && tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbout"); err = true; } } else if(rnd < 31){ if(!tchdbputasync(hdb, buf, len, buf, len)){ eprint(hdb, __LINE__, "tchdbputasync"); err = true; } } else { if(myrand(10) == 0){ int rsiz = myrand(256); char *rbuf = tcmalloc(rsiz + 1); for(int j = 0; j < rsiz; j++){ rbuf[j] = myrand('z' - 'a') + 'a'; } if(myrand(2) == 0){ if(!tchdbput(hdb, buf, len, rbuf, rsiz)){ eprint(hdb, __LINE__, "tchdbputcat"); err = true; } } else { if(!tchdbputcat(hdb, buf, len, rbuf, rsiz)){ eprint(hdb, __LINE__, "tchdbputcat"); err = true; } } tcfree(rbuf); } else { if(!tchdbput(hdb, buf, len, buf, len)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } } } if(id == 0){ if(myrand(mid) == 0){ iprintf("[v]"); if(!tchdbvanish(hdb)){ eprint(hdb, __LINE__, "tchdbvanish"); err = true; } } if(myrand(mid) == 0){ iprintf("[o]"); if(!tchdboptimize(hdb, myrand(rnum) + 1, myrand(10), myrand(10), 0)){ eprint(hdb, __LINE__, "tchdboptimize"); err = true; } } if(myrand(mid) == 0){ iprintf("[d]"); if(!tchdbdefrag(hdb, -1)){ eprint(hdb, __LINE__, "tchdbdefrag"); err = true; } } if(myrand(mid) == 0){ iprintf("[i]"); if(!tchdbiterinit(hdb)){ eprint(hdb, __LINE__, "tchdbput"); err = true; } char *kbuf; int ksiz; while((kbuf = tchdbiternext(hdb, &ksiz)) != NULL){ int vsiz; char *vbuf = tchdbget(hdb, kbuf, ksiz, &vsiz); if(vbuf){ tcfree(vbuf); } else if(tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "tchdbget"); err = true; } tcfree(kbuf); } if(tchdbecode(hdb) != TCENOREC){ eprint(hdb, __LINE__, "(validation)"); err = true; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } } return err ? "error" : NULL; } // END OF FILE tokyocabinet-1.4.48/md5.h0000644000175000017500000000704512013574446014157 0ustar mikiomikio/* Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.h is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Removed support for non-ANSI compilers; removed references to Ghostscript; clarified derivation from RFC 1321; now handles byte order either statically or dynamically. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); added conditionalization for C++ compilation from Martin Purschke . 1999-05-03 lpd Original version. */ #ifndef md5_INCLUDED # define md5_INCLUDED /* * This package supports both compile-time and run-time determination of CPU * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is * defined as non-zero, the code will be compiled to run only on big-endian * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to * run on either big- or little-endian CPUs, but will run slightly less * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. */ /* * hack to avoid conflict of application symbol names */ #define md5_init _tc_md5_init #define md5_append _tc_md5_append #define md5_finish _tc_md5_finish typedef unsigned char md5_byte_t; /* 8-bit byte */ typedef unsigned int md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ typedef struct md5_state_s { md5_word_t count[2]; /* message length in bits, lsw first */ md5_word_t abcd[4]; /* digest buffer */ md5_byte_t buf[64]; /* accumulate block */ } md5_state_t; #ifdef __cplusplus extern "C" { #endif /* Initialize the algorithm. */ void md5_init(md5_state_t *pms); /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* md5_INCLUDED */ tokyocabinet-1.4.48/tcutil.c0000644000175000017500000110642512013574446014774 0ustar mikiomikio/************************************************************************************************* * The utility API of Tokyo Cabinet * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include "tcutil.h" #include "myconf.h" #include "md5.h" /************************************************************************************************* * basic utilities *************************************************************************************************/ /* String containing the version information. */ const char *tcversion = _TC_VERSION; /* Call back function for handling a fatal error. */ void (*tcfatalfunc)(const char *message) = NULL; /* Allocate a region on memory. */ void *tcmalloc(size_t size){ assert(size > 0 && size < INT_MAX); char *p; TCMALLOC(p, size); return p; } /* Allocate a nullified region on memory. */ void *tccalloc(size_t nmemb, size_t size){ assert(nmemb > 0 && nmemb < INT_MAX && size > 0 && size < INT_MAX); char *p; TCCALLOC(p, nmemb, size); return p; } /* Re-allocate a region on memory. */ void *tcrealloc(void *ptr, size_t size){ assert(size >= 0 && size < INT_MAX); char *p; TCREALLOC(p, ptr, size); return p; } /* Duplicate a region on memory. */ void *tcmemdup(const void *ptr, size_t size){ assert(ptr && size >= 0); char *p; TCMALLOC(p, size + 1); memcpy(p, ptr, size); p[size] = '\0'; return p; } /* Duplicate a string on memory. */ char *tcstrdup(const void *str){ assert(str); int size = strlen(str); char *p; TCMALLOC(p, size + 1); memcpy(p, str, size); p[size] = '\0'; return p; } /* Free a region on memory. */ void tcfree(void *ptr){ TCFREE(ptr); } /************************************************************************************************* * extensible string *************************************************************************************************/ #define TCXSTRUNIT 12 // allocation unit size of an extensible string /* private function prototypes */ static void tcvxstrprintf(TCXSTR *xstr, const char *format, va_list ap); /* Create an extensible string object. */ TCXSTR *tcxstrnew(void){ TCXSTR *xstr; TCMALLOC(xstr, sizeof(*xstr)); TCMALLOC(xstr->ptr, TCXSTRUNIT); xstr->size = 0; xstr->asize = TCXSTRUNIT; xstr->ptr[0] = '\0'; return xstr; } /* Create an extensible string object from a character string. */ TCXSTR *tcxstrnew2(const char *str){ assert(str); TCXSTR *xstr; TCMALLOC(xstr, sizeof(*xstr)); int size = strlen(str); int asize = tclmax(size + 1, TCXSTRUNIT); TCMALLOC(xstr->ptr, asize); xstr->size = size; xstr->asize = asize; memcpy(xstr->ptr, str, size + 1); return xstr; } /* Create an extensible string object with the initial allocation size. */ TCXSTR *tcxstrnew3(int asiz){ assert(asiz >= 0); asiz = tclmax(asiz, TCXSTRUNIT); TCXSTR *xstr; TCMALLOC(xstr, sizeof(*xstr)); TCMALLOC(xstr->ptr, asiz); xstr->size = 0; xstr->asize = asiz; xstr->ptr[0] = '\0'; return xstr; } /* Copy an extensible string object. */ TCXSTR *tcxstrdup(const TCXSTR *xstr){ assert(xstr); TCXSTR *nxstr; TCMALLOC(nxstr, sizeof(*nxstr)); int asize = tclmax(xstr->size + 1, TCXSTRUNIT); TCMALLOC(nxstr->ptr, asize); nxstr->size = xstr->size; nxstr->asize = asize; memcpy(nxstr->ptr, xstr->ptr, xstr->size + 1); return nxstr; } /* Delete an extensible string object. */ void tcxstrdel(TCXSTR *xstr){ assert(xstr); TCFREE(xstr->ptr); TCFREE(xstr); } /* Concatenate a region to the end of an extensible string object. */ void tcxstrcat(TCXSTR *xstr, const void *ptr, int size){ assert(xstr && ptr && size >= 0); int nsize = xstr->size + size + 1; if(xstr->asize < nsize){ while(xstr->asize < nsize){ xstr->asize *= 2; if(xstr->asize < nsize) xstr->asize = nsize; } TCREALLOC(xstr->ptr, xstr->ptr, xstr->asize); } memcpy(xstr->ptr + xstr->size, ptr, size); xstr->size += size; xstr->ptr[xstr->size] = '\0'; } /* Concatenate a character string to the end of an extensible string object. */ void tcxstrcat2(TCXSTR *xstr, const char *str){ assert(xstr && str); int size = strlen(str); int nsize = xstr->size + size + 1; if(xstr->asize < nsize){ while(xstr->asize < nsize){ xstr->asize *= 2; if(xstr->asize < nsize) xstr->asize = nsize; } TCREALLOC(xstr->ptr, xstr->ptr, xstr->asize); } memcpy(xstr->ptr + xstr->size, str, size + 1); xstr->size += size; } /* Get the pointer of the region of an extensible string object. */ const void *tcxstrptr(const TCXSTR *xstr){ assert(xstr); return xstr->ptr; } /* Get the size of the region of an extensible string object. */ int tcxstrsize(const TCXSTR *xstr){ assert(xstr); return xstr->size; } /* Clear an extensible string object. */ void tcxstrclear(TCXSTR *xstr){ assert(xstr); xstr->size = 0; xstr->ptr[0] = '\0'; } /* Perform formatted output into an extensible string object. */ void tcxstrprintf(TCXSTR *xstr, const char *format, ...){ assert(xstr && format); va_list ap; va_start(ap, format); tcvxstrprintf(xstr, format, ap); va_end(ap); } /* Allocate a formatted string on memory. */ char *tcsprintf(const char *format, ...){ assert(format); TCXSTR *xstr = tcxstrnew(); va_list ap; va_start(ap, format); tcvxstrprintf(xstr, format, ap); va_end(ap); return tcxstrtomalloc(xstr); } /* Perform formatted output into an extensible string object. */ static void tcvxstrprintf(TCXSTR *xstr, const char *format, va_list ap){ assert(xstr && format); while(*format != '\0'){ if(*format == '%'){ char cbuf[TCNUMBUFSIZ]; cbuf[0] = '%'; int cblen = 1; int lnum = 0; format++; while(strchr("0123456789 .+-hlLz", *format) && *format != '\0' && cblen < TCNUMBUFSIZ - 1){ if(*format == 'l' || *format == 'L') lnum++; cbuf[cblen++] = *(format++); } cbuf[cblen++] = *format; cbuf[cblen] = '\0'; int tlen; char *tmp, tbuf[TCNUMBUFSIZ*4]; switch(*format){ case 's': tmp = va_arg(ap, char *); if(!tmp) tmp = "(null)"; tcxstrcat2(xstr, tmp); break; case 'd': if(lnum >= 2){ tlen = sprintf(tbuf, cbuf, va_arg(ap, long long)); } else if(lnum >= 1){ tlen = sprintf(tbuf, cbuf, va_arg(ap, long)); } else { tlen = sprintf(tbuf, cbuf, va_arg(ap, int)); } TCXSTRCAT(xstr, tbuf, tlen); break; case 'o': case 'u': case 'x': case 'X': case 'c': if(lnum >= 2){ tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned long long)); } else if(lnum >= 1){ tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned long)); } else { tlen = sprintf(tbuf, cbuf, va_arg(ap, unsigned int)); } TCXSTRCAT(xstr, tbuf, tlen); break; case 'e': case 'E': case 'f': case 'g': case 'G': if(lnum >= 1){ tlen = snprintf(tbuf, sizeof(tbuf), cbuf, va_arg(ap, long double)); } else { tlen = snprintf(tbuf, sizeof(tbuf), cbuf, va_arg(ap, double)); } if(tlen < 0 || tlen > sizeof(tbuf)){ tbuf[sizeof(tbuf)-1] = '*'; tlen = sizeof(tbuf); } TCXSTRCAT(xstr, tbuf, tlen); break; case '@': tmp = va_arg(ap, char *); if(!tmp) tmp = "(null)"; while(*tmp){ switch(*tmp){ case '&': TCXSTRCAT(xstr, "&", 5); break; case '<': TCXSTRCAT(xstr, "<", 4); break; case '>': TCXSTRCAT(xstr, ">", 4); break; case '"': TCXSTRCAT(xstr, """, 6); break; default: if(!((*tmp >= 0 && *tmp <= 0x8) || (*tmp >= 0x0e && *tmp <= 0x1f))) TCXSTRCAT(xstr, tmp, 1); break; } tmp++; } break; case '?': tmp = va_arg(ap, char *); if(!tmp) tmp = "(null)"; while(*tmp){ unsigned char c = *(unsigned char *)tmp; if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c != '\0' && strchr("_-.", c))){ TCXSTRCAT(xstr, tmp, 1); } else { tlen = sprintf(tbuf, "%%%02X", c); TCXSTRCAT(xstr, tbuf, tlen); } tmp++; } break; case 'b': if(lnum >= 2){ tlen = tcnumtostrbin(va_arg(ap, unsigned long long), tbuf, tcatoi(cbuf + 1), (cbuf[1] == '0') ? '0' : ' '); } else if(lnum >= 1){ tlen = tcnumtostrbin(va_arg(ap, unsigned long), tbuf, tcatoi(cbuf + 1), (cbuf[1] == '0') ? '0' : ' '); } else { tlen = tcnumtostrbin(va_arg(ap, unsigned int), tbuf, tcatoi(cbuf + 1), (cbuf[1] == '0') ? '0' : ' '); } TCXSTRCAT(xstr, tbuf, tlen); break; case '%': TCXSTRCAT(xstr, "%", 1); break; } } else { TCXSTRCAT(xstr, format, 1); } format++; } } /************************************************************************************************* * extensible string (for experts) *************************************************************************************************/ /* Convert an extensible string object into a usual allocated region. */ void *tcxstrtomalloc(TCXSTR *xstr){ assert(xstr); char *ptr; ptr = xstr->ptr; TCFREE(xstr); return ptr; } /* Create an extensible string object from an allocated region. */ TCXSTR *tcxstrfrommalloc(void *ptr, int size){ TCXSTR *xstr; TCMALLOC(xstr, sizeof(*xstr)); TCREALLOC(xstr->ptr, ptr, size + 1); xstr->ptr[size] = '\0'; xstr->size = size; xstr->asize = size; return xstr; } /************************************************************************************************* * array list *************************************************************************************************/ #define TCLISTUNIT 64 // allocation unit number of a list handle /* private function prototypes */ static int tclistelemcmp(const void *a, const void *b); static int tclistelemcmpci(const void *a, const void *b); /* Create a list object. */ TCLIST *tclistnew(void){ TCLIST *list; TCMALLOC(list, sizeof(*list)); list->anum = TCLISTUNIT; TCMALLOC(list->array, sizeof(list->array[0]) * list->anum); list->start = 0; list->num = 0; return list; } /* Create a list object. */ TCLIST *tclistnew2(int anum){ TCLIST *list; TCMALLOC(list, sizeof(*list)); if(anum < 1) anum = 1; list->anum = anum; TCMALLOC(list->array, sizeof(list->array[0]) * list->anum); list->start = 0; list->num = 0; return list; } /* Create a list object with initial string elements. */ TCLIST *tclistnew3(const char *str, ...){ TCLIST *list = tclistnew(); if(str){ tclistpush2(list, str); va_list ap; va_start(ap, str); const char *elem; while((elem = va_arg(ap, char *)) != NULL){ tclistpush2(list, elem); } va_end(ap); } return list; } /* Copy a list object. */ TCLIST *tclistdup(const TCLIST *list){ assert(list); int num = list->num; if(num < 1) return tclistnew(); const TCLISTDATUM *array = list->array + list->start; TCLIST *nlist; TCMALLOC(nlist, sizeof(*nlist)); TCLISTDATUM *narray; TCMALLOC(narray, sizeof(list->array[0]) * num); for(int i = 0; i < num; i++){ int size = array[i].size; TCMALLOC(narray[i].ptr, tclmax(size + 1, TCXSTRUNIT)); memcpy(narray[i].ptr, array[i].ptr, size + 1); narray[i].size = array[i].size; } nlist->anum = num; nlist->array = narray; nlist->start = 0; nlist->num = num; return nlist; } /* Delete a list object. */ void tclistdel(TCLIST *list){ assert(list); TCLISTDATUM *array = list->array; int end = list->start + list->num; for(int i = list->start; i < end; i++){ TCFREE(array[i].ptr); } TCFREE(list->array); TCFREE(list); } /* Get the number of elements of a list object. */ int tclistnum(const TCLIST *list){ assert(list); return list->num; } /* Get the pointer to the region of an element of a list object. */ const void *tclistval(const TCLIST *list, int index, int *sp){ assert(list && index >= 0 && sp); if(index >= list->num) return NULL; index += list->start; *sp = list->array[index].size; return list->array[index].ptr; } /* Get the string of an element of a list object. */ const char *tclistval2(const TCLIST *list, int index){ assert(list && index >= 0); if(index >= list->num) return NULL; index += list->start; return list->array[index].ptr; } /* Add an element at the end of a list object. */ void tclistpush(TCLIST *list, const void *ptr, int size){ assert(list && ptr && size >= 0); int index = list->start + list->num; if(index >= list->anum){ list->anum += list->num + 1; TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0])); } TCLISTDATUM *array = list->array; TCMALLOC(array[index].ptr, tclmax(size + 1, TCXSTRUNIT)); memcpy(array[index].ptr, ptr, size); array[index].ptr[size] = '\0'; array[index].size = size; list->num++; } /* Add a string element at the end of a list object. */ void tclistpush2(TCLIST *list, const char *str){ assert(list && str); int index = list->start + list->num; if(index >= list->anum){ list->anum += list->num + 1; TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0])); } int size = strlen(str); TCLISTDATUM *array = list->array; TCMALLOC(array[index].ptr, tclmax(size + 1, TCXSTRUNIT)); memcpy(array[index].ptr, str, size + 1); array[index].size = size; list->num++; } /* Remove an element of the end of a list object. */ void *tclistpop(TCLIST *list, int *sp){ assert(list && sp); if(list->num < 1) return NULL; int index = list->start + list->num - 1; list->num--; *sp = list->array[index].size; return list->array[index].ptr; } /* Remove a string element of the end of a list object. */ char *tclistpop2(TCLIST *list){ assert(list); if(list->num < 1) return NULL; int index = list->start + list->num - 1; list->num--; return list->array[index].ptr; } /* Add an element at the top of a list object. */ void tclistunshift(TCLIST *list, const void *ptr, int size){ assert(list && ptr && size >= 0); if(list->start < 1){ if(list->start + list->num >= list->anum){ list->anum += list->num + 1; TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0])); } list->start = list->anum - list->num; memmove(list->array + list->start, list->array, list->num * sizeof(list->array[0])); } int index = list->start - 1; TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT)); memcpy(list->array[index].ptr, ptr, size); list->array[index].ptr[size] = '\0'; list->array[index].size = size; list->start--; list->num++; } /* Add a string element at the top of a list object. */ void tclistunshift2(TCLIST *list, const char *str){ assert(list && str); if(list->start < 1){ if(list->start + list->num >= list->anum){ list->anum += list->num + 1; TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0])); } list->start = list->anum - list->num; memmove(list->array + list->start, list->array, list->num * sizeof(list->array[0])); } int index = list->start - 1; int size = strlen(str); TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT)); memcpy(list->array[index].ptr, str, size + 1); list->array[index].size = size; list->start--; list->num++; } /* Remove an element of the top of a list object. */ void *tclistshift(TCLIST *list, int *sp){ assert(list && sp); if(list->num < 1) return NULL; int index = list->start; list->start++; list->num--; *sp = list->array[index].size; void *rv = list->array[index].ptr; if((list->start & 0xff) == 0 && list->start > (list->num >> 1)){ memmove(list->array, list->array + list->start, list->num * sizeof(list->array[0])); list->start = 0; } return rv; } /* Remove a string element of the top of a list object. */ char *tclistshift2(TCLIST *list){ assert(list); if(list->num < 1) return NULL; int index = list->start; list->start++; list->num--; void *rv = list->array[index].ptr; if((list->start & 0xff) == 0 && list->start > (list->num >> 1)){ memmove(list->array, list->array + list->start, list->num * sizeof(list->array[0])); list->start = 0; } return rv; } /* Add an element at the specified location of a list object. */ void tclistinsert(TCLIST *list, int index, const void *ptr, int size){ assert(list && index >= 0 && ptr && size >= 0); if(index > list->num) return; index += list->start; if(list->start + list->num >= list->anum){ list->anum += list->num + 1; TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0])); } memmove(list->array + index + 1, list->array + index, sizeof(list->array[0]) * (list->start + list->num - index)); TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT)); memcpy(list->array[index].ptr, ptr, size); list->array[index].ptr[size] = '\0'; list->array[index].size = size; list->num++; } /* Add a string element at the specified location of a list object. */ void tclistinsert2(TCLIST *list, int index, const char *str){ assert(list && index >= 0 && str); if(index > list->num) return; index += list->start; if(list->start + list->num >= list->anum){ list->anum += list->num + 1; TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0])); } memmove(list->array + index + 1, list->array + index, sizeof(list->array[0]) * (list->start + list->num - index)); int size = strlen(str); TCMALLOC(list->array[index].ptr, tclmax(size + 1, TCXSTRUNIT)); memcpy(list->array[index].ptr, str, size); list->array[index].ptr[size] = '\0'; list->array[index].size = size; list->num++; } /* Remove an element at the specified location of a list object. */ void *tclistremove(TCLIST *list, int index, int *sp){ assert(list && index >= 0 && sp); if(index >= list->num) return NULL; index += list->start; void *rv = list->array[index].ptr; *sp = list->array[index].size; list->num--; memmove(list->array + index, list->array + index + 1, sizeof(list->array[0]) * (list->start + list->num - index)); return rv; } /* Remove a string element at the specified location of a list object. */ char *tclistremove2(TCLIST *list, int index){ assert(list && index >= 0); if(index >= list->num) return NULL; index += list->start; void *rv = list->array[index].ptr; list->num--; memmove(list->array + index, list->array + index + 1, sizeof(list->array[0]) * (list->start + list->num - index)); return rv; } /* Overwrite an element at the specified location of a list object. */ void tclistover(TCLIST *list, int index, const void *ptr, int size){ assert(list && index >= 0 && ptr && size >= 0); if(index >= list->num) return; index += list->start; if(size > list->array[index].size) TCREALLOC(list->array[index].ptr, list->array[index].ptr, size + 1); memcpy(list->array[index].ptr, ptr, size); list->array[index].size = size; list->array[index].ptr[size] = '\0'; } /* Overwrite a string element at the specified location of a list object. */ void tclistover2(TCLIST *list, int index, const char *str){ assert(list && index >= 0 && str); if(index >= list->num) return; index += list->start; int size = strlen(str); if(size > list->array[index].size) TCREALLOC(list->array[index].ptr, list->array[index].ptr, size + 1); memcpy(list->array[index].ptr, str, size + 1); list->array[index].size = size; } /* Sort elements of a list object in lexical order. */ void tclistsort(TCLIST *list){ assert(list); qsort(list->array + list->start, list->num, sizeof(list->array[0]), tclistelemcmp); } /* Search a list object for an element using liner search. */ int tclistlsearch(const TCLIST *list, const void *ptr, int size){ assert(list && ptr && size >= 0); int end = list->start + list->num; for(int i = list->start; i < end; i++){ if(list->array[i].size == size && !memcmp(list->array[i].ptr, ptr, size)) return i - list->start; } return -1; } /* Search a list object for an element using binary search. */ int tclistbsearch(const TCLIST *list, const void *ptr, int size){ assert(list && ptr && size >= 0); TCLISTDATUM key; key.ptr = (char *)ptr; key.size = size; TCLISTDATUM *res = bsearch(&key, list->array + list->start, list->num, sizeof(list->array[0]), tclistelemcmp); return res ? res - list->array - list->start : -1; } /* Clear a list object. */ void tclistclear(TCLIST *list){ assert(list); TCLISTDATUM *array = list->array; int end = list->start + list->num; for(int i = list->start; i < end; i++){ TCFREE(array[i].ptr); } list->start = 0; list->num = 0; } /* Serialize a list object into a byte array. */ void *tclistdump(const TCLIST *list, int *sp){ assert(list && sp); const TCLISTDATUM *array = list->array; int end = list->start + list->num; int tsiz = 0; for(int i = list->start; i < end; i++){ tsiz += array[i].size + sizeof(int); } char *buf; TCMALLOC(buf, tsiz + 1); char *wp = buf; for(int i = list->start; i < end; i++){ int step; TCSETVNUMBUF(step, wp, array[i].size); wp += step; memcpy(wp, array[i].ptr, array[i].size); wp += array[i].size; } *sp = wp - buf; return buf; } /* Create a list object from a serialized byte array. */ TCLIST *tclistload(const void *ptr, int size){ assert(ptr && size >= 0); TCLIST *list; TCMALLOC(list, sizeof(*list)); int anum = size / sizeof(int) + 1; TCLISTDATUM *array; TCMALLOC(array, sizeof(array[0]) * anum); int num = 0; const char *rp = ptr; const char *ep = (char *)ptr + size; while(rp < ep){ int step, vsiz; TCREADVNUMBUF(rp, vsiz, step); rp += step; if(num >= anum){ anum *= 2; TCREALLOC(array, array, anum * sizeof(array[0])); } TCMALLOC(array[num].ptr, tclmax(vsiz + 1, TCXSTRUNIT)); memcpy(array[num].ptr, rp, vsiz); array[num].ptr[vsiz] = '\0'; array[num].size = vsiz; num++; rp += vsiz; } list->anum = anum; list->array = array; list->start = 0; list->num = num; return list; } /* Compare two list elements in lexical order. `a' specifies the pointer to one element. `b' specifies the pointer to the other element. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tclistelemcmp(const void *a, const void *b){ assert(a && b); unsigned char *ao = (unsigned char *)((TCLISTDATUM *)a)->ptr; unsigned char *bo = (unsigned char *)((TCLISTDATUM *)b)->ptr; int size = (((TCLISTDATUM *)a)->size < ((TCLISTDATUM *)b)->size) ? ((TCLISTDATUM *)a)->size : ((TCLISTDATUM *)b)->size; for(int i = 0; i < size; i++){ if(ao[i] > bo[i]) return 1; if(ao[i] < bo[i]) return -1; } return ((TCLISTDATUM *)a)->size - ((TCLISTDATUM *)b)->size; } /* Compare two list elements in case-insensitive lexical order.. `a' specifies the pointer to one element. `b' specifies the pointer to the other element. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tclistelemcmpci(const void *a, const void *b){ assert(a && b); TCLISTDATUM *ap = (TCLISTDATUM *)a; TCLISTDATUM *bp = (TCLISTDATUM *)b; unsigned char *ao = (unsigned char *)ap->ptr; unsigned char *bo = (unsigned char *)bp->ptr; int size = (ap->size < bp->size) ? ap->size : bp->size; for(int i = 0; i < size; i++){ int ac = ao[i]; bool ab = false; if(ac >= 'A' && ac <= 'Z'){ ac += 'a' - 'A'; ab = true; } int bc = bo[i]; bool bb = false; if(bc >= 'A' && bc <= 'Z'){ bc += 'a' - 'A'; bb = true; } if(ac > bc) return 1; if(ac < bc) return -1; if(!ab && bb) return 1; if(ab && !bb) return -1; } return ap->size - bp->size; } /************************************************************************************************* * array list (for experts) *************************************************************************************************/ /* Add an allocated element at the end of a list object. */ void tclistpushmalloc(TCLIST *list, void *ptr, int size){ assert(list && ptr && size >= 0); int index = list->start + list->num; if(index >= list->anum){ list->anum += list->num + 1; TCREALLOC(list->array, list->array, list->anum * sizeof(list->array[0])); } TCLISTDATUM *array = list->array; TCREALLOC(array[index].ptr, ptr, size + 1); array[index].ptr[size] = '\0'; array[index].size = size; list->num++; } /* Sort elements of a list object in case-insensitive lexical order. */ void tclistsortci(TCLIST *list){ assert(list); qsort(list->array + list->start, list->num, sizeof(list->array[0]), tclistelemcmpci); } /* Sort elements of a list object by an arbitrary comparison function. */ void tclistsortex(TCLIST *list, int (*cmp)(const TCLISTDATUM *, const TCLISTDATUM *)){ assert(list && cmp); qsort(list->array + list->start, list->num, sizeof(list->array[0]), (int (*)(const void *, const void *))cmp); } /* Invert elements of a list object. */ void tclistinvert(TCLIST *list){ assert(list); TCLISTDATUM *top = list->array + list->start; TCLISTDATUM *bot = top + list->num - 1; while(top < bot){ TCLISTDATUM swap = *top; *top = *bot; *bot = swap; top++; bot--; } } /* Perform formatted output into a list object. */ void tclistprintf(TCLIST *list, const char *format, ...){ assert(list && format); TCXSTR *xstr = tcxstrnew(); va_list ap; va_start(ap, format); tcvxstrprintf(xstr, format, ap); va_end(ap); int size = TCXSTRSIZE(xstr); char *ptr = tcxstrtomalloc(xstr); tclistpushmalloc(list, ptr, size); } /************************************************************************************************* * hash map *************************************************************************************************/ #define TCMAPKMAXSIZ 0xfffff // maximum size of each key #define TCMAPDEFBNUM 4093 // default bucket number #define TCMAPZMMINSIZ 131072 // minimum memory size to use nullified region #define TCMAPCSUNIT 52 // small allocation unit size of map concatenation #define TCMAPCBUNIT 252 // big allocation unit size of map concatenation #define TCMAPTINYBNUM 31 // bucket number of a tiny map /* get the first hash value */ #define TCMAPHASH1(TC_res, TC_kbuf, TC_ksiz) \ do { \ const unsigned char *_TC_p = (const unsigned char *)(TC_kbuf); \ int _TC_ksiz = TC_ksiz; \ for((TC_res) = 19780211; _TC_ksiz--;){ \ (TC_res) = (TC_res) * 37 + *(_TC_p)++; \ } \ } while(false) /* get the second hash value */ #define TCMAPHASH2(TC_res, TC_kbuf, TC_ksiz) \ do { \ const unsigned char *_TC_p = (const unsigned char *)(TC_kbuf) + TC_ksiz - 1; \ int _TC_ksiz = TC_ksiz; \ for((TC_res) = 0x13579bdf; _TC_ksiz--;){ \ (TC_res) = (TC_res) * 31 + *(_TC_p)--; \ } \ } while(false) /* compare two keys */ #define TCKEYCMP(TC_abuf, TC_asiz, TC_bbuf, TC_bsiz) \ ((TC_asiz > TC_bsiz) ? 1 : (TC_asiz < TC_bsiz) ? -1 : memcmp(TC_abuf, TC_bbuf, TC_asiz)) /* Create a map object. */ TCMAP *tcmapnew(void){ return tcmapnew2(TCMAPDEFBNUM); } /* Create a map object with specifying the number of the buckets. */ TCMAP *tcmapnew2(uint32_t bnum){ if(bnum < 1) bnum = 1; TCMAP *map; TCMALLOC(map, sizeof(*map)); TCMAPREC **buckets; if(bnum >= TCMAPZMMINSIZ / sizeof(*buckets)){ buckets = tczeromap(bnum * sizeof(*buckets)); } else { TCCALLOC(buckets, bnum, sizeof(*buckets)); } map->buckets = buckets; map->first = NULL; map->last = NULL; map->cur = NULL; map->bnum = bnum; map->rnum = 0; map->msiz = 0; return map; } /* Create a map object with initial string elements. */ TCMAP *tcmapnew3(const char *str, ...){ TCMAP *map = tcmapnew2(TCMAPTINYBNUM); if(str){ va_list ap; va_start(ap, str); const char *key = str; const char *elem; while((elem = va_arg(ap, char *)) != NULL){ if(key){ tcmapput2(map, key, elem); key = NULL; } else { key = elem; } } va_end(ap); } return map; } /* Copy a map object. */ TCMAP *tcmapdup(const TCMAP *map){ assert(map); TCMAP *nmap = tcmapnew2(tclmax(tclmax(map->bnum, map->rnum), TCMAPDEFBNUM)); TCMAPREC *rec = map->first; while(rec){ char *dbuf = (char *)rec + sizeof(*rec); uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; tcmapput(nmap, dbuf, rksiz, dbuf + rksiz + TCALIGNPAD(rksiz), rec->vsiz); rec = rec->next; } return nmap; } /* Close a map object. */ void tcmapdel(TCMAP *map){ assert(map); TCMAPREC *rec = map->first; while(rec){ TCMAPREC *next = rec->next; TCFREE(rec); rec = next; } if(map->bnum >= TCMAPZMMINSIZ / sizeof(map->buckets[0])){ tczerounmap(map->buckets); } else { TCFREE(map->buckets); } TCFREE(map); } /* Store a record into a map object. */ void tcmapput(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ; uint32_t hash; TCMAPHASH1(hash, kbuf, ksiz); int bidx = hash % map->bnum; TCMAPREC *rec = map->buckets[bidx]; TCMAPREC **entp = map->buckets + bidx; TCMAPHASH2(hash, kbuf, ksiz); hash &= ~TCMAPKMAXSIZ; while(rec){ uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ; uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; if(hash > rhash){ entp = &(rec->left); rec = rec->left; } else if(hash < rhash){ entp = &(rec->right); rec = rec->right; } else { char *dbuf = (char *)rec + sizeof(*rec); int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz); if(kcmp < 0){ entp = &(rec->left); rec = rec->left; } else if(kcmp > 0){ entp = &(rec->right); rec = rec->right; } else { map->msiz += vsiz - rec->vsiz; int psiz = TCALIGNPAD(ksiz); if(vsiz > rec->vsiz){ TCMAPREC *old = rec; TCREALLOC(rec, rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); if(rec != old){ if(map->first == old) map->first = rec; if(map->last == old) map->last = rec; if(map->cur == old) map->cur = rec; *entp = rec; if(rec->prev) rec->prev->next = rec; if(rec->next) rec->next->prev = rec; dbuf = (char *)rec + sizeof(*rec); } } memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; return; } } } int psiz = TCALIGNPAD(ksiz); map->msiz += ksiz + vsiz; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz | hash; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = NULL; rec->right = NULL; rec->prev = map->last; rec->next = NULL; *entp = rec; if(!map->first) map->first = rec; if(map->last) map->last->next = rec; map->last = rec; map->rnum++; } /* Store a string record into a map object. */ void tcmapput2(TCMAP *map, const char *kstr, const char *vstr){ assert(map && kstr && vstr); tcmapput(map, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Store a new record into a map object. */ bool tcmapputkeep(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ; uint32_t hash; TCMAPHASH1(hash, kbuf, ksiz); int bidx = hash % map->bnum; TCMAPREC *rec = map->buckets[bidx]; TCMAPREC **entp = map->buckets + bidx; TCMAPHASH2(hash, kbuf, ksiz); hash &= ~TCMAPKMAXSIZ; while(rec){ uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ; uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; if(hash > rhash){ entp = &(rec->left); rec = rec->left; } else if(hash < rhash){ entp = &(rec->right); rec = rec->right; } else { char *dbuf = (char *)rec + sizeof(*rec); int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz); if(kcmp < 0){ entp = &(rec->left); rec = rec->left; } else if(kcmp > 0){ entp = &(rec->right); rec = rec->right; } else { return false; } } } int psiz = TCALIGNPAD(ksiz); map->msiz += ksiz + vsiz; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz | hash; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = NULL; rec->right = NULL; rec->prev = map->last; rec->next = NULL; *entp = rec; if(!map->first) map->first = rec; if(map->last) map->last->next = rec; map->last = rec; map->rnum++; return true; } /* Store a new string record into a map object. */ bool tcmapputkeep2(TCMAP *map, const char *kstr, const char *vstr){ assert(map && kstr && vstr); return tcmapputkeep(map, kstr, strlen(kstr), vstr, strlen(vstr)); } /* 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){ assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ; uint32_t hash; TCMAPHASH1(hash, kbuf, ksiz); int bidx = hash % map->bnum; TCMAPREC *rec = map->buckets[bidx]; TCMAPREC **entp = map->buckets + bidx; TCMAPHASH2(hash, kbuf, ksiz); hash &= ~TCMAPKMAXSIZ; while(rec){ uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ; uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; if(hash > rhash){ entp = &(rec->left); rec = rec->left; } else if(hash < rhash){ entp = &(rec->right); rec = rec->right; } else { char *dbuf = (char *)rec + sizeof(*rec); int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz); if(kcmp < 0){ entp = &(rec->left); rec = rec->left; } else if(kcmp > 0){ entp = &(rec->right); rec = rec->right; } else { map->msiz += vsiz; int psiz = TCALIGNPAD(ksiz); int asiz = sizeof(*rec) + ksiz + psiz + rec->vsiz + vsiz + 1; int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT; asiz = (asiz - 1) + unit - (asiz - 1) % unit; TCMAPREC *old = rec; TCREALLOC(rec, rec, asiz); if(rec != old){ if(map->first == old) map->first = rec; if(map->last == old) map->last = rec; if(map->cur == old) map->cur = rec; *entp = rec; if(rec->prev) rec->prev->next = rec; if(rec->next) rec->next->prev = rec; dbuf = (char *)rec + sizeof(*rec); } memcpy(dbuf + ksiz + psiz + rec->vsiz, vbuf, vsiz); rec->vsiz += vsiz; dbuf[ksiz+psiz+rec->vsiz] = '\0'; return; } } } int psiz = TCALIGNPAD(ksiz); int asiz = sizeof(*rec) + ksiz + psiz + vsiz + 1; int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT; asiz = (asiz - 1) + unit - (asiz - 1) % unit; map->msiz += ksiz + vsiz; TCMALLOC(rec, asiz); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz | hash; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = NULL; rec->right = NULL; rec->prev = map->last; rec->next = NULL; *entp = rec; if(!map->first) map->first = rec; if(map->last) map->last->next = rec; map->last = rec; map->rnum++; } /* 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){ assert(map && kstr && vstr); tcmapputcat(map, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Remove a record of a map object. */ bool tcmapout(TCMAP *map, const void *kbuf, int ksiz){ assert(map && kbuf && ksiz >= 0); if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ; uint32_t hash; TCMAPHASH1(hash, kbuf, ksiz); int bidx = hash % map->bnum; TCMAPREC *rec = map->buckets[bidx]; TCMAPREC **entp = map->buckets + bidx; TCMAPHASH2(hash, kbuf, ksiz); hash &= ~TCMAPKMAXSIZ; while(rec){ uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ; uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; if(hash > rhash){ entp = &(rec->left); rec = rec->left; } else if(hash < rhash){ entp = &(rec->right); rec = rec->right; } else { char *dbuf = (char *)rec + sizeof(*rec); int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz); if(kcmp < 0){ entp = &(rec->left); rec = rec->left; } else if(kcmp > 0){ entp = &(rec->right); rec = rec->right; } else { map->rnum--; map->msiz -= rksiz + rec->vsiz; if(rec->prev) rec->prev->next = rec->next; if(rec->next) rec->next->prev = rec->prev; if(rec == map->first) map->first = rec->next; if(rec == map->last) map->last = rec->prev; if(rec == map->cur) map->cur = rec->next; if(rec->left && !rec->right){ *entp = rec->left; } else if(!rec->left && rec->right){ *entp = rec->right; } else if(!rec->left){ *entp = NULL; } else { *entp = rec->left; TCMAPREC *tmp = *entp; while(tmp->right){ tmp = tmp->right; } tmp->right = rec->right; } TCFREE(rec); return true; } } } return false; } /* Remove a string record of a map object. */ bool tcmapout2(TCMAP *map, const char *kstr){ assert(map && kstr); return tcmapout(map, kstr, strlen(kstr)); } /* Retrieve a record in a map object. */ const void *tcmapget(const TCMAP *map, const void *kbuf, int ksiz, int *sp){ assert(map && kbuf && ksiz >= 0 && sp); if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ; uint32_t hash; TCMAPHASH1(hash, kbuf, ksiz); TCMAPREC *rec = map->buckets[hash%map->bnum]; TCMAPHASH2(hash, kbuf, ksiz); hash &= ~TCMAPKMAXSIZ; while(rec){ uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ; uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; if(hash > rhash){ rec = rec->left; } else if(hash < rhash){ rec = rec->right; } else { char *dbuf = (char *)rec + sizeof(*rec); int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz); if(kcmp < 0){ rec = rec->left; } else if(kcmp > 0){ rec = rec->right; } else { *sp = rec->vsiz; return dbuf + rksiz + TCALIGNPAD(rksiz); } } } return NULL; } /* Retrieve a string record in a map object. */ const char *tcmapget2(const TCMAP *map, const char *kstr){ assert(map && kstr); int ksiz = strlen(kstr); if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ; uint32_t hash; TCMAPHASH1(hash, kstr, ksiz); TCMAPREC *rec = map->buckets[hash%map->bnum]; TCMAPHASH2(hash, kstr, ksiz); hash &= ~TCMAPKMAXSIZ; while(rec){ uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ; uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; if(hash > rhash){ rec = rec->left; } else if(hash < rhash){ rec = rec->right; } else { char *dbuf = (char *)rec + sizeof(*rec); int kcmp = TCKEYCMP(kstr, ksiz, dbuf, rksiz); if(kcmp < 0){ rec = rec->left; } else if(kcmp > 0){ rec = rec->right; } else { return dbuf + rksiz + TCALIGNPAD(rksiz); } } } return NULL; } /* Move a record to the edge of a map object. */ bool tcmapmove(TCMAP *map, const void *kbuf, int ksiz, bool head){ assert(map && kbuf && ksiz >= 0); if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ; uint32_t hash; TCMAPHASH1(hash, kbuf, ksiz); TCMAPREC *rec = map->buckets[hash%map->bnum]; TCMAPHASH2(hash, kbuf, ksiz); hash &= ~TCMAPKMAXSIZ; while(rec){ uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ; uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; if(hash > rhash){ rec = rec->left; } else if(hash < rhash){ rec = rec->right; } else { char *dbuf = (char *)rec + sizeof(*rec); int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz); if(kcmp < 0){ rec = rec->left; } else if(kcmp > 0){ rec = rec->right; } else { if(head){ if(map->first == rec) return true; if(map->last == rec) map->last = rec->prev; if(rec->prev) rec->prev->next = rec->next; if(rec->next) rec->next->prev = rec->prev; rec->prev = NULL; rec->next = map->first; map->first->prev = rec; map->first = rec; } else { if(map->last == rec) return true; if(map->first == rec) map->first = rec->next; if(rec->prev) rec->prev->next = rec->next; if(rec->next) rec->next->prev = rec->prev; rec->prev = map->last; rec->next = NULL; map->last->next = rec; map->last = rec; } return true; } } } return false; } /* Move a string record to the edge of a map object. */ bool tcmapmove2(TCMAP *map, const char *kstr, bool head){ assert(map && kstr); return tcmapmove(map, kstr, strlen(kstr), head); } /* Initialize the iterator of a map object. */ void tcmapiterinit(TCMAP *map){ assert(map); map->cur = map->first; } /* Get the next key of the iterator of a map object. */ const void *tcmapiternext(TCMAP *map, int *sp){ assert(map && sp); TCMAPREC *rec; if(!map->cur) return NULL; rec = map->cur; map->cur = rec->next; *sp = rec->ksiz & TCMAPKMAXSIZ; return (char *)rec + sizeof(*rec); } /* Get the next key string of the iterator of a map object. */ const char *tcmapiternext2(TCMAP *map){ assert(map); TCMAPREC *rec; if(!map->cur) return NULL; rec = map->cur; map->cur = rec->next; return (char *)rec + sizeof(*rec); } /* Get the number of records stored in a map object. */ uint64_t tcmaprnum(const TCMAP *map){ assert(map); return map->rnum; } /* Get the total size of memory used in a map object. */ uint64_t tcmapmsiz(const TCMAP *map){ assert(map); return map->msiz + map->rnum * (sizeof(*map->first) + sizeof(tcgeneric_t)) + map->bnum * sizeof(void *); } /* Create a list object containing all keys in a map object. */ TCLIST *tcmapkeys(const TCMAP *map){ assert(map); TCLIST *list = tclistnew2(map->rnum); TCMAPREC *rec = map->first; while(rec){ char *dbuf = (char *)rec + sizeof(*rec); TCLISTPUSH(list, dbuf, rec->ksiz & TCMAPKMAXSIZ); rec = rec->next; } return list; } /* Create a list object containing all values in a map object. */ TCLIST *tcmapvals(const TCMAP *map){ assert(map); TCLIST *list = tclistnew2(map->rnum); TCMAPREC *rec = map->first; while(rec){ char *dbuf = (char *)rec + sizeof(*rec); uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; TCLISTPUSH(list, dbuf + rksiz + TCALIGNPAD(rksiz), rec->vsiz); rec = rec->next; } return list; } /* Add an integer to a record in a map object. */ int tcmapaddint(TCMAP *map, const void *kbuf, int ksiz, int num){ assert(map && kbuf && ksiz >= 0); if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ; uint32_t hash; TCMAPHASH1(hash, kbuf, ksiz); int bidx = hash % map->bnum; TCMAPREC *rec = map->buckets[bidx]; TCMAPREC **entp = map->buckets + bidx; TCMAPHASH2(hash, kbuf, ksiz); hash &= ~TCMAPKMAXSIZ; while(rec){ uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ; uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; if(hash > rhash){ entp = &(rec->left); rec = rec->left; } else if(hash < rhash){ entp = &(rec->right); rec = rec->right; } else { char *dbuf = (char *)rec + sizeof(*rec); int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz); if(kcmp < 0){ entp = &(rec->left); rec = rec->left; } else if(kcmp > 0){ entp = &(rec->right); rec = rec->right; } else { if(rec->vsiz != sizeof(num)) return INT_MIN; int *resp = (int *)(dbuf + ksiz + TCALIGNPAD(ksiz)); return *resp += num; } } } int psiz = TCALIGNPAD(ksiz); TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz | hash; memcpy(dbuf + ksiz + psiz, &num, sizeof(num)); dbuf[ksiz+psiz+sizeof(num)] = '\0'; rec->vsiz = sizeof(num); rec->left = NULL; rec->right = NULL; rec->prev = map->last; rec->next = NULL; *entp = rec; if(!map->first) map->first = rec; if(map->last) map->last->next = rec; map->last = rec; map->rnum++; return num; } /* Add a real number to a record in a map object. */ double tcmapadddouble(TCMAP *map, const void *kbuf, int ksiz, double num){ assert(map && kbuf && ksiz >= 0); if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ; uint32_t hash; TCMAPHASH1(hash, kbuf, ksiz); int bidx = hash % map->bnum; TCMAPREC *rec = map->buckets[bidx]; TCMAPREC **entp = map->buckets + bidx; TCMAPHASH2(hash, kbuf, ksiz); hash &= ~TCMAPKMAXSIZ; while(rec){ uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ; uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; if(hash > rhash){ entp = &(rec->left); rec = rec->left; } else if(hash < rhash){ entp = &(rec->right); rec = rec->right; } else { char *dbuf = (char *)rec + sizeof(*rec); int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz); if(kcmp < 0){ entp = &(rec->left); rec = rec->left; } else if(kcmp > 0){ entp = &(rec->right); rec = rec->right; } else { if(rec->vsiz != sizeof(num)) return nan(""); double *resp = (double *)(dbuf + ksiz + TCALIGNPAD(ksiz)); return *resp += num; } } } int psiz = TCALIGNPAD(ksiz); TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz | hash; memcpy(dbuf + ksiz + psiz, &num, sizeof(num)); dbuf[ksiz+psiz+sizeof(num)] = '\0'; rec->vsiz = sizeof(num); rec->left = NULL; rec->right = NULL; rec->prev = map->last; rec->next = NULL; *entp = rec; if(!map->first) map->first = rec; if(map->last) map->last->next = rec; map->last = rec; map->rnum++; return num; } /* Clear a map object. */ void tcmapclear(TCMAP *map){ assert(map); TCMAPREC *rec = map->first; while(rec){ TCMAPREC *next = rec->next; TCFREE(rec); rec = next; } TCMAPREC **buckets = map->buckets; int bnum = map->bnum; for(int i = 0; i < bnum; i++){ buckets[i] = NULL; } map->first = NULL; map->last = NULL; map->cur = NULL; map->rnum = 0; map->msiz = 0; } /* Remove front records of a map object. */ void tcmapcutfront(TCMAP *map, int num){ assert(map && num >= 0); tcmapiterinit(map); while(num-- > 0){ int ksiz; const char *kbuf = tcmapiternext(map, &ksiz); if(!kbuf) break; tcmapout(map, kbuf, ksiz); } } /* Serialize a map object into a byte array. */ void *tcmapdump(const TCMAP *map, int *sp){ assert(map && sp); int tsiz = 0; TCMAPREC *rec = map->first; while(rec){ tsiz += (rec->ksiz & TCMAPKMAXSIZ) + rec->vsiz + sizeof(int) * 2; rec = rec->next; } char *buf; TCMALLOC(buf, tsiz + 1); char *wp = buf; rec = map->first; while(rec){ const char *kbuf = (char *)rec + sizeof(*rec); int ksiz = rec->ksiz & TCMAPKMAXSIZ; const char *vbuf = kbuf + ksiz + TCALIGNPAD(ksiz); int vsiz = rec->vsiz; int step; TCSETVNUMBUF(step, wp, ksiz); wp += step; memcpy(wp, kbuf, ksiz); wp += ksiz; TCSETVNUMBUF(step, wp, vsiz); wp += step; memcpy(wp, vbuf, vsiz); wp += vsiz; rec = rec->next; } *sp = wp - buf; return buf; } /* Create a map object from a serialized byte array. */ TCMAP *tcmapload(const void *ptr, int size){ assert(ptr && size >= 0); TCMAP *map = tcmapnew2(tclmin(size / 6 + 1, TCMAPDEFBNUM)); const char *rp = ptr; const char *ep = (char *)ptr + size; while(rp < ep){ int step, ksiz, vsiz; TCREADVNUMBUF(rp, ksiz, step); rp += step; const char *kbuf = rp; rp += ksiz; TCREADVNUMBUF(rp, vsiz, step); rp += step; tcmapputkeep(map, kbuf, ksiz, rp, vsiz); rp += vsiz; } return map; } /************************************************************************************************* * hash map (for experts) *************************************************************************************************/ /* Store a record and make it semivolatile in a map object. */ void tcmapput3(TCMAP *map, const void *kbuf, int ksiz, const char *vbuf, int vsiz){ assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ; uint32_t hash; TCMAPHASH1(hash, kbuf, ksiz); int bidx = hash % map->bnum; TCMAPREC *rec = map->buckets[bidx]; TCMAPREC **entp = map->buckets + bidx; TCMAPHASH2(hash, kbuf, ksiz); hash &= ~TCMAPKMAXSIZ; while(rec){ uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ; uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; if(hash > rhash){ entp = &(rec->left); rec = rec->left; } else if(hash < rhash){ entp = &(rec->right); rec = rec->right; } else { char *dbuf = (char *)rec + sizeof(*rec); int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz); if(kcmp < 0){ entp = &(rec->left); rec = rec->left; } else if(kcmp > 0){ entp = &(rec->right); rec = rec->right; } else { map->msiz += vsiz - rec->vsiz; int psiz = TCALIGNPAD(ksiz); if(vsiz > rec->vsiz){ TCMAPREC *old = rec; TCREALLOC(rec, rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); if(rec != old){ if(map->first == old) map->first = rec; if(map->last == old) map->last = rec; if(map->cur == old) map->cur = rec; *entp = rec; if(rec->prev) rec->prev->next = rec; if(rec->next) rec->next->prev = rec; dbuf = (char *)rec + sizeof(*rec); } } memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; if(map->last != rec){ if(map->first == rec) map->first = rec->next; if(rec->prev) rec->prev->next = rec->next; if(rec->next) rec->next->prev = rec->prev; rec->prev = map->last; rec->next = NULL; map->last->next = rec; map->last = rec; } return; } } } int psiz = TCALIGNPAD(ksiz); map->msiz += ksiz + vsiz; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz | hash; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = NULL; rec->right = NULL; rec->prev = map->last; rec->next = NULL; *entp = rec; if(!map->first) map->first = rec; if(map->last) map->last->next = rec; map->last = rec; map->rnum++; } /* Store a record of the value of two regions into a map object. */ void tcmapput4(TCMAP *map, const void *kbuf, int ksiz, const void *fvbuf, int fvsiz, const void *lvbuf, int lvsiz){ assert(map && kbuf && ksiz >= 0 && fvbuf && fvsiz >= 0 && lvbuf && lvsiz >= 0); if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ; uint32_t hash; TCMAPHASH1(hash, kbuf, ksiz); int bidx = hash % map->bnum; TCMAPREC *rec = map->buckets[bidx]; TCMAPREC **entp = map->buckets + bidx; TCMAPHASH2(hash, kbuf, ksiz); hash &= ~TCMAPKMAXSIZ; while(rec){ uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ; uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; if(hash > rhash){ entp = &(rec->left); rec = rec->left; } else if(hash < rhash){ entp = &(rec->right); rec = rec->right; } else { char *dbuf = (char *)rec + sizeof(*rec); int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz); if(kcmp < 0){ entp = &(rec->left); rec = rec->left; } else if(kcmp > 0){ entp = &(rec->right); rec = rec->right; } else { int vsiz = fvsiz + lvsiz; map->msiz += vsiz - rec->vsiz; int psiz = TCALIGNPAD(ksiz); ksiz += psiz; if(vsiz > rec->vsiz){ TCMAPREC *old = rec; TCREALLOC(rec, rec, sizeof(*rec) + ksiz + vsiz + 1); if(rec != old){ if(map->first == old) map->first = rec; if(map->last == old) map->last = rec; if(map->cur == old) map->cur = rec; *entp = rec; if(rec->prev) rec->prev->next = rec; if(rec->next) rec->next->prev = rec; dbuf = (char *)rec + sizeof(*rec); } } memcpy(dbuf + ksiz, fvbuf, fvsiz); memcpy(dbuf + ksiz + fvsiz, lvbuf, lvsiz); dbuf[ksiz+vsiz] = '\0'; rec->vsiz = vsiz; return; } } } int vsiz = fvsiz + lvsiz; int psiz = TCALIGNPAD(ksiz); map->msiz += ksiz + vsiz; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz | hash; ksiz += psiz; memcpy(dbuf + ksiz, fvbuf, fvsiz); memcpy(dbuf + ksiz + fvsiz, lvbuf, lvsiz); dbuf[ksiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = NULL; rec->right = NULL; rec->prev = map->last; rec->next = NULL; *entp = rec; if(!map->first) map->first = rec; if(map->last) map->last->next = rec; map->last = rec; map->rnum++; } /* Concatenate a value at the existing record and make it semivolatile in a map object. */ void tcmapputcat3(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(map && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ; uint32_t hash; TCMAPHASH1(hash, kbuf, ksiz); int bidx = hash % map->bnum; TCMAPREC *rec = map->buckets[bidx]; TCMAPREC **entp = map->buckets + bidx; TCMAPHASH2(hash, kbuf, ksiz); hash &= ~TCMAPKMAXSIZ; while(rec){ uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ; uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; if(hash > rhash){ entp = &(rec->left); rec = rec->left; } else if(hash < rhash){ entp = &(rec->right); rec = rec->right; } else { char *dbuf = (char *)rec + sizeof(*rec); int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz); if(kcmp < 0){ entp = &(rec->left); rec = rec->left; } else if(kcmp > 0){ entp = &(rec->right); rec = rec->right; } else { map->msiz += vsiz; int psiz = TCALIGNPAD(ksiz); int asiz = sizeof(*rec) + ksiz + psiz + rec->vsiz + vsiz + 1; int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT; asiz = (asiz - 1) + unit - (asiz - 1) % unit; TCMAPREC *old = rec; TCREALLOC(rec, rec, asiz); if(rec != old){ if(map->first == old) map->first = rec; if(map->last == old) map->last = rec; if(map->cur == old) map->cur = rec; *entp = rec; if(rec->prev) rec->prev->next = rec; if(rec->next) rec->next->prev = rec; dbuf = (char *)rec + sizeof(*rec); } memcpy(dbuf + ksiz + psiz + rec->vsiz, vbuf, vsiz); rec->vsiz += vsiz; dbuf[ksiz+psiz+rec->vsiz] = '\0'; if(map->last != rec){ if(map->first == rec) map->first = rec->next; if(rec->prev) rec->prev->next = rec->next; if(rec->next) rec->next->prev = rec->prev; rec->prev = map->last; rec->next = NULL; map->last->next = rec; map->last = rec; } return; } } } int psiz = TCALIGNPAD(ksiz); int asiz = sizeof(*rec) + ksiz + psiz + vsiz + 1; int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT; asiz = (asiz - 1) + unit - (asiz - 1) % unit; map->msiz += ksiz + vsiz; TCMALLOC(rec, asiz); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz | hash; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = NULL; rec->right = NULL; rec->prev = map->last; rec->next = NULL; *entp = rec; if(!map->first) map->first = rec; if(map->last) map->last->next = rec; map->last = rec; map->rnum++; } /* Store a record into a map object with a duplication handler. */ bool tcmapputproc(TCMAP *map, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op){ assert(map && kbuf && ksiz >= 0 && proc); if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ; uint32_t hash; TCMAPHASH1(hash, kbuf, ksiz); int bidx = hash % map->bnum; TCMAPREC *rec = map->buckets[bidx]; TCMAPREC **entp = map->buckets + bidx; TCMAPHASH2(hash, kbuf, ksiz); hash &= ~TCMAPKMAXSIZ; while(rec){ uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ; uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; if(hash > rhash){ entp = &(rec->left); rec = rec->left; } else if(hash < rhash){ entp = &(rec->right); rec = rec->right; } else { char *dbuf = (char *)rec + sizeof(*rec); int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz); if(kcmp < 0){ entp = &(rec->left); rec = rec->left; } else if(kcmp > 0){ entp = &(rec->right); rec = rec->right; } else { int psiz = TCALIGNPAD(ksiz); int nvsiz; char *nvbuf = proc(dbuf + ksiz + psiz, rec->vsiz, &nvsiz, op); if(nvbuf == (void *)-1){ map->rnum--; map->msiz -= rksiz + rec->vsiz; if(rec->prev) rec->prev->next = rec->next; if(rec->next) rec->next->prev = rec->prev; if(rec == map->first) map->first = rec->next; if(rec == map->last) map->last = rec->prev; if(rec == map->cur) map->cur = rec->next; if(rec->left && !rec->right){ *entp = rec->left; } else if(!rec->left && rec->right){ *entp = rec->right; } else if(!rec->left && !rec->left){ *entp = NULL; } else { *entp = rec->left; TCMAPREC *tmp = *entp; while(tmp->right){ tmp = tmp->right; } tmp->right = rec->right; } TCFREE(rec); return true; } if(!nvbuf) return false; map->msiz += nvsiz - rec->vsiz; if(nvsiz > rec->vsiz){ TCMAPREC *old = rec; TCREALLOC(rec, rec, sizeof(*rec) + ksiz + psiz + nvsiz + 1); if(rec != old){ if(map->first == old) map->first = rec; if(map->last == old) map->last = rec; if(map->cur == old) map->cur = rec; *entp = rec; if(rec->prev) rec->prev->next = rec; if(rec->next) rec->next->prev = rec; dbuf = (char *)rec + sizeof(*rec); } } memcpy(dbuf + ksiz + psiz, nvbuf, nvsiz); dbuf[ksiz+psiz+nvsiz] = '\0'; rec->vsiz = nvsiz; TCFREE(nvbuf); return true; } } } if(!vbuf) return false; int psiz = TCALIGNPAD(ksiz); int asiz = sizeof(*rec) + ksiz + psiz + vsiz + 1; int unit = (asiz <= TCMAPCSUNIT) ? TCMAPCSUNIT : TCMAPCBUNIT; asiz = (asiz - 1) + unit - (asiz - 1) % unit; map->msiz += ksiz + vsiz; TCMALLOC(rec, asiz); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz | hash; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = NULL; rec->right = NULL; rec->prev = map->last; rec->next = NULL; *entp = rec; if(!map->first) map->first = rec; if(map->last) map->last->next = rec; map->last = rec; map->rnum++; return true; } /* Retrieve a semivolatile record in a map object. */ const void *tcmapget3(TCMAP *map, const void *kbuf, int ksiz, int *sp){ assert(map && kbuf && ksiz >= 0 && sp); if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ; uint32_t hash; TCMAPHASH1(hash, kbuf, ksiz); TCMAPREC *rec = map->buckets[hash%map->bnum]; TCMAPHASH2(hash, kbuf, ksiz); hash &= ~TCMAPKMAXSIZ; while(rec){ uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ; uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; if(hash > rhash){ rec = rec->left; } else if(hash < rhash){ rec = rec->right; } else { char *dbuf = (char *)rec + sizeof(*rec); int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz); if(kcmp < 0){ rec = rec->left; } else if(kcmp > 0){ rec = rec->right; } else { if(map->last != rec){ if(map->first == rec) map->first = rec->next; if(rec->prev) rec->prev->next = rec->next; if(rec->next) rec->next->prev = rec->prev; rec->prev = map->last; rec->next = NULL; map->last->next = rec; map->last = rec; } *sp = rec->vsiz; return dbuf + rksiz + TCALIGNPAD(rksiz); } } } return NULL; } /* Retrieve a string record in a map object with specifying the default value string. */ const char *tcmapget4(TCMAP *map, const char *kstr, const char *dstr){ assert(map && kstr && dstr); int vsiz; const char *vbuf = tcmapget(map, kstr, strlen(kstr), &vsiz); return vbuf ? vbuf : dstr; } /* Initialize the iterator of a map object at the record corresponding a key. */ void tcmapiterinit2(TCMAP *map, const void *kbuf, int ksiz){ assert(map && kbuf && ksiz >= 0); if(ksiz > TCMAPKMAXSIZ) ksiz = TCMAPKMAXSIZ; uint32_t hash; TCMAPHASH1(hash, kbuf, ksiz); TCMAPREC *rec = map->buckets[hash%map->bnum]; TCMAPHASH2(hash, kbuf, ksiz); hash &= ~TCMAPKMAXSIZ; while(rec){ uint32_t rhash = rec->ksiz & ~TCMAPKMAXSIZ; uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; if(hash > rhash){ rec = rec->left; } else if(hash < rhash){ rec = rec->right; } else { char *dbuf = (char *)rec + sizeof(*rec); int kcmp = TCKEYCMP(kbuf, ksiz, dbuf, rksiz); if(kcmp < 0){ rec = rec->left; } else if(kcmp > 0){ rec = rec->right; } else { map->cur = rec; return; } } } } /* Initialize the iterator of a map object at the record corresponding a key string. */ void tcmapiterinit3(TCMAP *map, const char *kstr){ assert(map && kstr); tcmapiterinit2(map, kstr, strlen(kstr)); } /* Get the value bound to the key fetched from the iterator of a map object. */ const void *tcmapiterval(const void *kbuf, int *sp){ assert(kbuf && sp); TCMAPREC *rec = (TCMAPREC *)((char *)kbuf - sizeof(*rec)); uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; *sp = rec->vsiz; return (char *)kbuf + rksiz + TCALIGNPAD(rksiz); } /* Get the value string bound to the key fetched from the iterator of a map object. */ const char *tcmapiterval2(const char *kstr){ assert(kstr); TCMAPREC *rec = (TCMAPREC *)(kstr - sizeof(*rec)); uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; return kstr + rksiz + TCALIGNPAD(rksiz); } /* Create an array of strings of all keys in a map object. */ const char **tcmapkeys2(const TCMAP *map, int *np){ assert(map && np); const char **ary; TCMALLOC(ary, sizeof(*ary) * map->rnum + 1); int anum = 0; TCMAPREC *rec = map->first; while(rec){ ary[(anum++)] = (char *)rec + sizeof(*rec); rec = rec->next; } *np = anum; return ary; } /* Create an array of strings of all values in a map object. */ const char **tcmapvals2(const TCMAP *map, int *np){ assert(map && np); const char **ary; TCMALLOC(ary, sizeof(*ary) * map->rnum + 1); int anum = 0; TCMAPREC *rec = map->first; while(rec){ uint32_t rksiz = rec->ksiz & TCMAPKMAXSIZ; ary[(anum++)] = (char *)rec + sizeof(*rec) + rksiz + TCALIGNPAD(rksiz); rec = rec->next; } *np = anum; return ary; } /* Extract a map record from a serialized byte array. */ void *tcmaploadone(const void *ptr, int size, const void *kbuf, int ksiz, int *sp){ assert(ptr && size >= 0 && kbuf && ksiz >= 0 && sp); const char *rp = ptr; const char *ep = (char *)ptr + size; while(rp < ep){ int step, rsiz; TCREADVNUMBUF(rp, rsiz, step); rp += step; if(rsiz == ksiz && !memcmp(kbuf, rp, rsiz)){ rp += rsiz; TCREADVNUMBUF(rp, rsiz, step); rp += step; *sp = rsiz; char *rv; TCMEMDUP(rv, rp, rsiz); return rv; } rp += rsiz; TCREADVNUMBUF(rp, rsiz, step); rp += step; rp += rsiz; } return NULL; } /* Perform formatted output into a map object. */ void tcmapprintf(TCMAP *map, const char *kstr, const char *format, ...){ assert(map && kstr && format); TCXSTR *xstr = tcxstrnew(); va_list ap; va_start(ap, format); tcvxstrprintf(xstr, format, ap); va_end(ap); tcmapput(map, kstr, strlen(kstr), TCXSTRPTR(xstr), TCXSTRSIZE(xstr)); tcxstrdel(xstr); } /************************************************************************************************* * ordered tree *************************************************************************************************/ #define TREESTACKNUM 2048 // capacity of the stack of ordered tree #define TCTREECSUNIT 52 // small allocation unit size of map concatenation #define TCTREECBUNIT 252 // big allocation unit size of map concatenation /* private function prototypes */ static TCTREEREC* tctreesplay(TCTREE *tree, const void *kbuf, int ksiz); /* Create a tree object. */ TCTREE *tctreenew(void){ return tctreenew2(tccmplexical, NULL); } /* Create a tree object with specifying the custom comparison function. */ TCTREE *tctreenew2(TCCMP cmp, void *cmpop){ assert(cmp); TCTREE *tree; TCMALLOC(tree, sizeof(*tree)); tree->root = NULL; tree->cur = NULL; tree->rnum = 0; tree->msiz = 0; tree->cmp = cmp; tree->cmpop = cmpop; return tree; } /* Copy a tree object. */ TCTREE *tctreedup(const TCTREE *tree){ assert(tree); TCTREE *ntree = tctreenew2(tree->cmp, tree->cmpop); if(tree->root){ TCTREEREC *histbuf[TREESTACKNUM]; TCTREEREC **history = histbuf; int hnum = 0; history[hnum++] = tree->root; while(hnum > 0){ TCTREEREC *rec = history[--hnum]; if(hnum >= TREESTACKNUM - 2 && history == histbuf){ TCMALLOC(history, sizeof(*history) * tree->rnum); memcpy(history, histbuf, sizeof(*history) * hnum); } if(rec->left) history[hnum++] = rec->left; if(rec->right) history[hnum++] = rec->right; char *dbuf = (char *)rec + sizeof(*rec); tctreeput(ntree, dbuf, rec->ksiz, dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz), rec->vsiz); } if(history != histbuf) TCFREE(history); } return ntree; } /* Delete a tree object. */ void tctreedel(TCTREE *tree){ assert(tree); if(tree->root){ TCTREEREC *histbuf[TREESTACKNUM]; TCTREEREC **history = histbuf; int hnum = 0; history[hnum++] = tree->root; while(hnum > 0){ TCTREEREC *rec = history[--hnum]; if(hnum >= TREESTACKNUM - 2 && history == histbuf){ TCMALLOC(history, sizeof(*history) * tree->rnum); memcpy(history, histbuf, sizeof(*history) * hnum); } if(rec->left) history[hnum++] = rec->left; if(rec->right) history[hnum++] = rec->right; TCFREE(rec); } if(history != histbuf) TCFREE(history); } TCFREE(tree); } /* Store a record into a tree object. */ void tctreeput(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(tree && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); TCTREEREC *top = tctreesplay(tree, kbuf, ksiz); if(!top){ int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = NULL; rec->right = NULL; tree->root = rec; tree->rnum = 1; tree->msiz = ksiz + vsiz; return; } char *dbuf = (char *)top + sizeof(*top); int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop); if(cv < 0){ int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = top->left; rec->right = top; top->left = NULL; tree->rnum++; tree->msiz += ksiz + vsiz; tree->root = rec; } else if(cv > 0){ int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = top; rec->right = top->right; top->right = NULL; tree->rnum++; tree->msiz += ksiz + vsiz; tree->root = rec; } else { tree->msiz += vsiz - top->vsiz; int psiz = TCALIGNPAD(ksiz); if(vsiz > top->vsiz){ TCTREEREC *old = top; TCREALLOC(top, top, sizeof(*top) + ksiz + psiz + vsiz + 1); if(top != old){ if(tree->cur == old) tree->cur = top; dbuf = (char *)top + sizeof(*top); } } memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; top->vsiz = vsiz; tree->root = top; } } /* Store a string record into a tree object. */ void tctreeput2(TCTREE *tree, const char *kstr, const char *vstr){ assert(tree && kstr && vstr); tctreeput(tree, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Store a new record into a tree object. */ bool tctreeputkeep(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(tree && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); TCTREEREC *top = tctreesplay(tree, kbuf, ksiz); if(!top){ int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = NULL; rec->right = NULL; tree->root = rec; tree->rnum = 1; tree->msiz = ksiz + vsiz; return true; } char *dbuf = (char *)top + sizeof(*top); int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop); if(cv < 0){ int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = top->left; rec->right = top; top->left = NULL; tree->rnum++; tree->msiz += ksiz + vsiz; tree->root = rec; } else if(cv > 0){ int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = top; rec->right = top->right; top->right = NULL; tree->rnum++; tree->msiz += ksiz + vsiz; tree->root = rec; } else { tree->root = top; return false; } return true; } /* Store a new string record into a tree object. */ bool tctreeputkeep2(TCTREE *tree, const char *kstr, const char *vstr){ assert(tree && kstr && vstr); return tctreeputkeep(tree, kstr, strlen(kstr), vstr, strlen(vstr)); } /* 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){ assert(tree && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); TCTREEREC *top = tctreesplay(tree, kbuf, ksiz); if(!top){ int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = NULL; rec->right = NULL; tree->root = rec; tree->rnum = 1; tree->msiz = ksiz + vsiz; return; } char *dbuf = (char *)top + sizeof(*top); int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop); if(cv < 0){ int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = top->left; rec->right = top; top->left = NULL; tree->rnum++; tree->msiz += ksiz + vsiz; tree->root = rec; } else if(cv > 0){ int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = top; rec->right = top->right; top->right = NULL; tree->rnum++; tree->msiz += ksiz + vsiz; tree->root = rec; } else { tree->msiz += vsiz; int psiz = TCALIGNPAD(ksiz); int asiz = sizeof(*top) + ksiz + psiz + top->vsiz + vsiz + 1; int unit = (asiz <= TCTREECSUNIT) ? TCTREECSUNIT : TCTREECBUNIT; asiz = (asiz - 1) + unit - (asiz - 1) % unit; TCTREEREC *old = top; TCREALLOC(top, top, asiz); if(top != old){ if(tree->cur == old) tree->cur = top; dbuf = (char *)top + sizeof(*top); } memcpy(dbuf + ksiz + psiz + top->vsiz, vbuf, vsiz); top->vsiz += vsiz; dbuf[ksiz+psiz+top->vsiz] = '\0'; tree->root = top; } } /* 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){ assert(tree && kstr && vstr); tctreeputcat(tree, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Store a record into a tree object with a duplication handler. */ bool tctreeputproc(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op){ assert(tree && kbuf && ksiz >= 0 && proc); TCTREEREC *top = tctreesplay(tree, kbuf, ksiz); if(!top){ if(!vbuf) return false; int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = NULL; rec->right = NULL; tree->root = rec; tree->rnum = 1; tree->msiz = ksiz + vsiz; return true; } char *dbuf = (char *)top + sizeof(*top); int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop); if(cv < 0){ if(!vbuf){ tree->root = top; return false; } int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = top->left; rec->right = top; top->left = NULL; tree->rnum++; tree->msiz += ksiz + vsiz; tree->root = rec; } else if(cv > 0){ if(!vbuf){ tree->root = top; return false; } int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = top; rec->right = top->right; top->right = NULL; tree->rnum++; tree->msiz += ksiz + vsiz; tree->root = rec; } else { int psiz = TCALIGNPAD(ksiz); int nvsiz; char *nvbuf = proc(dbuf + ksiz + psiz, top->vsiz, &nvsiz, op); if(nvbuf == (void *)-1){ tree->rnum--; tree->msiz -= top->ksiz + top->vsiz; if(tree->cur == top){ TCTREEREC *rec = top->right; if(rec){ while(rec->left){ rec = rec->left; } } tree->cur = rec; } if(!top->left){ tree->root = top->right; } else if(!top->right){ tree->root = top->left; } else { tree->root = top->left; TCTREEREC *rec = tctreesplay(tree, kbuf, ksiz); rec->right = top->right; tree->root = rec; } TCFREE(top); return true; } if(!nvbuf){ tree->root = top; return false; } tree->msiz += nvsiz - top->vsiz; if(nvsiz > top->vsiz){ TCTREEREC *old = top; TCREALLOC(top, top, sizeof(*top) + ksiz + psiz + nvsiz + 1); if(top != old){ if(tree->cur == old) tree->cur = top; dbuf = (char *)top + sizeof(*top); } } memcpy(dbuf + ksiz + psiz, nvbuf, nvsiz); dbuf[ksiz+psiz+nvsiz] = '\0'; top->vsiz = nvsiz; TCFREE(nvbuf); tree->root = top; } return true; } /* Remove a record of a tree object. */ bool tctreeout(TCTREE *tree, const void *kbuf, int ksiz){ assert(tree && kbuf && ksiz >= 0); TCTREEREC *top = tctreesplay(tree, kbuf, ksiz); if(!top) return false; char *dbuf = (char *)top + sizeof(*top); int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop); if(cv != 0){ tree->root = top; return false; } tree->rnum--; tree->msiz -= top->ksiz + top->vsiz; if(tree->cur == top){ TCTREEREC *rec = top->right; if(rec){ while(rec->left){ rec = rec->left; } } tree->cur = rec; } if(!top->left){ tree->root = top->right; } else if(!top->right){ tree->root = top->left; } else { tree->root = top->left; TCTREEREC *rec = tctreesplay(tree, kbuf, ksiz); rec->right = top->right; tree->root = rec; } TCFREE(top); return true; } /* Remove a string record of a tree object. */ bool tctreeout2(TCTREE *tree, const char *kstr){ assert(tree && kstr); return tctreeout(tree, kstr, strlen(kstr)); } /* Retrieve a record in a tree object. */ const void *tctreeget(TCTREE *tree, const void *kbuf, int ksiz, int *sp){ assert(tree && kbuf && ksiz >= 0 && sp); TCTREEREC *top = tctreesplay(tree, kbuf, ksiz); if(!top) return NULL; char *dbuf = (char *)top + sizeof(*top); int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop); if(cv != 0){ tree->root = top; return NULL; } tree->root = top; *sp = top->vsiz; return dbuf + top->ksiz + TCALIGNPAD(top->ksiz); } /* Retrieve a string record in a tree object. */ const char *tctreeget2(TCTREE *tree, const char *kstr){ assert(tree && kstr); int vsiz; return tctreeget(tree, kstr, strlen(kstr), &vsiz); } /* Initialize the iterator of a tree object. */ void tctreeiterinit(TCTREE *tree){ assert(tree); TCTREEREC *rec = tree->root; if(!rec) return; while(rec->left){ rec = rec->left; } tree->cur = rec; } /* Get the next key of the iterator of a tree object. */ const void *tctreeiternext(TCTREE *tree, int *sp){ assert(tree && sp); if(!tree->cur) return NULL; TCTREEREC *rec = tree->cur; const char *kbuf = (char *)rec + sizeof(*rec); int ksiz = rec->ksiz; rec = tctreesplay(tree, kbuf, ksiz); if(!rec) return NULL; tree->root = rec; rec = rec->right; if(rec){ while(rec->left){ rec = rec->left; } } tree->cur = rec; *sp = ksiz; return kbuf; } /* Get the next key string of the iterator of a tree object. */ const char *tctreeiternext2(TCTREE *tree){ assert(tree); int ksiz; return tctreeiternext(tree, &ksiz); } /* Get the number of records stored in a tree object. */ uint64_t tctreernum(const TCTREE *tree){ assert(tree); return tree->rnum; } /* Get the total size of memory used in a tree object. */ uint64_t tctreemsiz(const TCTREE *tree){ assert(tree); return tree->msiz + tree->rnum * (sizeof(*tree->root) + sizeof(tcgeneric_t)); } /* Create a list object containing all keys in a tree object. */ TCLIST *tctreekeys(const TCTREE *tree){ assert(tree); TCLIST *list = tclistnew2(tree->rnum); if(tree->root){ TCTREEREC **history; TCMALLOC(history, sizeof(*history) * tree->rnum); TCTREEREC **result; TCMALLOC(result, sizeof(*history) * tree->rnum); int hnum = 0; history[hnum++] = tree->root; while(hnum > 0){ TCTREEREC *rec = history[--hnum]; if(!rec){ rec = result[hnum]; char *dbuf = (char *)rec + sizeof(*rec); TCLISTPUSH(list, dbuf, rec->ksiz); continue; } if(rec->right) history[hnum++] = rec->right; history[hnum] = NULL; result[hnum] = rec; hnum++; if(rec->left) history[hnum++] = rec->left; } TCFREE(result); TCFREE(history); } return list; } /* Create a list object containing all values in a tree object. */ TCLIST *tctreevals(const TCTREE *tree){ assert(tree); TCLIST *list = tclistnew2(tree->rnum); if(tree->root){ TCTREEREC **history; TCMALLOC(history, sizeof(*history) * tree->rnum); TCTREEREC **result; TCMALLOC(result, sizeof(*history) * tree->rnum); int hnum = 0; history[hnum++] = tree->root; while(hnum > 0){ TCTREEREC *rec = history[--hnum]; if(!rec){ rec = result[hnum]; char *dbuf = (char *)rec + sizeof(*rec); TCLISTPUSH(list, dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz), rec->vsiz); continue; } if(rec->right) history[hnum++] = rec->right; history[hnum] = NULL; result[hnum] = rec; hnum++; if(rec->left) history[hnum++] = rec->left; } TCFREE(result); TCFREE(history); } return list; } /* Add an integer to a record in a tree object. */ int tctreeaddint(TCTREE *tree, const void *kbuf, int ksiz, int num){ assert(tree && kbuf && ksiz >= 0); TCTREEREC *top = tctreesplay(tree, kbuf, ksiz); if(!top){ int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, &num, sizeof(num)); dbuf[ksiz+psiz+sizeof(num)] = '\0'; rec->vsiz = sizeof(num); rec->left = NULL; rec->right = NULL; tree->root = rec; tree->rnum = 1; tree->msiz = ksiz + sizeof(num); return num; } char *dbuf = (char *)top + sizeof(*top); int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop); if(cv < 0){ int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1); dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, &num, sizeof(num)); dbuf[ksiz+psiz+sizeof(num)] = '\0'; rec->vsiz = sizeof(num); rec->left = top->left; rec->right = top; top->left = NULL; tree->rnum++; tree->msiz += ksiz + sizeof(num); tree->root = rec; } else if(cv > 0){ int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1); dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, &num, sizeof(num)); dbuf[ksiz+psiz+sizeof(num)] = '\0'; rec->vsiz = sizeof(num); rec->left = top; rec->right = top->right; top->right = NULL; tree->rnum++; tree->msiz += ksiz + sizeof(num); tree->root = rec; } else { tree->root = top; if(top->vsiz != sizeof(num)) return INT_MIN; int *resp = (int *)(dbuf + ksiz + TCALIGNPAD(ksiz)); return *resp += num; } return num; } /* Add a real number to a record in a tree object. */ double tctreeadddouble(TCTREE *tree, const void *kbuf, int ksiz, double num){ assert(tree && kbuf && ksiz >= 0); TCTREEREC *top = tctreesplay(tree, kbuf, ksiz); if(!top){ int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, &num, sizeof(num)); dbuf[ksiz+psiz+sizeof(num)] = '\0'; rec->vsiz = sizeof(num); rec->left = NULL; rec->right = NULL; tree->root = rec; tree->rnum = 1; tree->msiz = ksiz + sizeof(num); return num; } char *dbuf = (char *)top + sizeof(*top); int cv = tree->cmp(kbuf, ksiz, dbuf, top->ksiz, tree->cmpop); if(cv < 0){ int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1); dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, &num, sizeof(num)); dbuf[ksiz+psiz+sizeof(num)] = '\0'; rec->vsiz = sizeof(num); rec->left = top->left; rec->right = top; top->left = NULL; tree->rnum++; tree->msiz += ksiz + sizeof(num); tree->root = rec; } else if(cv > 0){ int psiz = TCALIGNPAD(ksiz); TCTREEREC *rec; TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + sizeof(num) + 1); dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, &num, sizeof(num)); dbuf[ksiz+psiz+sizeof(num)] = '\0'; rec->vsiz = sizeof(num); rec->left = top; rec->right = top->right; top->right = NULL; tree->rnum++; tree->msiz += ksiz + sizeof(num); tree->root = rec; } else { tree->root = top; if(top->vsiz != sizeof(num)) return nan(""); double *resp = (double *)(dbuf + ksiz + TCALIGNPAD(ksiz)); return *resp += num; } return num; } /* Clear a tree object. */ void tctreeclear(TCTREE *tree){ assert(tree); if(tree->root){ TCTREEREC *histbuf[TREESTACKNUM]; TCTREEREC **history = histbuf; int hnum = 0; history[hnum++] = tree->root; while(hnum > 0){ TCTREEREC *rec = history[--hnum]; if(hnum >= TREESTACKNUM - 2 && history == histbuf){ TCMALLOC(history, sizeof(*history) * tree->rnum); memcpy(history, histbuf, sizeof(*history) * hnum); } if(rec->left) history[hnum++] = rec->left; if(rec->right) history[hnum++] = rec->right; TCFREE(rec); } if(history != histbuf) TCFREE(history); } tree->root = NULL; tree->cur = NULL; tree->rnum = 0; tree->msiz = 0; } /* Remove fringe records of a tree object. */ void tctreecutfringe(TCTREE *tree, int num){ assert(tree && num >= 0); if(!tree->root || num < 1) return; TCTREEREC **history; TCMALLOC(history, sizeof(*history) * tree->rnum); int hnum = 0; history[hnum++] = tree->root; for(int i = 0; i < hnum; i++){ TCTREEREC *rec = history[i]; if(rec->left) history[hnum++] = rec->left; if(rec->right) history[hnum++] = rec->right; } TCTREEREC *cur = NULL; for(int i = hnum - 1; i >= 0; i--){ TCTREEREC *rec = history[i]; if(rec->left){ TCTREEREC *child = rec->left; tree->rnum--; tree->msiz -= child->ksiz + child->vsiz; rec->left = NULL; if(tree->cur == child){ tree->cur = NULL; cur = child; } else { TCFREE(child); } if(--num < 1) break; } if(rec->right){ TCTREEREC *child = rec->right; tree->rnum--; tree->msiz -= child->ksiz + child->vsiz; rec->right = NULL; if(tree->cur == child){ tree->cur = NULL; cur = child; } else { TCFREE(child); } if(--num < 1) break; } } if(num > 0){ TCFREE(tree->root); tree->root = NULL; tree->cur = NULL; tree->rnum = 0; tree->msiz = 0; } if(cur){ char *dbuf = (char *)cur + sizeof(*cur); tctreeiterinit2(tree, dbuf, cur->ksiz); TCFREE(cur); } TCFREE(history); } /* Serialize a tree object into a byte array. */ void *tctreedump(const TCTREE *tree, int *sp){ assert(tree && sp); int tsiz = 0; if(tree->root){ TCTREEREC *histbuf[TREESTACKNUM]; TCTREEREC **history = histbuf; int hnum = 0; history[hnum++] = tree->root; while(hnum > 0){ TCTREEREC *rec = history[--hnum]; if(hnum >= TREESTACKNUM - 2 && history == histbuf){ TCMALLOC(history, sizeof(*history) * tree->rnum); memcpy(history, histbuf, sizeof(*history) * hnum); } if(rec->left) history[hnum++] = rec->left; if(rec->right) history[hnum++] = rec->right; tsiz += rec->ksiz + rec->vsiz + sizeof(int) * 2; } if(history != histbuf) TCFREE(history); } char *buf; TCMALLOC(buf, tsiz + 1); char *wp = buf; if(tree->root){ TCTREEREC *histbuf[TREESTACKNUM]; TCTREEREC **history = histbuf; int hnum = 0; history[hnum++] = tree->root; while(hnum > 0){ TCTREEREC *rec = history[--hnum]; if(hnum >= TREESTACKNUM - 2 && history == histbuf){ TCMALLOC(history, sizeof(*history) * tree->rnum); memcpy(history, histbuf, sizeof(*history) * hnum); } if(rec->left) history[hnum++] = rec->left; if(rec->right) history[hnum++] = rec->right; const char *kbuf = (char *)rec + sizeof(*rec); int ksiz = rec->ksiz; const char *vbuf = kbuf + ksiz + TCALIGNPAD(ksiz); int vsiz = rec->vsiz; int step; TCSETVNUMBUF(step, wp, ksiz); wp += step; memcpy(wp, kbuf, ksiz); wp += ksiz; TCSETVNUMBUF(step, wp, vsiz); wp += step; memcpy(wp, vbuf, vsiz); wp += vsiz; } if(history != histbuf) TCFREE(history); } *sp = wp - buf; return buf; } /* Create a tree object from a serialized byte array. */ TCTREE *tctreeload(const void *ptr, int size, TCCMP cmp, void *cmpop){ assert(ptr && size >= 0 && cmp); TCTREE *tree = tctreenew2(cmp, cmpop); const char *rp = ptr; const char *ep = (char *)ptr + size; while(rp < ep){ int step, ksiz, vsiz; TCREADVNUMBUF(rp, ksiz, step); rp += step; const char *kbuf = rp; rp += ksiz; TCREADVNUMBUF(rp, vsiz, step); rp += step; tctreeputkeep(tree, kbuf, ksiz, rp, vsiz); rp += vsiz; } return tree; } /* Perform the splay operation of a tree object. `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. The return value is the pointer to the record corresponding the key. */ static TCTREEREC* tctreesplay(TCTREE *tree, const void *kbuf, int ksiz){ assert(tree && kbuf && ksiz >= 0); TCTREEREC *top = tree->root; if(!top) return NULL; TCCMP cmp = tree->cmp; void *cmpop = tree->cmpop; TCTREEREC ent; ent.left = NULL; ent.right = NULL; TCTREEREC *lrec = &ent; TCTREEREC *rrec = &ent; while(true){ char *dbuf = (char *)top + sizeof(*top); int cv = cmp(kbuf, ksiz, dbuf, top->ksiz, cmpop); if(cv < 0){ if(!top->left) break; dbuf = (char *)top->left + sizeof(*top); cv = cmp(kbuf, ksiz, dbuf, top->left->ksiz, cmpop); if(cv < 0){ TCTREEREC *swap = top->left; top->left = swap->right; swap->right = top; top = swap; if(!top->left) break; } rrec->left = top; rrec = top; top = top->left; } else if(cv > 0){ if(!top->right) break; dbuf = (char *)top->right + sizeof(*top); cv = cmp(kbuf, ksiz, dbuf, top->right->ksiz, cmpop); if(cv > 0){ TCTREEREC *swap = top->right; top->right = swap->left; swap->left = top; top = swap; if(!top->right) break; } lrec->right = top; lrec = top; top = top->right; } else { break; } } lrec->right = top->left; rrec->left = top->right; top->left = ent.right; top->right = ent.left; return top; } /************************************************************************************************* * ordered tree (for experts) *************************************************************************************************/ /* Store a record into a tree object without balancing nodes. */ void tctreeput3(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(tree && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); TCTREEREC *rec = tree->root; TCTREEREC **entp = NULL; while(rec){ char *dbuf = (char *)rec + sizeof(*rec); int cv = tree->cmp(kbuf, ksiz, dbuf, rec->ksiz, tree->cmpop); if(cv < 0){ entp = &(rec->left); rec = rec->left; } else if(cv > 0){ entp = &(rec->right); rec = rec->right; } else { tree->msiz += vsiz - rec->vsiz; int psiz = TCALIGNPAD(ksiz); if(vsiz > rec->vsiz){ TCTREEREC *old = rec; TCREALLOC(rec, rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); if(rec != old){ if(tree->root == old) tree->root = rec; if(tree->cur == old) tree->cur = rec; if(entp) *entp = rec; dbuf = (char *)rec + sizeof(*rec); } } memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; return; } } int psiz = TCALIGNPAD(ksiz); TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = NULL; rec->right = NULL; if(entp){ *entp = rec; } else { tree->root = rec; } tree->rnum++; tree->msiz += ksiz + vsiz; } /* Store a new record into a map object without balancing nodes. */ bool tctreeputkeep3(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(tree && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); TCTREEREC *rec = tree->root; TCTREEREC **entp = NULL; while(rec){ char *dbuf = (char *)rec + sizeof(*rec); int cv = tree->cmp(kbuf, ksiz, dbuf, rec->ksiz, tree->cmpop); if(cv < 0){ entp = &(rec->left); rec = rec->left; } else if(cv > 0){ entp = &(rec->right); rec = rec->right; } else { return false; } } int psiz = TCALIGNPAD(ksiz); TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = NULL; rec->right = NULL; if(entp){ *entp = rec; } else { tree->root = rec; } tree->rnum++; tree->msiz += ksiz + vsiz; return true; } /* Concatenate a value at the existing record in a tree object without balancing nodes. */ void tctreeputcat3(TCTREE *tree, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(tree && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); TCTREEREC *rec = tree->root; TCTREEREC **entp = NULL; while(rec){ char *dbuf = (char *)rec + sizeof(*rec); int cv = tree->cmp(kbuf, ksiz, dbuf, rec->ksiz, tree->cmpop); if(cv < 0){ entp = &(rec->left); rec = rec->left; } else if(cv > 0){ entp = &(rec->right); rec = rec->right; } else { tree->msiz += vsiz; int psiz = TCALIGNPAD(ksiz); int asiz = sizeof(*rec) + ksiz + psiz + rec->vsiz + vsiz + 1; int unit = (asiz <= TCTREECSUNIT) ? TCTREECSUNIT : TCTREECBUNIT; asiz = (asiz - 1) + unit - (asiz - 1) % unit; TCTREEREC *old = rec; TCREALLOC(rec, rec, asiz); if(rec != old){ if(tree->root == old) tree->root = rec; if(tree->cur == old) tree->cur = rec; if(entp) *entp = rec; dbuf = (char *)rec + sizeof(*rec); } memcpy(dbuf + ksiz + psiz + rec->vsiz, vbuf, vsiz); rec->vsiz += vsiz; dbuf[ksiz+psiz+rec->vsiz] = '\0'; return; } } int psiz = TCALIGNPAD(ksiz); TCMALLOC(rec, sizeof(*rec) + ksiz + psiz + vsiz + 1); char *dbuf = (char *)rec + sizeof(*rec); memcpy(dbuf, kbuf, ksiz); dbuf[ksiz] = '\0'; rec->ksiz = ksiz; memcpy(dbuf + ksiz + psiz, vbuf, vsiz); dbuf[ksiz+psiz+vsiz] = '\0'; rec->vsiz = vsiz; rec->left = NULL; rec->right = NULL; if(entp){ *entp = rec; } else { tree->root = rec; } tree->rnum++; tree->msiz += ksiz + vsiz; } /* Retrieve a record in a tree object without balancing nodes. */ const void *tctreeget3(const TCTREE *tree, const void *kbuf, int ksiz, int *sp){ assert(tree && kbuf && ksiz >= 0 && sp); TCTREEREC *rec = tree->root; while(rec){ char *dbuf = (char *)rec + sizeof(*rec); int cv = tree->cmp(kbuf, ksiz, dbuf, rec->ksiz, tree->cmpop); if(cv < 0){ rec = rec->left; } else if(cv > 0){ rec = rec->right; } else { *sp = rec->vsiz; return dbuf + rec->ksiz + TCALIGNPAD(rec->ksiz); } } return NULL; } /* Retrieve a string record in a tree object with specifying the default value string. */ const char *tctreeget4(TCTREE *tree, const char *kstr, const char *dstr){ assert(tree && kstr && dstr); int vsiz; const char *vbuf = tctreeget(tree, kstr, strlen(kstr), &vsiz); return vbuf ? vbuf : dstr; } /* Initialize the iterator of a tree object in front of records corresponding a key. */ void tctreeiterinit2(TCTREE *tree, const void *kbuf, int ksiz){ assert(tree && kbuf && ksiz >= 0); TCTREEREC *rec = tree->root; while(rec){ char *dbuf = (char *)rec + sizeof(*rec); int cv = tree->cmp(kbuf, ksiz, dbuf, rec->ksiz, tree->cmpop); if(cv < 0){ tree->cur = rec; rec = rec->left; } else if(cv > 0){ rec = rec->right; } else { tree->cur = rec; return; } } } /* Initialize the iterator of a tree object in front of records corresponding a key string. */ void tctreeiterinit3(TCTREE *tree, const char *kstr){ assert(tree); tctreeiterinit2(tree, kstr, strlen(kstr)); } /* Get the value bound to the key fetched from the iterator of a tree object. */ const void *tctreeiterval(const void *kbuf, int *sp){ assert(kbuf && sp); TCTREEREC *rec = (TCTREEREC *)((char *)kbuf - sizeof(*rec)); *sp = rec->vsiz; return (char *)kbuf + rec->ksiz + TCALIGNPAD(rec->ksiz); } /* Get the value string bound to the key fetched from the iterator of a tree object. */ const char *tctreeiterval2(const char *kstr){ assert(kstr); TCTREEREC *rec = (TCTREEREC *)(kstr - sizeof(*rec)); return kstr + rec->ksiz + TCALIGNPAD(rec->ksiz); } /* Create an array of strings of all keys in a tree object. */ const char **tctreekeys2(const TCTREE *tree, int *np){ assert(tree && np); const char **ary; TCMALLOC(ary, sizeof(*ary) * tree->rnum + 1); int anum = 0; if(tree->root){ TCTREEREC **history; TCMALLOC(history, sizeof(*history) * tree->rnum); TCTREEREC **result; TCMALLOC(result, sizeof(*history) * tree->rnum); int hnum = 0; history[hnum++] = tree->root; while(hnum > 0){ TCTREEREC *rec = history[--hnum]; if(!rec){ rec = result[hnum]; ary[(anum++)] = (char *)rec + sizeof(*rec); continue; } if(rec->right) history[hnum++] = rec->right; history[hnum] = NULL; result[hnum] = rec; hnum++; if(rec->left) history[hnum++] = rec->left; } TCFREE(result); TCFREE(history); } *np = anum; return ary; } /* Create an array of strings of all values in a tree object. */ const char **tctreevals2(const TCTREE *tree, int *np){ assert(tree && np); const char **ary; TCMALLOC(ary, sizeof(*ary) * tree->rnum + 1); int anum = 0; if(tree->root){ TCTREEREC **history; TCMALLOC(history, sizeof(*history) * tree->rnum); TCTREEREC **result; TCMALLOC(result, sizeof(*history) * tree->rnum); int hnum = 0; history[hnum++] = tree->root; while(hnum > 0){ TCTREEREC *rec = history[--hnum]; if(!rec){ rec = result[hnum]; ary[(anum++)] = (char *)rec + sizeof(*rec); continue; } if(rec->right) history[hnum++] = rec->right; history[hnum] = NULL; result[hnum] = rec; hnum++; if(rec->left) history[hnum++] = rec->left; } TCFREE(result); TCFREE(history); } *np = anum; return ary; } /* Extract a tree record from a serialized byte array. */ void *tctreeloadone(const void *ptr, int size, const void *kbuf, int ksiz, int *sp){ assert(ptr && size >= 0 && kbuf && ksiz >= 0 && sp); const char *rp = ptr; const char *ep = (char *)ptr + size; while(rp < ep){ int step, rsiz; TCREADVNUMBUF(rp, rsiz, step); rp += step; if(rsiz == ksiz && !memcmp(kbuf, rp, rsiz)){ rp += rsiz; TCREADVNUMBUF(rp, rsiz, step); rp += step; *sp = rsiz; char *rv; TCMEMDUP(rv, rp, rsiz); return rv; } rp += rsiz; TCREADVNUMBUF(rp, rsiz, step); rp += step; rp += rsiz; } return NULL; } /* Perform formatted output into a tree object. */ void tctreeprintf(TCTREE *tree, const char *kstr, const char *format, ...){ assert(tree && kstr && format); TCXSTR *xstr = tcxstrnew(); va_list ap; va_start(ap, format); tcvxstrprintf(xstr, format, ap); va_end(ap); tctreeput(tree, kstr, strlen(kstr), TCXSTRPTR(xstr), TCXSTRSIZE(xstr)); tcxstrdel(xstr); } /************************************************************************************************* * on-memory hash database *************************************************************************************************/ #define TCMDBMNUM 8 // number of internal maps #define TCMDBDEFBNUM 65536 // default bucket number /* get the first hash value */ #define TCMDBHASH(TC_res, TC_kbuf, TC_ksiz) \ do { \ const unsigned char *_TC_p = (const unsigned char *)(TC_kbuf) + TC_ksiz - 1; \ int _TC_ksiz = TC_ksiz; \ for((TC_res) = 0x20071123; _TC_ksiz--;){ \ (TC_res) = (TC_res) * 33 + *(_TC_p)--; \ } \ (TC_res) &= TCMDBMNUM - 1; \ } while(false) /* Create an on-memory hash database object. */ TCMDB *tcmdbnew(void){ return tcmdbnew2(TCMDBDEFBNUM); } /* Create an on-memory hash database with specifying the number of the buckets. */ TCMDB *tcmdbnew2(uint32_t bnum){ TCMDB *mdb; if(bnum < 1) bnum = TCMDBDEFBNUM; bnum = bnum / TCMDBMNUM + 17; TCMALLOC(mdb, sizeof(*mdb)); TCMALLOC(mdb->mmtxs, sizeof(pthread_rwlock_t) * TCMDBMNUM); TCMALLOC(mdb->imtx, sizeof(pthread_mutex_t)); TCMALLOC(mdb->maps, sizeof(TCMAP *) * TCMDBMNUM); if(pthread_mutex_init(mdb->imtx, NULL) != 0) tcmyfatal("mutex error"); for(int i = 0; i < TCMDBMNUM; i++){ if(pthread_rwlock_init((pthread_rwlock_t *)mdb->mmtxs + i, NULL) != 0) tcmyfatal("rwlock error"); mdb->maps[i] = tcmapnew2(bnum); } mdb->iter = -1; return mdb; } /* Delete an on-memory hash database object. */ void tcmdbdel(TCMDB *mdb){ assert(mdb); for(int i = TCMDBMNUM - 1; i >= 0; i--){ tcmapdel(mdb->maps[i]); pthread_rwlock_destroy((pthread_rwlock_t *)mdb->mmtxs + i); } pthread_mutex_destroy(mdb->imtx); TCFREE(mdb->maps); TCFREE(mdb->imtx); TCFREE(mdb->mmtxs); TCFREE(mdb); } /* Store a record into an on-memory hash database. */ void tcmdbput(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); unsigned int mi; TCMDBHASH(mi, kbuf, ksiz); if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return; tcmapput(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz); pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); } /* Store a string record into an on-memory hash database. */ void tcmdbput2(TCMDB *mdb, const char *kstr, const char *vstr){ assert(mdb && kstr && vstr); tcmdbput(mdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Store a new record into an on-memory hash database. */ bool tcmdbputkeep(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); unsigned int mi; TCMDBHASH(mi, kbuf, ksiz); if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return false; bool rv = tcmapputkeep(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz); pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); return rv; } /* Store a new string record into an on-memory hash database. */ bool tcmdbputkeep2(TCMDB *mdb, const char *kstr, const char *vstr){ assert(mdb && kstr && vstr); return tcmdbputkeep(mdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* 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){ assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); unsigned int mi; TCMDBHASH(mi, kbuf, ksiz); if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return; tcmapputcat(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz); pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); } /* 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){ assert(mdb && kstr && vstr); tcmdbputcat(mdb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Remove a record of an on-memory hash database. */ bool tcmdbout(TCMDB *mdb, const void *kbuf, int ksiz){ assert(mdb && kbuf && ksiz >= 0); unsigned int mi; TCMDBHASH(mi, kbuf, ksiz); if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return false; bool rv = tcmapout(mdb->maps[mi], kbuf, ksiz); pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); return rv; } /* Remove a string record of an on-memory hash database. */ bool tcmdbout2(TCMDB *mdb, const char *kstr){ assert(mdb && kstr); return tcmdbout(mdb, kstr, strlen(kstr)); } /* Retrieve a record in an on-memory hash database. */ void *tcmdbget(TCMDB *mdb, const void *kbuf, int ksiz, int *sp){ assert(mdb && kbuf && ksiz >= 0 && sp); unsigned int mi; TCMDBHASH(mi, kbuf, ksiz); if(pthread_rwlock_rdlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return NULL; int vsiz; const char *vbuf = tcmapget(mdb->maps[mi], kbuf, ksiz, &vsiz); char *rv; if(vbuf){ TCMEMDUP(rv, vbuf, vsiz); *sp = vsiz; } else { rv = NULL; } pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); return rv; } /* Retrieve a string record in an on-memory hash database. */ char *tcmdbget2(TCMDB *mdb, const char *kstr){ assert(mdb && kstr); int vsiz; return tcmdbget(mdb, kstr, strlen(kstr), &vsiz); } /* 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){ assert(mdb && kbuf && ksiz >= 0); unsigned int mi; TCMDBHASH(mi, kbuf, ksiz); if(pthread_rwlock_rdlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return -1; int vsiz; const char *vbuf = tcmapget(mdb->maps[mi], kbuf, ksiz, &vsiz); if(!vbuf) vsiz = -1; pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); return vsiz; } /* Get the size of the value of a string record in an on-memory hash database object. */ int tcmdbvsiz2(TCMDB *mdb, const char *kstr){ assert(mdb && kstr); return tcmdbvsiz(mdb, kstr, strlen(kstr)); } /* Initialize the iterator of an on-memory hash database. */ void tcmdbiterinit(TCMDB *mdb){ assert(mdb); if(pthread_mutex_lock(mdb->imtx) != 0) return; for(int i = 0; i < TCMDBMNUM; i++){ tcmapiterinit(mdb->maps[i]); } mdb->iter = 0; pthread_mutex_unlock(mdb->imtx); } /* Get the next key of the iterator of an on-memory hash database. */ void *tcmdbiternext(TCMDB *mdb, int *sp){ assert(mdb && sp); if(pthread_mutex_lock(mdb->imtx) != 0) return NULL; if(mdb->iter < 0 || mdb->iter >= TCMDBMNUM){ pthread_mutex_unlock(mdb->imtx); return NULL; } int mi = mdb->iter; if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0){ pthread_mutex_unlock(mdb->imtx); return NULL; } int ksiz; const char *kbuf; while(!(kbuf = tcmapiternext(mdb->maps[mi], &ksiz)) && mi < TCMDBMNUM - 1){ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); mi = ++mdb->iter; if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0){ pthread_mutex_unlock(mdb->imtx); return NULL; } } char *rv; if(kbuf){ TCMEMDUP(rv, kbuf, ksiz); *sp = ksiz; } else { rv = NULL; } pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); pthread_mutex_unlock(mdb->imtx); return rv; } /* Get the next key string of the iterator of an on-memory hash database. */ char *tcmdbiternext2(TCMDB *mdb){ assert(mdb); int ksiz; return tcmdbiternext(mdb, &ksiz); } /* Get forward matching keys in an on-memory hash database object. */ TCLIST *tcmdbfwmkeys(TCMDB *mdb, const void *pbuf, int psiz, int max){ assert(mdb && pbuf && psiz >= 0); TCLIST* keys = tclistnew(); if(pthread_mutex_lock(mdb->imtx) != 0) return keys; if(max < 0) max = INT_MAX; for(int i = 0; i < TCMDBMNUM && TCLISTNUM(keys) < max; i++){ if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){ TCMAP *map = mdb->maps[i]; TCMAPREC *cur = map->cur; tcmapiterinit(map); const char *kbuf; int ksiz; while(TCLISTNUM(keys) < max && (kbuf = tcmapiternext(map, &ksiz)) != NULL){ if(ksiz >= psiz && !memcmp(kbuf, pbuf, psiz)) TCLISTPUSH(keys, kbuf, ksiz); } map->cur = cur; pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i); } } pthread_mutex_unlock(mdb->imtx); return keys; } /* Get forward matching string keys in an on-memory hash database object. */ TCLIST *tcmdbfwmkeys2(TCMDB *mdb, const char *pstr, int max){ assert(mdb && pstr); return tcmdbfwmkeys(mdb, pstr, strlen(pstr), max); } /* Get the number of records stored in an on-memory hash database. */ uint64_t tcmdbrnum(TCMDB *mdb){ assert(mdb); uint64_t rnum = 0; for(int i = 0; i < TCMDBMNUM; i++){ rnum += tcmaprnum(mdb->maps[i]); } return rnum; } /* Get the total size of memory used in an on-memory hash database object. */ uint64_t tcmdbmsiz(TCMDB *mdb){ assert(mdb); uint64_t msiz = 0; for(int i = 0; i < TCMDBMNUM; i++){ msiz += tcmapmsiz(mdb->maps[i]); } return msiz; } /* Add an integer to a record in an on-memory hash database object. */ int tcmdbaddint(TCMDB *mdb, const void *kbuf, int ksiz, int num){ assert(mdb && kbuf && ksiz >= 0); unsigned int mi; TCMDBHASH(mi, kbuf, ksiz); if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return INT_MIN; int rv = tcmapaddint(mdb->maps[mi], kbuf, ksiz, num); pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); return rv; } /* 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){ assert(mdb && kbuf && ksiz >= 0); unsigned int mi; TCMDBHASH(mi, kbuf, ksiz); if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return nan(""); double rv = tcmapadddouble(mdb->maps[mi], kbuf, ksiz, num); pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); return rv; } /* Clear an on-memory hash database object. */ void tcmdbvanish(TCMDB *mdb){ assert(mdb); for(int i = 0; i < TCMDBMNUM; i++){ if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){ tcmapclear(mdb->maps[i]); pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i); } } } /* Remove front records of a map object. */ void tcmdbcutfront(TCMDB *mdb, int num){ assert(mdb && num >= 0); num = num / TCMDBMNUM + 1; for(int i = 0; i < TCMDBMNUM; i++){ if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) == 0){ tcmapcutfront(mdb->maps[i], num); pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i); } } } /************************************************************************************************* * on-memory hash database (for experts) *************************************************************************************************/ /* Store a record and make it semivolatile in an on-memory hash database object. */ void tcmdbput3(TCMDB *mdb, const void *kbuf, int ksiz, const char *vbuf, int vsiz){ assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); unsigned int mi; TCMDBHASH(mi, kbuf, ksiz); if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return; tcmapput3(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz); pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); } /* Store a record of the value of two regions into an on-memory hash database object. */ void tcmdbput4(TCMDB *mdb, const void *kbuf, int ksiz, const void *fvbuf, int fvsiz, const void *lvbuf, int lvsiz){ assert(mdb && kbuf && ksiz >= 0 && fvbuf && fvsiz >= 0 && lvbuf && lvsiz >= 0); unsigned int mi; TCMDBHASH(mi, kbuf, ksiz); if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return; tcmapput4(mdb->maps[mi], kbuf, ksiz, fvbuf, fvsiz, lvbuf, lvsiz); pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); } /* Concatenate a value and make it semivolatile in on-memory hash database object. */ void tcmdbputcat3(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(mdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); unsigned int mi; TCMDBHASH(mi, kbuf, ksiz); if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return; tcmapputcat3(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz); pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); } /* Store a record into a on-memory hash database object with a duplication handler. */ bool tcmdbputproc(TCMDB *mdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op){ assert(mdb && kbuf && ksiz >= 0 && proc); unsigned int mi; TCMDBHASH(mi, kbuf, ksiz); if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return false; bool rv = tcmapputproc(mdb->maps[mi], kbuf, ksiz, vbuf, vsiz, proc, op); pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); return rv; } /* Retrieve a record and move it astern in an on-memory hash database. */ void *tcmdbget3(TCMDB *mdb, const void *kbuf, int ksiz, int *sp){ assert(mdb && kbuf && ksiz >= 0 && sp); unsigned int mi; TCMDBHASH(mi, kbuf, ksiz); if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0) return NULL; int vsiz; const char *vbuf = tcmapget3(mdb->maps[mi], kbuf, ksiz, &vsiz); char *rv; if(vbuf){ TCMEMDUP(rv, vbuf, vsiz); *sp = vsiz; } else { rv = NULL; } pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); return rv; } /* Initialize the iterator of an on-memory map database object in front of a key. */ void tcmdbiterinit2(TCMDB *mdb, const void *kbuf, int ksiz){ if(pthread_mutex_lock(mdb->imtx) != 0) return; unsigned int mi; TCMDBHASH(mi, kbuf, ksiz); if(pthread_rwlock_rdlock((pthread_rwlock_t *)mdb->mmtxs + mi) != 0){ pthread_mutex_unlock(mdb->imtx); return; } int vsiz; if(tcmapget(mdb->maps[mi], kbuf, ksiz, &vsiz)){ for(int i = 0; i < TCMDBMNUM; i++){ tcmapiterinit(mdb->maps[i]); } tcmapiterinit2(mdb->maps[mi], kbuf, ksiz); mdb->iter = mi; } pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + mi); pthread_mutex_unlock(mdb->imtx); } /* Initialize the iterator of an on-memory map database object in front of a key string. */ void tcmdbiterinit3(TCMDB *mdb, const char *kstr){ assert(mdb && kstr); tcmdbiterinit2(mdb, kstr, strlen(kstr)); } /* Process each record atomically of an on-memory hash database object. */ void tcmdbforeach(TCMDB *mdb, TCITER iter, void *op){ assert(mdb && iter); for(int i = 0; i < TCMDBMNUM; i++){ if(pthread_rwlock_wrlock((pthread_rwlock_t *)mdb->mmtxs + i) != 0){ while(i >= 0){ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i); i--; } return; } } bool cont = true; for(int i = 0; cont && i < TCMDBMNUM; i++){ TCMAP *map = mdb->maps[i]; TCMAPREC *cur = map->cur; tcmapiterinit(map); int ksiz; const char *kbuf; while(cont && (kbuf = tcmapiternext(map, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); if(!iter(kbuf, ksiz, vbuf, vsiz, op)) cont = false; } map->cur = cur; } for(int i = TCMDBMNUM - 1; i >= 0; i--){ pthread_rwlock_unlock((pthread_rwlock_t *)mdb->mmtxs + i); } } /************************************************************************************************* * on-memory tree database *************************************************************************************************/ /* Create an on-memory tree database object. */ TCNDB *tcndbnew(void){ return tcndbnew2(tccmplexical, NULL); } /* Create an on-memory tree database object with specifying the custom comparison function. */ TCNDB *tcndbnew2(TCCMP cmp, void *cmpop){ assert(cmp); TCNDB *ndb; TCMALLOC(ndb, sizeof(*ndb)); TCMALLOC(ndb->mmtx, sizeof(pthread_mutex_t)); if(pthread_mutex_init(ndb->mmtx, NULL) != 0) tcmyfatal("mutex error"); ndb->tree = tctreenew2(cmp, cmpop); return ndb; } /* Delete an on-memory tree database object. */ void tcndbdel(TCNDB *ndb){ assert(ndb); tctreedel(ndb->tree); pthread_mutex_destroy(ndb->mmtx); TCFREE(ndb->mmtx); TCFREE(ndb); } /* Store a record into an on-memory tree database object. */ void tcndbput(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(ndb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return; tctreeput(ndb->tree, kbuf, ksiz, vbuf, vsiz); pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); } /* Store a string record into an on-memory tree database object. */ void tcndbput2(TCNDB *ndb, const char *kstr, const char *vstr){ assert(ndb && kstr && vstr); tcndbput(ndb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* 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){ assert(ndb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return false; bool rv = tctreeputkeep(ndb->tree, kbuf, ksiz, vbuf, vsiz); pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); return rv; } /* Store a new string record into an on-memory tree database object. */ bool tcndbputkeep2(TCNDB *ndb, const char *kstr, const char *vstr){ assert(ndb && kstr && vstr); return tcndbputkeep(ndb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* 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){ assert(ndb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return; tctreeputcat(ndb->tree, kbuf, ksiz, vbuf, vsiz); pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); } /* 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){ assert(ndb && kstr && vstr); tcndbputcat(ndb, kstr, strlen(kstr), vstr, strlen(vstr)); } /* Remove a record of an on-memory tree database object. */ bool tcndbout(TCNDB *ndb, const void *kbuf, int ksiz){ assert(ndb && kbuf && ksiz >= 0); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return false; bool rv = tctreeout(ndb->tree, kbuf, ksiz); pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); return rv; } /* Remove a string record of an on-memory tree database object. */ bool tcndbout2(TCNDB *ndb, const char *kstr){ assert(ndb && kstr); return tcndbout(ndb, kstr, strlen(kstr)); } /* Retrieve a record in an on-memory tree database object. */ void *tcndbget(TCNDB *ndb, const void *kbuf, int ksiz, int *sp){ assert(ndb && kbuf && ksiz >= 0 && sp); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return NULL; int vsiz; const char *vbuf = tctreeget(ndb->tree, kbuf, ksiz, &vsiz); char *rv; if(vbuf){ TCMEMDUP(rv, vbuf, vsiz); *sp = vsiz; } else { rv = NULL; } pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); return rv; } /* Retrieve a string record in an on-memory tree database object. */ char *tcndbget2(TCNDB *ndb, const char *kstr){ assert(ndb && kstr); int vsiz; return tcndbget(ndb, kstr, strlen(kstr), &vsiz); } /* 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){ assert(ndb && kbuf && ksiz >= 0); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return -1; int vsiz; const char *vbuf = tctreeget(ndb->tree, kbuf, ksiz, &vsiz); if(!vbuf) vsiz = -1; pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); return vsiz; } /* Get the size of the value of a string record in an on-memory tree database object. */ int tcndbvsiz2(TCNDB *ndb, const char *kstr){ assert(ndb && kstr); return tcndbvsiz(ndb, kstr, strlen(kstr)); } /* Initialize the iterator of an on-memory tree database object. */ void tcndbiterinit(TCNDB *ndb){ assert(ndb); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return; tctreeiterinit(ndb->tree); pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); } /* Get the next key of the iterator of an on-memory tree database object. */ void *tcndbiternext(TCNDB *ndb, int *sp){ assert(ndb && sp); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return NULL; int ksiz; const char *kbuf = tctreeiternext(ndb->tree, &ksiz); char *rv; if(kbuf){ TCMEMDUP(rv, kbuf, ksiz); *sp = ksiz; } else { rv = NULL; } pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); return rv; } /* Get the next key string of the iterator of an on-memory tree database object. */ char *tcndbiternext2(TCNDB *ndb){ assert(ndb); int ksiz; return tcndbiternext(ndb, &ksiz); } /* Get forward matching keys in an on-memory tree database object. */ TCLIST *tcndbfwmkeys(TCNDB *ndb, const void *pbuf, int psiz, int max){ assert(ndb && pbuf && psiz >= 0); TCLIST* keys = tclistnew(); if(pthread_mutex_lock(ndb->mmtx) != 0) return keys; if(max < 0) max = INT_MAX; TCTREE *tree = ndb->tree; TCTREEREC *cur = tree->cur; tctreeiterinit2(tree, pbuf, psiz); const char *lbuf = NULL; int lsiz = 0; const char *kbuf; int ksiz; while(TCLISTNUM(keys) < max && (kbuf = tctreeiternext(tree, &ksiz)) != NULL){ if(ksiz < psiz || memcmp(kbuf, pbuf, psiz)) break; if(!lbuf || lsiz != ksiz || memcmp(kbuf, lbuf, ksiz)){ TCLISTPUSH(keys, kbuf, ksiz); if(TCLISTNUM(keys) >= max) break; lbuf = kbuf; lsiz = ksiz; } } tree->cur = cur; pthread_mutex_unlock(ndb->mmtx); return keys; } /* Get forward matching string keys in an on-memory tree database object. */ TCLIST *tcndbfwmkeys2(TCNDB *ndb, const char *pstr, int max){ assert(ndb && pstr); return tcndbfwmkeys(ndb, pstr, strlen(pstr), max); } /* Get the number of records stored in an on-memory tree database object. */ uint64_t tcndbrnum(TCNDB *ndb){ assert(ndb); return tctreernum(ndb->tree); } /* Get the total size of memory used in an on-memory tree database object. */ uint64_t tcndbmsiz(TCNDB *ndb){ assert(ndb); return tctreemsiz(ndb->tree); } /* Add an integer to a record in an on-memory tree database object. */ int tcndbaddint(TCNDB *ndb, const void *kbuf, int ksiz, int num){ assert(ndb && kbuf && ksiz >= 0); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return INT_MIN; int rv = tctreeaddint(ndb->tree, kbuf, ksiz, num); pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); return rv; } /* 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){ assert(ndb && kbuf && ksiz >= 0); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return nan(""); double rv = tctreeadddouble(ndb->tree, kbuf, ksiz, num); pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); return rv; } /* Clear an on-memory tree database object. */ void tcndbvanish(TCNDB *ndb){ assert(ndb); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0); tctreeclear(ndb->tree); pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); } /* Remove fringe records of an on-memory tree database object. */ void tcndbcutfringe(TCNDB *ndb, int num){ assert(ndb && num >= 0); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0); tctreecutfringe(ndb->tree, num); pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); } /************************************************************************************************* * ordered tree (for experts) *************************************************************************************************/ /* Store a record into a on-memory tree database without balancing nodes. */ void tcndbput3(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(ndb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return; tctreeput3(ndb->tree, kbuf, ksiz, vbuf, vsiz); pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); } /* Store a new record into a on-memory tree database object without balancing nodes. */ bool tcndbputkeep3(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(ndb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return false; bool rv = tctreeputkeep3(ndb->tree, kbuf, ksiz, vbuf, vsiz); pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); return rv; } /* Concatenate a value in a on-memory tree database without balancing nodes. */ void tcndbputcat3(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){ assert(ndb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return; tctreeputcat3(ndb->tree, kbuf, ksiz, vbuf, vsiz); pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); } /* Store a record into a on-memory tree database object with a duplication handler. */ bool tcndbputproc(TCNDB *ndb, const void *kbuf, int ksiz, const void *vbuf, int vsiz, TCPDPROC proc, void *op){ assert(ndb && kbuf && ksiz >= 0 && proc); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return false; bool rv = tctreeputproc(ndb->tree, kbuf, ksiz, vbuf, vsiz, proc, op); pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); return rv; } /* Retrieve a record in an on-memory tree database object without balancing nodes. */ void *tcndbget3(TCNDB *ndb, const void *kbuf, int ksiz, int *sp){ assert(ndb && kbuf && ksiz >= 0 && sp); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return NULL; int vsiz; const char *vbuf = tctreeget3(ndb->tree, kbuf, ksiz, &vsiz); char *rv; if(vbuf){ TCMEMDUP(rv, vbuf, vsiz); *sp = vsiz; } else { rv = NULL; } pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); return rv; } /* Initialize the iterator of an on-memory tree database object in front of a key. */ void tcndbiterinit2(TCNDB *ndb, const void *kbuf, int ksiz){ assert(ndb && kbuf && ksiz >= 0); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return; tctreeiterinit2(ndb->tree, kbuf, ksiz); pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); } /* Initialize the iterator of an on-memory tree database object in front of a key string. */ void tcndbiterinit3(TCNDB *ndb, const char *kstr){ assert(ndb && kstr); tcndbiterinit2(ndb, kstr, strlen(kstr)); } /* Process each record atomically of an on-memory tree database object. */ void tcndbforeach(TCNDB *ndb, TCITER iter, void *op){ assert(ndb && iter); if(pthread_mutex_lock((pthread_mutex_t *)ndb->mmtx) != 0) return; TCTREE *tree = ndb->tree; TCTREEREC *cur = tree->cur; tctreeiterinit(tree); int ksiz; const char *kbuf; while((kbuf = tctreeiternext(tree, &ksiz)) != NULL){ int vsiz; const char *vbuf = tctreeiterval(kbuf, &vsiz); if(!iter(kbuf, ksiz, vbuf, vsiz, op)) break; } tree->cur = cur; pthread_mutex_unlock((pthread_mutex_t *)ndb->mmtx); } /************************************************************************************************* * memory pool *************************************************************************************************/ #define TCMPOOLUNIT 128 // allocation unit size of memory pool elements /* Global memory pool object. */ TCMPOOL *tcglobalmemorypool = NULL; /* private function prototypes */ static void tcmpooldelglobal(void); /* Create a memory pool object. */ TCMPOOL *tcmpoolnew(void){ TCMPOOL *mpool; TCMALLOC(mpool, sizeof(*mpool)); TCMALLOC(mpool->mutex, sizeof(pthread_mutex_t)); if(pthread_mutex_init(mpool->mutex, NULL) != 0) tcmyfatal("locking failed"); mpool->anum = TCMPOOLUNIT; TCMALLOC(mpool->elems, sizeof(mpool->elems[0]) * mpool->anum); mpool->num = 0; return mpool; } /* Delete a memory pool object. */ void tcmpooldel(TCMPOOL *mpool){ assert(mpool); TCMPELEM *elems = mpool->elems; for(int i = mpool->num - 1; i >= 0; i--){ elems[i].del(elems[i].ptr); } TCFREE(elems); pthread_mutex_destroy(mpool->mutex); TCFREE(mpool->mutex); TCFREE(mpool); } /* Relegate an arbitrary object to a memory pool object. */ void *tcmpoolpush(TCMPOOL *mpool, void *ptr, void (*del)(void *)){ assert(mpool && del); if(!ptr) return NULL; if(pthread_mutex_lock(mpool->mutex) != 0) tcmyfatal("locking failed"); int num = mpool->num; if(num >= mpool->anum){ mpool->anum *= 2; TCREALLOC(mpool->elems, mpool->elems, mpool->anum * sizeof(mpool->elems[0])); } mpool->elems[num].ptr = ptr; mpool->elems[num].del = del; mpool->num++; pthread_mutex_unlock(mpool->mutex); return ptr; } /* Relegate an allocated region to a memory pool object. */ void *tcmpoolpushptr(TCMPOOL *mpool, void *ptr){ assert(mpool); return tcmpoolpush(mpool, ptr, (void (*)(void *))free); } /* Relegate an extensible string object to a memory pool object. */ TCXSTR *tcmpoolpushxstr(TCMPOOL *mpool, TCXSTR *xstr){ assert(mpool); return tcmpoolpush(mpool, xstr, (void (*)(void *))tcxstrdel); } /* Relegate a list object to a memory pool object. */ TCLIST *tcmpoolpushlist(TCMPOOL *mpool, TCLIST *list){ assert(mpool); return tcmpoolpush(mpool, list, (void (*)(void *))tclistdel); } /* Relegate a map object to a memory pool object. */ TCMAP *tcmpoolpushmap(TCMPOOL *mpool, TCMAP *map){ assert(mpool); return tcmpoolpush(mpool, map, (void (*)(void *))tcmapdel); } /* Relegate a tree object to a memory pool object. */ TCTREE *tcmpoolpushtree(TCMPOOL *mpool, TCTREE *tree){ assert(mpool); return tcmpoolpush(mpool, tree, (void (*)(void *))tctreedel); } /* Allocate a region relegated to a memory pool object. */ void *tcmpoolmalloc(TCMPOOL *mpool, size_t size){ assert(mpool && size > 0); void *ptr; TCMALLOC(ptr, size); tcmpoolpush(mpool, ptr, (void (*)(void *))free); return ptr; } /* Create an extensible string object relegated to a memory pool object. */ TCXSTR *tcmpoolxstrnew(TCMPOOL *mpool){ assert(mpool); TCXSTR *xstr = tcxstrnew(); tcmpoolpush(mpool, xstr, (void (*)(void *))tcxstrdel); return xstr; } /* Create a list object relegated to a memory pool object. */ TCLIST *tcmpoollistnew(TCMPOOL *mpool){ assert(mpool); TCLIST *list = tclistnew(); tcmpoolpush(mpool, list, (void (*)(void *))tclistdel); return list; } /* Create a map object relegated to a memory pool object. */ TCMAP *tcmpoolmapnew(TCMPOOL *mpool){ assert(mpool); TCMAP *map = tcmapnew(); tcmpoolpush(mpool, map, (void (*)(void *))tcmapdel); return map; } /* Create a tree object relegated to a memory pool object. */ TCTREE *tcmpooltreenew(TCMPOOL *mpool){ assert(mpool); TCTREE *tree = tctreenew(); tcmpoolpush(mpool, tree, (void (*)(void *))tctreedel); return tree; } /* Remove the most recently installed cleanup handler of a memory pool object. */ void tcmpoolpop(TCMPOOL *mpool, bool exe){ assert(mpool); if(pthread_mutex_lock(mpool->mutex) != 0) tcmyfatal("locking failed"); if(mpool->num > 0){ mpool->num--; if(exe) mpool->elems[mpool->num].del(mpool->elems[mpool->num].ptr); } pthread_mutex_unlock(mpool->mutex); } /* Remove all cleanup handler of a memory pool object. `mpool' specifies the memory pool object. */ void tcmpoolclear(TCMPOOL *mpool, bool exe){ assert(mpool); if(pthread_mutex_lock(mpool->mutex) != 0) tcmyfatal("locking failed"); if(exe){ for(int i = mpool->num - 1; i >= 0; i--){ mpool->elems[i].del(mpool->elems[i].ptr); } } mpool->num = 0; pthread_mutex_unlock(mpool->mutex); } /* Get the global memory pool object. */ TCMPOOL *tcmpoolglobal(void){ if(tcglobalmemorypool) return tcglobalmemorypool; tcglobalmemorypool = tcmpoolnew(); atexit(tcmpooldelglobal); return tcglobalmemorypool; } /* Detete the global memory pool object. */ static void tcmpooldelglobal(void){ if(tcglobalmemorypool) tcmpooldel(tcglobalmemorypool); } /************************************************************************************************* * miscellaneous utilities *************************************************************************************************/ #define TCRANDDEV "/dev/urandom" // path of the random device file #define TCDISTMAXLEN 4096 // maximum size of a string for distance checking #define TCDISTBUFSIZ 16384 // size of a distance buffer #define TCLDBLCOLMAX 16 // maximum number of columns of the long double /* File descriptor of random number generator. */ int tcrandomdevfd = -1; /* private function prototypes */ static void tcrandomfdclose(void); static time_t tcmkgmtime(struct tm *tm); /* Get the larger value of two integers. */ long tclmax(long a, long b){ return (a > b) ? a : b; } /* Get the lesser value of two integers. */ long tclmin(long a, long b){ return (a < b) ? a : b; } /* Get a random number as long integer based on uniform distribution. */ unsigned long tclrand(void){ static uint32_t cnt = 0; static uint64_t seed = 0; static uint64_t mask = 0; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; if((cnt & 0xff) == 0 && pthread_mutex_lock(&mutex) == 0){ if(cnt == 0) seed += time(NULL); if(tcrandomdevfd == -1 && (tcrandomdevfd = open(TCRANDDEV, O_RDONLY, 00644)) != -1) atexit(tcrandomfdclose); if(tcrandomdevfd == -1 || read(tcrandomdevfd, &mask, sizeof(mask)) != sizeof(mask)){ double t = tctime(); uint64_t tmask; memcpy(&tmask, &t, tclmin(sizeof(t), sizeof(tmask))); mask = (mask << 8) ^ tmask; } pthread_mutex_unlock(&mutex); } seed = seed * 123456789012301LL + 211; uint64_t num = (mask ^ cnt++) ^ seed; return TCSWAB64(num); } /* Get a random number as double decimal based on uniform distribution. */ double tcdrand(void){ double val = tclrand() / (double)ULONG_MAX; return val < 1.0 ? val : 0.0; } /* Get a random number as double decimal based on normal distribution. */ double tcdrandnd(double avg, double sd){ assert(sd >= 0.0); return sqrt(-2.0 * log(tcdrand())) * cos(2 * 3.141592653589793 * tcdrand()) * sd + avg; } /* Compare two strings with case insensitive evaluation. */ int tcstricmp(const char *astr, const char *bstr){ assert(astr && bstr); while(*astr != '\0'){ if(*bstr == '\0') return 1; int ac = (*astr >= 'A' && *astr <= 'Z') ? *astr + ('a' - 'A') : *(unsigned char *)astr; int bc = (*bstr >= 'A' && *bstr <= 'Z') ? *bstr + ('a' - 'A') : *(unsigned char *)bstr; if(ac != bc) return ac - bc; astr++; bstr++; } return (*bstr == '\0') ? 0 : -1; } /* Check whether a string begins with a key. */ bool tcstrfwm(const char *str, const char *key){ assert(str && key); while(*key != '\0'){ if(*str != *key || *str == '\0') return false; key++; str++; } return true; } /* Check whether a string begins with a key with case insensitive evaluation. */ bool tcstrifwm(const char *str, const char *key){ assert(str && key); while(*key != '\0'){ if(*str == '\0') return false; int sc = *str; if(sc >= 'A' && sc <= 'Z') sc += 'a' - 'A'; int kc = *key; if(kc >= 'A' && kc <= 'Z') kc += 'a' - 'A'; if(sc != kc) return false; key++; str++; } return true; } /* Check whether a string ends with a key. */ bool tcstrbwm(const char *str, const char *key){ assert(str && key); int slen = strlen(str); int klen = strlen(key); for(int i = 1; i <= klen; i++){ if(i > slen || str[slen-i] != key[klen-i]) return false; } return true; } /* Check whether a string ends with a key with case insensitive evaluation. */ bool tcstribwm(const char *str, const char *key){ assert(str && key); int slen = strlen(str); int klen = strlen(key); for(int i = 1; i <= klen; i++){ if(i > slen) return false; int sc = str[slen-i]; if(sc >= 'A' && sc <= 'Z') sc += 'a' - 'A'; int kc = key[klen-i]; if(kc >= 'A' && kc <= 'Z') kc += 'a' - 'A'; if(sc != kc) return false; } return true; } /* Calculate the edit distance of two strings. */ int tcstrdist(const char *astr, const char *bstr){ assert(astr && bstr); int alen = tclmin(strlen(astr), TCDISTMAXLEN); int blen = tclmin(strlen(bstr), TCDISTMAXLEN); int dsiz = blen + 1; int tbuf[TCDISTBUFSIZ]; int *tbl; if((alen + 1) * dsiz < TCDISTBUFSIZ){ tbl = tbuf; } else { TCMALLOC(tbl, (alen + 1) * dsiz * sizeof(*tbl)); } for(int i = 0; i <= alen; i++){ tbl[i*dsiz] = i; } for(int i = 1; i <= blen; i++){ tbl[i] = i; } astr--; bstr--; for(int i = 1; i <= alen; i++){ for(int j = 1; j <= blen; j++){ int ac = tbl[(i-1)*dsiz+j] + 1; int bc = tbl[i*dsiz+j-1] + 1; int cc = tbl[(i-1)*dsiz+j-1] + (astr[i] != bstr[j]); ac = ac < bc ? ac : bc; tbl[i*dsiz+j] = ac < cc ? ac : cc; } } int rv = tbl[alen*dsiz+blen]; if(tbl != tbuf) TCFREE(tbl); return rv; } /* Calculate the edit distance of two UTF-8 strings. */ int tcstrdistutf(const char *astr, const char *bstr){ assert(astr && bstr); int alen = strlen(astr); uint16_t abuf[TCDISTBUFSIZ]; uint16_t *aary; if(alen < TCDISTBUFSIZ){ aary = abuf; } else { TCMALLOC(aary, alen * sizeof(*aary)); } tcstrutftoucs(astr, aary, &alen); int blen = strlen(bstr); uint16_t bbuf[TCDISTBUFSIZ]; uint16_t *bary; if(blen < TCDISTBUFSIZ){ bary = bbuf; } else { TCMALLOC(bary, blen * sizeof(*bary)); } tcstrutftoucs(bstr, bary, &blen); if(alen > TCDISTMAXLEN) alen = TCDISTMAXLEN; if(blen > TCDISTMAXLEN) blen = TCDISTMAXLEN; int dsiz = blen + 1; int tbuf[TCDISTBUFSIZ]; int *tbl; if((alen + 1) * dsiz < TCDISTBUFSIZ){ tbl = tbuf; } else { TCMALLOC(tbl, (alen + 1) * dsiz * sizeof(*tbl)); } for(int i = 0; i <= alen; i++){ tbl[i*dsiz] = i; } for(int i = 1; i <= blen; i++){ tbl[i] = i; } aary--; bary--; for(int i = 1; i <= alen; i++){ for(int j = 1; j <= blen; j++){ int ac = tbl[(i-1)*dsiz+j] + 1; int bc = tbl[i*dsiz+j-1] + 1; int cc = tbl[(i-1)*dsiz+j-1] + (aary[i] != bary[j]); ac = ac < bc ? ac : bc; tbl[i*dsiz+j] = ac < cc ? ac : cc; } } aary++; bary++; int rv = tbl[alen*dsiz+blen]; if(tbl != tbuf) TCFREE(tbl); if(bary != bbuf) TCFREE(bary); if(aary != abuf) TCFREE(aary); return rv; } /* Convert the letters of a string into upper case. */ char *tcstrtoupper(char *str){ assert(str); char *wp = str; while(*wp != '\0'){ if(*wp >= 'a' && *wp <= 'z') *wp -= 'a' - 'A'; wp++; } return str; } /* Convert the letters of a string into lower case. */ char *tcstrtolower(char *str){ assert(str); char *wp = str; while(*wp != '\0'){ if(*wp >= 'A' && *wp <= 'Z') *wp += 'a' - 'A'; wp++; } return str; } /* Cut space characters at head or tail of a string. */ char *tcstrtrim(char *str){ assert(str); const char *rp = str; char *wp = str; bool head = true; while(*rp != '\0'){ if(*rp > '\0' && *rp <= ' '){ if(!head) *(wp++) = *rp; } else { *(wp++) = *rp; head = false; } rp++; } *wp = '\0'; while(wp > str && wp[-1] > '\0' && wp[-1] <= ' '){ *(--wp) = '\0'; } return str; } /* Squeeze space characters in a string and trim it. */ char *tcstrsqzspc(char *str){ assert(str); char *rp = str; char *wp = str; bool spc = true; while(*rp != '\0'){ if(*rp > 0 && *rp <= ' '){ if(!spc) *(wp++) = *rp; spc = true; } else { *(wp++) = *rp; spc = false; } rp++; } *wp = '\0'; for(wp--; wp >= str; wp--){ if(*wp > 0 && *wp <= ' '){ *wp = '\0'; } else { break; } } return str; } /* Substitute characters in a string. */ char *tcstrsubchr(char *str, const char *rstr, const char *sstr){ assert(str && rstr && sstr); int slen = strlen(sstr); char *wp = str; for(int i = 0; str[i] != '\0'; i++){ const char *p = strchr(rstr, str[i]); if(p){ int idx = p - rstr; if(idx < slen) *(wp++) = sstr[idx]; } else { *(wp++) = str[i]; } } *wp = '\0'; return str; } /* Count the number of characters in a string of UTF-8. */ int tcstrcntutf(const char *str){ assert(str); const unsigned char *rp = (unsigned char *)str; int cnt = 0; while(*rp != '\0'){ if((*rp & 0x80) == 0x00 || (*rp & 0xe0) == 0xc0 || (*rp & 0xf0) == 0xe0 || (*rp & 0xf8) == 0xf0) cnt++; rp++; } return cnt; } /* Cut a string of UTF-8 at the specified number of characters. */ char *tcstrcututf(char *str, int num){ assert(str && num >= 0); unsigned char *wp = (unsigned char *)str; int cnt = 0; while(*wp != '\0'){ if((*wp & 0x80) == 0x00 || (*wp & 0xe0) == 0xc0 || (*wp & 0xf0) == 0xe0 || (*wp & 0xf8) == 0xf0){ cnt++; if(cnt > num){ *wp = '\0'; break; } } wp++; } return str; } /* Convert a UTF-8 string into a UCS-2 array. */ void tcstrutftoucs(const char *str, uint16_t *ary, int *np){ assert(str && ary && np); const unsigned char *rp = (unsigned char *)str; unsigned int wi = 0; while(*rp != '\0'){ int c = *(unsigned char *)rp; if(c < 0x80){ ary[wi++] = c; } else if(c < 0xe0){ if(rp[1] >= 0x80){ ary[wi++] = ((rp[0] & 0x1f) << 6) | (rp[1] & 0x3f); rp++; } } else if(c < 0xf0){ if(rp[1] >= 0x80 && rp[2] >= 0x80){ ary[wi++] = ((rp[0] & 0xf) << 12) | ((rp[1] & 0x3f) << 6) | (rp[2] & 0x3f); rp += 2; } } rp++; } *np = wi; } /* Convert a UCS-2 array into a UTF-8 string. */ int tcstrucstoutf(const uint16_t *ary, int num, char *str){ assert(ary && num >= 0 && str); unsigned char *wp = (unsigned char *)str; for(int i = 0; i < num; i++){ unsigned int c = ary[i]; if(c < 0x80){ *(wp++) = c; } else if(c < 0x800){ *(wp++) = 0xc0 | (c >> 6); *(wp++) = 0x80 | (c & 0x3f); } else { *(wp++) = 0xe0 | (c >> 12); *(wp++) = 0x80 | ((c & 0xfff) >> 6); *(wp++) = 0x80 | (c & 0x3f); } } *wp = '\0'; return (char *)wp - str; } /* Create a list object by splitting a string. */ TCLIST *tcstrsplit(const char *str, const char *delims){ assert(str && delims); TCLIST *list = tclistnew(); while(true){ const char *sp = str; while(*str != '\0' && !strchr(delims, *str)){ str++; } TCLISTPUSH(list, sp, str - sp); if(*str == '\0') break; str++; } return list; } /* Create a string by joining all elements of a list object. */ char *tcstrjoin(const TCLIST *list, char delim){ assert(list); int num = TCLISTNUM(list); int size = num + 1; for(int i = 0; i < num; i++){ size += TCLISTVALSIZ(list, i); } char *buf; TCMALLOC(buf, size); char *wp = buf; for(int i = 0; i < num; i++){ if(i > 0) *(wp++) = delim; int vsiz; const char *vbuf = tclistval(list, i, &vsiz); memcpy(wp, vbuf, vsiz); wp += vsiz; } *wp = '\0'; return buf; } /* Convert a string to an integer. */ int64_t tcatoi(const char *str){ assert(str); while(*str > '\0' && *str <= ' '){ str++; } int sign = 1; int64_t num = 0; if(*str == '-'){ str++; sign = -1; } else if(*str == '+'){ str++; } while(*str != '\0'){ if(*str < '0' || *str > '9') break; num = num * 10 + *str - '0'; str++; } return num * sign; } /* Convert a string with a metric prefix to an integer. */ int64_t tcatoix(const char *str){ assert(str); while(*str > '\0' && *str <= ' '){ str++; } int sign = 1; if(*str == '-'){ str++; sign = -1; } else if(*str == '+'){ str++; } long double num = 0; while(*str != '\0'){ if(*str < '0' || *str > '9') break; num = num * 10 + *str - '0'; str++; } if(*str == '.'){ str++; long double base = 10; while(*str != '\0'){ if(*str < '0' || *str > '9') break; num += (*str - '0') / base; str++; base *= 10; } } num *= sign; while(*str > '\0' && *str <= ' '){ str++; } if(*str == 'k' || *str == 'K'){ num *= 1LL << 10; } else if(*str == 'm' || *str == 'M'){ num *= 1LL << 20; } else if(*str == 'g' || *str == 'G'){ num *= 1LL << 30; } else if(*str == 't' || *str == 'T'){ num *= 1LL << 40; } else if(*str == 'p' || *str == 'P'){ num *= 1LL << 50; } else if(*str == 'e' || *str == 'E'){ num *= 1LL << 60; } if(num > INT64_MAX) return INT64_MAX; if(num < INT64_MIN) return INT64_MIN; return num; } /* Convert a string to a real number. */ double tcatof(const char *str){ assert(str); while(*str > '\0' && *str <= ' '){ str++; } int sign = 1; if(*str == '-'){ str++; sign = -1; } else if(*str == '+'){ str++; } if(tcstrifwm(str, "inf")) return HUGE_VAL * sign; if(tcstrifwm(str, "nan")) return nan(""); long double num = 0; int col = 0; while(*str != '\0'){ if(*str < '0' || *str > '9') break; num = num * 10 + *str - '0'; str++; if(num > 0) col++; } if(*str == '.'){ str++; long double fract = 0.0; long double base = 10; while(col < TCLDBLCOLMAX && *str != '\0'){ if(*str < '0' || *str > '9') break; fract += (*str - '0') / base; str++; col++; base *= 10; } num += fract; } if(*str == 'e' || *str == 'E'){ str++; num *= pow(10, tcatoi(str)); } return num * sign; } /* Check whether a string matches a regular expression. */ bool tcregexmatch(const char *str, const char *regex){ assert(str && regex); int options = REG_EXTENDED | REG_NOSUB; if(*regex == '*'){ options |= REG_ICASE; regex++; } regex_t rbuf; if(regcomp(&rbuf, regex, options) != 0) return false; bool rv = regexec(&rbuf, str, 0, NULL, 0) == 0; regfree(&rbuf); return rv; } /* Replace each substring matching a regular expression string. */ char *tcregexreplace(const char *str, const char *regex, const char *alt){ assert(str && regex && alt); int options = REG_EXTENDED; if(*regex == '*'){ options |= REG_ICASE; regex++; } regex_t rbuf; if(regex[0] == '\0' || regcomp(&rbuf, regex, options) != 0) return tcstrdup(str); regmatch_t subs[256]; if(regexec(&rbuf, str, 32, subs, 0) != 0){ regfree(&rbuf); return tcstrdup(str); } const char *sp = str; TCXSTR *xstr = tcxstrnew(); bool first = true; while(sp[0] != '\0' && regexec(&rbuf, sp, 10, subs, first ? 0 : REG_NOTBOL) == 0){ first = false; if(subs[0].rm_so == -1) break; tcxstrcat(xstr, sp, subs[0].rm_so); for(const char *rp = alt; *rp != '\0'; rp++){ if(*rp == '\\'){ if(rp[1] >= '0' && rp[1] <= '9'){ int num = rp[1] - '0'; if(subs[num].rm_so != -1 && subs[num].rm_eo != -1) tcxstrcat(xstr, sp + subs[num].rm_so, subs[num].rm_eo - subs[num].rm_so); ++rp; } else if(rp[1] != '\0'){ tcxstrcat(xstr, ++rp, 1); } } else if(*rp == '&'){ tcxstrcat(xstr, sp + subs[0].rm_so, subs[0].rm_eo - subs[0].rm_so); } else { tcxstrcat(xstr, rp, 1); } } sp += subs[0].rm_eo; if(subs[0].rm_eo < 1) break; } tcxstrcat2(xstr, sp); regfree(&rbuf); return tcxstrtomalloc(xstr); } /* Get the MD5 hash value of a serial object. */ void tcmd5hash(const void *ptr, int size, char *buf){ assert(ptr && size >= 0 && buf); int i; md5_state_t ms; md5_init(&ms); md5_append(&ms, (md5_byte_t *)ptr, size); unsigned char digest[16]; md5_finish(&ms, (md5_byte_t *)digest); char *wp = buf; for(i = 0; i < 16; i++){ wp += sprintf(wp, "%02x", digest[i]); } *wp = '\0'; } /* 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){ assert(ptr && size >= 0 && kbuf && ksiz >= 0 && obuf); if(ksiz < 1){ kbuf = ""; ksiz = 1; } uint32_t sbox[0x100], kbox[0x100]; for(int i = 0; i < 0x100; i++){ sbox[i] = i; kbox[i] = ((uint8_t *)kbuf)[i%ksiz]; } int sidx = 0; for(int i = 0; i < 0x100; i++){ sidx = (sidx + sbox[i] + kbox[i]) & 0xff; uint32_t swap = sbox[i]; sbox[i] = sbox[sidx]; sbox[sidx] = swap; } int x = 0; int y = 0; for(int i = 0; i < size; i++){ x = (x + 1) & 0xff; y = (y + sbox[x]) & 0xff; int32_t swap = sbox[x]; sbox[x] = sbox[y]; sbox[y] = swap; ((uint8_t *)obuf)[i] = ((uint8_t *)ptr)[i] ^ sbox[(sbox[x]+sbox[y])&0xff]; } } /* Get the time of day in seconds. */ double tctime(void){ struct timeval tv; if(gettimeofday(&tv, NULL) == -1) return 0.0; return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; } /* 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){ if(t == INT64_MAX) t = time(NULL); if(jl == INT_MAX) jl = tcjetlag(); time_t tt = (time_t)t + jl; struct tm ts; if(!gmtime_r(&tt, &ts)){ if(yearp) *yearp = 0; if(monp) *monp = 0; if(dayp) *dayp = 0; if(hourp) *hourp = 0; if(minp) *minp = 0; if(secp) *secp = 0; } if(yearp) *yearp = ts.tm_year + 1900; if(monp) *monp = ts.tm_mon + 1; if(dayp) *dayp = ts.tm_mday; if(hourp) *hourp = ts.tm_hour; if(minp) *minp = ts.tm_min; if(secp) *secp = ts.tm_sec; } /* Format a date as a string in W3CDTF. */ void tcdatestrwww(int64_t t, int jl, char *buf){ assert(buf); if(t == INT64_MAX) t = time(NULL); if(jl == INT_MAX) jl = tcjetlag(); time_t tt = (time_t)t + jl; struct tm ts; if(!gmtime_r(&tt, &ts)) memset(&ts, 0, sizeof(ts)); ts.tm_year += 1900; ts.tm_mon += 1; jl /= 60; char tzone[16]; if(jl == 0){ sprintf(tzone, "Z"); } else if(jl < 0){ jl *= -1; sprintf(tzone, "-%02d:%02d", jl / 60, jl % 60); } else { sprintf(tzone, "+%02d:%02d", jl / 60, jl % 60); } sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%s", ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, tzone); } /* Format a date as a string in RFC 1123 format. */ void tcdatestrhttp(int64_t t, int jl, char *buf){ assert(buf); if(t == INT64_MAX) t = time(NULL); if(jl == INT_MAX) jl = tcjetlag(); time_t tt = (time_t)t + jl; struct tm ts; if(!gmtime_r(&tt, &ts)) memset(&ts, 0, sizeof(ts)); ts.tm_year += 1900; ts.tm_mon += 1; jl /= 60; char *wp = buf; switch(tcdayofweek(ts.tm_year, ts.tm_mon, ts.tm_mday)){ case 0: wp += sprintf(wp, "Sun, "); break; case 1: wp += sprintf(wp, "Mon, "); break; case 2: wp += sprintf(wp, "Tue, "); break; case 3: wp += sprintf(wp, "Wed, "); break; case 4: wp += sprintf(wp, "Thu, "); break; case 5: wp += sprintf(wp, "Fri, "); break; case 6: wp += sprintf(wp, "Sat, "); break; } wp += sprintf(wp, "%02d ", ts.tm_mday); switch(ts.tm_mon){ case 1: wp += sprintf(wp, "Jan "); break; case 2: wp += sprintf(wp, "Feb "); break; case 3: wp += sprintf(wp, "Mar "); break; case 4: wp += sprintf(wp, "Apr "); break; case 5: wp += sprintf(wp, "May "); break; case 6: wp += sprintf(wp, "Jun "); break; case 7: wp += sprintf(wp, "Jul "); break; case 8: wp += sprintf(wp, "Aug "); break; case 9: wp += sprintf(wp, "Sep "); break; case 10: wp += sprintf(wp, "Oct "); break; case 11: wp += sprintf(wp, "Nov "); break; case 12: wp += sprintf(wp, "Dec "); break; } wp += sprintf(wp, "%04d %02d:%02d:%02d ", ts.tm_year, ts.tm_hour, ts.tm_min, ts.tm_sec); if(jl == 0){ sprintf(wp, "GMT"); } else if(jl < 0){ jl *= -1; sprintf(wp, "-%02d%02d", jl / 60, jl % 60); } else { sprintf(wp, "+%02d%02d", jl / 60, jl % 60); } } /* Get the time value of a date string. */ int64_t tcstrmktime(const char *str){ assert(str); while(*str > '\0' && *str <= ' '){ str++; } if(*str == '\0') return INT64_MIN; if(str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) return tcatoih(str + 2); struct tm ts; memset(&ts, 0, sizeof(ts)); ts.tm_year = 70; ts.tm_mon = 0; ts.tm_mday = 1; ts.tm_hour = 0; ts.tm_min = 0; ts.tm_sec = 0; ts.tm_isdst = 0; int len = strlen(str); time_t t = (time_t)tcatoi(str); const char *pv = str; while(*pv >= '0' && *pv <= '9'){ pv++; } while(*pv > '\0' && *pv <= ' '){ pv++; } if(*pv == '\0') return (int64_t)t; if((pv[0] == 's' || pv[0] == 'S') && pv[1] >= '\0' && pv[1] <= ' ') return (int64_t)t; if((pv[0] == 'm' || pv[0] == 'M') && pv[1] >= '\0' && pv[1] <= ' ') return (int64_t)t * 60; if((pv[0] == 'h' || pv[0] == 'H') && pv[1] >= '\0' && pv[1] <= ' ') return (int64_t)t * 60 * 60; if((pv[0] == 'd' || pv[0] == 'D') && pv[1] >= '\0' && pv[1] <= ' ') return (int64_t)t * 60 * 60 * 24; if(len > 4 && str[4] == '-'){ ts.tm_year = tcatoi(str) - 1900; if((pv = strchr(str, '-')) != NULL && pv - str == 4){ const char *rp = pv + 1; ts.tm_mon = tcatoi(rp) - 1; if((pv = strchr(rp, '-')) != NULL && pv - str == 7){ rp = pv + 1; ts.tm_mday = tcatoi(rp); if((pv = strchr(rp, 'T')) != NULL && pv - str == 10){ rp = pv + 1; ts.tm_hour = tcatoi(rp); if((pv = strchr(rp, ':')) != NULL && pv - str == 13){ rp = pv + 1; ts.tm_min = tcatoi(rp); } if((pv = strchr(rp, ':')) != NULL && pv - str == 16){ rp = pv + 1; ts.tm_sec = tcatoi(rp); } if((pv = strchr(rp, '.')) != NULL && pv - str >= 19) rp = pv + 1; pv = rp; while(*pv >= '0' && *pv <= '9'){ pv++; } if((*pv == '+' || *pv == '-') && strlen(pv) >= 6 && pv[3] == ':') ts.tm_sec -= (tcatoi(pv + 1) * 3600 + tcatoi(pv + 4) * 60) * (pv[0] == '+' ? 1 : -1); } } } return (int64_t)tcmkgmtime(&ts); } if(len > 4 && str[4] == '/'){ ts.tm_year = tcatoi(str) - 1900; if((pv = strchr(str, '/')) != NULL && pv - str == 4){ const char *rp = pv + 1; ts.tm_mon = tcatoi(rp) - 1; if((pv = strchr(rp, '/')) != NULL && pv - str == 7){ rp = pv + 1; ts.tm_mday = tcatoi(rp); if((pv = strchr(rp, ' ')) != NULL && pv - str == 10){ rp = pv + 1; ts.tm_hour = tcatoi(rp); if((pv = strchr(rp, ':')) != NULL && pv - str == 13){ rp = pv + 1; ts.tm_min = tcatoi(rp); } if((pv = strchr(rp, ':')) != NULL && pv - str == 16){ rp = pv + 1; ts.tm_sec = tcatoi(rp); } if((pv = strchr(rp, '.')) != NULL && pv - str >= 19) rp = pv + 1; pv = rp; while(*pv >= '0' && *pv <= '9'){ pv++; } if((*pv == '+' || *pv == '-') && strlen(pv) >= 6 && pv[3] == ':') ts.tm_sec -= (tcatoi(pv + 1) * 3600 + tcatoi(pv + 4) * 60) * (pv[0] == '+' ? 1 : -1); } } } return (int64_t)tcmkgmtime(&ts); } const char *crp = str; if(len >= 4 && str[3] == ',') crp = str + 4; while(*crp == ' '){ crp++; } ts.tm_mday = tcatoi(crp); while((*crp >= '0' && *crp <= '9') || *crp == ' '){ crp++; } if(tcstrifwm(crp, "Jan")){ ts.tm_mon = 0; } else if(tcstrifwm(crp, "Feb")){ ts.tm_mon = 1; } else if(tcstrifwm(crp, "Mar")){ ts.tm_mon = 2; } else if(tcstrifwm(crp, "Apr")){ ts.tm_mon = 3; } else if(tcstrifwm(crp, "May")){ ts.tm_mon = 4; } else if(tcstrifwm(crp, "Jun")){ ts.tm_mon = 5; } else if(tcstrifwm(crp, "Jul")){ ts.tm_mon = 6; } else if(tcstrifwm(crp, "Aug")){ ts.tm_mon = 7; } else if(tcstrifwm(crp, "Sep")){ ts.tm_mon = 8; } else if(tcstrifwm(crp, "Oct")){ ts.tm_mon = 9; } else if(tcstrifwm(crp, "Nov")){ ts.tm_mon = 10; } else if(tcstrifwm(crp, "Dec")){ ts.tm_mon = 11; } else { ts.tm_mon = -1; } if(ts.tm_mon >= 0) crp += 3; while(*crp == ' '){ crp++; } ts.tm_year = tcatoi(crp); if(ts.tm_year >= 1969) ts.tm_year -= 1900; while(*crp >= '0' && *crp <= '9'){ crp++; } while(*crp == ' '){ crp++; } if(ts.tm_mday > 0 && ts.tm_mon >= 0 && ts.tm_year >= 0){ int clen = strlen(crp); if(clen >= 8 && crp[2] == ':' && crp[5] == ':'){ ts.tm_hour = tcatoi(crp + 0); ts.tm_min = tcatoi(crp + 3); ts.tm_sec = tcatoi(crp + 6); if(clen >= 14 && crp[8] == ' ' && (crp[9] == '+' || crp[9] == '-')){ ts.tm_sec -= ((crp[10] - '0') * 36000 + (crp[11] - '0') * 3600 + (crp[12] - '0') * 600 + (crp[13] - '0') * 60) * (crp[9] == '+' ? 1 : -1); } else if(clen > 9){ if(!strcmp(crp + 9, "JST")){ ts.tm_sec -= 9 * 3600; } else if(!strcmp(crp + 9, "CCT")){ ts.tm_sec -= 8 * 3600; } else if(!strcmp(crp + 9, "KST")){ ts.tm_sec -= 9 * 3600; } else if(!strcmp(crp + 9, "EDT")){ ts.tm_sec -= -4 * 3600; } else if(!strcmp(crp + 9, "EST")){ ts.tm_sec -= -5 * 3600; } else if(!strcmp(crp + 9, "CDT")){ ts.tm_sec -= -5 * 3600; } else if(!strcmp(crp + 9, "CST")){ ts.tm_sec -= -6 * 3600; } else if(!strcmp(crp + 9, "MDT")){ ts.tm_sec -= -6 * 3600; } else if(!strcmp(crp + 9, "MST")){ ts.tm_sec -= -7 * 3600; } else if(!strcmp(crp + 9, "PDT")){ ts.tm_sec -= -7 * 3600; } else if(!strcmp(crp + 9, "PST")){ ts.tm_sec -= -8 * 3600; } else if(!strcmp(crp + 9, "HDT")){ ts.tm_sec -= -9 * 3600; } else if(!strcmp(crp + 9, "HST")){ ts.tm_sec -= -10 * 3600; } } } return (int64_t)tcmkgmtime(&ts); } return INT64_MIN; } /* Get the jet lag of the local time. */ int tcjetlag(void){ #if defined(_SYS_LINUX_) tzset(); return -timezone; #else time_t t = 86400; struct tm gts; if(!gmtime_r(&t, >s)) return 0; struct tm lts; t = 86400; if(!localtime_r(&t, <s)) return 0; return mktime(<s) - mktime(>s); #endif } /* Get the day of week of a date. */ int tcdayofweek(int year, int mon, int day){ if(mon < 3){ year--; mon += 12; } return (day + ((8 + (13 * mon)) / 5) + (year + (year / 4) - (year / 100) + (year / 400))) % 7; } /* Close the random number generator. */ static void tcrandomfdclose(void){ close(tcrandomdevfd); } /* Make the GMT from a time structure. `tm' specifies the pointer to the time structure. The return value is the GMT. */ static time_t tcmkgmtime(struct tm *tm){ #if defined(_SYS_LINUX_) assert(tm); return timegm(tm); #else assert(tm); return mktime(tm) + tcjetlag(); #endif } /************************************************************************************************* * miscellaneous utilities (for experts) *************************************************************************************************/ #define TCCHIDXVNNUM 128 // number of virtual node of consistent hashing /* private function prototypes */ static int tcstrutfkwicputtext(const uint16_t *oary, const uint16_t *nary, int si, int ti, int end, char *buf, const TCLIST *uwords, int opts); static int tcchidxcmp(const void *a, const void *b); /* Check whether a string is numeric completely or not. */ bool tcstrisnum(const char *str){ assert(str); bool isnum = false; while(*str > '\0' && *str <= ' '){ str++; } if(*str == '-') str++; while(*str >= '0' && *str <= '9'){ isnum = true; str++; } if(*str == '.') str++; while(*str >= '0' && *str <= '9'){ isnum = true; str++; } while(*str > '\0' && *str <= ' '){ str++; } return isnum && *str == '\0'; } /* Convert a hexadecimal string to an integer. */ int64_t tcatoih(const char *str){ assert(str); while(*str > '\0' && *str <= ' '){ str++; } if(str[0] == '0' && (str[1] == 'x' || str[1] == 'X')){ str += 2; } int64_t num = 0; while(true){ if(*str >= '0' && *str <= '9'){ num = num * 0x10 + *str - '0'; } else if(*str >= 'a' && *str <= 'f'){ num = num * 0x10 + *str - 'a' + 10; } else if(*str >= 'A' && *str <= 'F'){ num = num * 0x10 + *str - 'A' + 10; } else { break; } str++; } return num; } /* Skip space characters at head of a string. */ const char *tcstrskipspc(const char *str){ assert(str); while(*str > '\0' && *str <= ' '){ str++; } return str; } /* Normalize a UTF-8 string. */ char *tcstrutfnorm(char *str, int opts){ assert(str); uint16_t buf[TCDISTBUFSIZ]; uint16_t *ary; int len = strlen(str); if(len < TCDISTBUFSIZ){ ary = buf; } else { TCMALLOC(ary, len * sizeof(*ary)); } int num; tcstrutftoucs(str, ary, &num); num = tcstrucsnorm(ary, num, opts); tcstrucstoutf(ary, num, str); if(ary != buf) TCFREE(ary); return str; } /* Normalize a UCS-2 array. */ int tcstrucsnorm(uint16_t *ary, int num, int opts){ assert(ary && num >= 0); bool spcmode = opts & TCUNSPACE; bool lowmode = opts & TCUNLOWER; bool nacmode = opts & TCUNNOACC; bool widmode = opts & TCUNWIDTH; int wi = 0; for(int i = 0; i < num; i++){ int c = ary[i]; int high = c >> 8; if(high == 0x00){ if(c <= 0x0020 || c == 0x007f){ // control characters if(spcmode){ ary[wi++] = 0x0020; if(wi < 2 || ary[wi-2] == 0x0020) wi--; } else if(c == 0x0009 || c == 0x000a || c == 0x000d){ ary[wi++] = c; } else { ary[wi++] = 0x0020; } } else if(c == 0x00a0){ // no-break space if(spcmode){ ary[wi++] = 0x0020; if(wi < 2 || ary[wi-2] == 0x0020) wi--; } else { ary[wi++] = c; } } else { // otherwise if(lowmode){ if(c < 0x007f){ if(c >= 0x0041 && c <= 0x005a) c += 0x20; } else if(c >= 0x00c0 && c <= 0x00de && c != 0x00d7){ c += 0x20; } } if(nacmode){ if(c >= 0x00c0 && c <= 0x00c5){ c = 'A'; } else if(c == 0x00c7){ c = 'C'; } if(c >= 0x00c7 && c <= 0x00cb){ c = 'E'; } if(c >= 0x00cc && c <= 0x00cf){ c = 'I'; } else if(c == 0x00d0){ c = 'D'; } else if(c == 0x00d1){ c = 'N'; } if((c >= 0x00d2 && c <= 0x00d6) || c == 0x00d8){ c = 'O'; } if(c >= 0x00d9 && c <= 0x00dc){ c = 'U'; } if(c == 0x00dd || c == 0x00de){ c = 'Y'; } else if(c == 0x00df){ c = 's'; } else if(c >= 0x00e0 && c <= 0x00e5){ c = 'a'; } else if(c == 0x00e7){ c = 'c'; } if(c >= 0x00e7 && c <= 0x00eb){ c = 'e'; } if(c >= 0x00ec && c <= 0x00ef){ c = 'i'; } else if(c == 0x00f0){ c = 'd'; } else if(c == 0x00f1){ c = 'n'; } if((c >= 0x00f2 && c <= 0x00f6) || c == 0x00f8){ c = 'o'; } if(c >= 0x00f9 && c <= 0x00fc){ c = 'u'; } if(c >= 0x00fd && c <= 0x00ff){ c = 'y'; } } ary[wi++] = c; } } else if(high == 0x01){ // latin-1 extended if(lowmode){ if(c <= 0x0137){ if((c & 1) == 0) c++; } else if(c == 0x0138){ c += 0; } else if(c <= 0x0148){ if((c & 1) == 1) c++; } else if(c == 0x0149){ c += 0; } else if(c <= 0x0177){ if((c & 1) == 0) c++; } else if(c == 0x0178){ c = 0x00ff; } else if(c <= 0x017e){ if((c & 1) == 1) c++; } else if(c == 0x017f){ c += 0; } } if(nacmode){ if(c == 0x00ff){ c = 'y'; } else if(c <= 0x0105){ c = ((c & 1) == 0) ? 'A' : 'a'; } else if(c <= 0x010d){ c = ((c & 1) == 0) ? 'C' : 'c'; } else if(c <= 0x0111){ c = ((c & 1) == 0) ? 'D' : 'd'; } else if(c <= 0x011b){ c = ((c & 1) == 0) ? 'E' : 'e'; } else if(c <= 0x0123){ c = ((c & 1) == 0) ? 'G' : 'g'; } else if(c <= 0x0127){ c = ((c & 1) == 0) ? 'H' : 'h'; } else if(c <= 0x0131){ c = ((c & 1) == 0) ? 'I' : 'i'; } else if(c == 0x0134){ c = 'J'; } else if(c == 0x0135){ c = 'j'; } else if(c == 0x0136){ c = 'K'; } else if(c == 0x0137){ c = 'k'; } else if(c == 0x0138){ c = 'k'; } else if(c >= 0x0139 && c <= 0x0142){ c = ((c & 1) == 1) ? 'L' : 'l'; } else if(c >= 0x0143 && c <= 0x0148){ c = ((c & 1) == 1) ? 'N' : 'n'; } else if(c >= 0x0149 && c <= 0x014b){ c = ((c & 1) == 0) ? 'N' : 'n'; } else if(c >= 0x014c && c <= 0x0151){ c = ((c & 1) == 0) ? 'O' : 'o'; } else if(c >= 0x0154 && c <= 0x0159){ c = ((c & 1) == 0) ? 'R' : 'r'; } else if(c >= 0x015a && c <= 0x0161){ c = ((c & 1) == 0) ? 'S' : 's'; } else if(c >= 0x0162 && c <= 0x0167){ c = ((c & 1) == 0) ? 'T' : 't'; } else if(c >= 0x0168 && c <= 0x0173){ c = ((c & 1) == 0) ? 'U' : 'u'; } else if(c == 0x0174){ c = 'W'; } else if(c == 0x0175){ c = 'w'; } else if(c == 0x0176){ c = 'Y'; } else if(c == 0x0177){ c = 'y'; } else if(c == 0x0178){ c = 'Y'; } else if(c >= 0x0179 && c <= 0x017e){ c = ((c & 1) == 1) ? 'Z' : 'z'; } else if(c == 0x017f){ c = 's'; } } ary[wi++] = c; } else if(high == 0x03){ // greek if(lowmode){ if(c >= 0x0391 && c <= 0x03a9){ c += 0x20; } else if(c >= 0x03d8 && c <= 0x03ef){ if((c & 1) == 0) c++; } else if(c == 0x0374 || c == 0x03f7 || c == 0x03fa){ c++; } } ary[wi++] = c; } else if(high == 0x04){ // cyrillic if(lowmode){ if(c <= 0x040f){ c += 0x50; } else if(c <= 0x042f){ c += 0x20; } else if(c >= 0x0460 && c <= 0x0481){ if((c & 1) == 0) c++; } else if(c >= 0x048a && c <= 0x04bf){ if((c & 1) == 0) c++; } else if(c == 0x04c0){ c = 0x04cf; } else if(c >= 0x04c1 && c <= 0x04ce){ if((c & 1) == 1) c++; } else if(c >= 0x04d0){ if((c & 1) == 0) c++; } } ary[wi++] = c; } else if(high == 0x20){ if(c == 0x2002 || c == 0x2003 || c == 0x2009){ // en space, em space, thin space if(spcmode){ ary[wi++] = 0x0020; if(wi < 2 || ary[wi-2] == 0x0020) wi--; } else { ary[wi++] = c; } } else if(c == 0x2010){ // hyphen ary[wi++] = widmode ? 0x002d : c; } else if(c == 0x2015){ // fullwidth horizontal line ary[wi++] = widmode ? 0x002d : c; } else if(c == 0x2019){ // apostrophe ary[wi++] = widmode ? 0x0027 : c; } else if(c == 0x2033){ // double quotes ary[wi++] = widmode ? 0x0022 : c; } else { // (otherwise) ary[wi++] = c; } } else if(high == 0x22){ if(c == 0x2212){ // minus sign ary[wi++] = widmode ? 0x002d : c; } else { // (otherwise) ary[wi++] = c; } } else if(high == 0x30){ if(c == 0x3000){ // fullwidth space if(spcmode){ ary[wi++] = 0x0020; if(wi < 2 || ary[wi-2] == 0x0020) wi--; } else if(widmode){ ary[wi++] = 0x0020; } else { ary[wi++] = c; } } else { // (otherwise) ary[wi++] = c; } } else if(high == 0xff){ if(c == 0xff01){ // fullwidth exclamation ary[wi++] = widmode ? 0x0021 : c; } else if(c == 0xff03){ // fullwidth igeta ary[wi++] = widmode ? 0x0023 : c; } else if(c == 0xff04){ // fullwidth dollar ary[wi++] = widmode ? 0x0024 : c; } else if(c == 0xff05){ // fullwidth parcent ary[wi++] = widmode ? 0x0025 : c; } else if(c == 0xff06){ // fullwidth ampersand ary[wi++] = widmode ? 0x0026 : c; } else if(c == 0xff0a){ // fullwidth asterisk ary[wi++] = widmode ? 0x002a : c; } else if(c == 0xff0b){ // fullwidth plus ary[wi++] = widmode ? 0x002b : c; } else if(c == 0xff0c){ // fullwidth comma ary[wi++] = widmode ? 0x002c : c; } else if(c == 0xff0e){ // fullwidth period ary[wi++] = widmode ? 0x002e : c; } else if(c == 0xff0f){ // fullwidth slash ary[wi++] = widmode ? 0x002f : c; } else if(c == 0xff1a){ // fullwidth colon ary[wi++] = widmode ? 0x003a : c; } else if(c == 0xff1b){ // fullwidth semicolon ary[wi++] = widmode ? 0x003b : c; } else if(c == 0xff1d){ // fullwidth equal ary[wi++] = widmode ? 0x003d : c; } else if(c == 0xff1f){ // fullwidth question ary[wi++] = widmode ? 0x003f : c; } else if(c == 0xff20){ // fullwidth atmark ary[wi++] = widmode ? 0x0040 : c; } else if(c == 0xff3c){ // fullwidth backslash ary[wi++] = widmode ? 0x005c : c; } else if(c == 0xff3e){ // fullwidth circumflex ary[wi++] = widmode ? 0x005e : c; } else if(c == 0xff3f){ // fullwidth underscore ary[wi++] = widmode ? 0x005f : c; } else if(c == 0xff5c){ // fullwidth vertical line ary[wi++] = widmode ? 0x007c : c; } else if(c >= 0xff21 && c <= 0xff3a){ // fullwidth alphabets if(widmode){ if(lowmode){ ary[wi++] = c - 0xfee0 + 0x20; } else { ary[wi++] = c - 0xfee0; } } else if(lowmode){ ary[wi++] = c + 0x20; } else { ary[wi++] = c; } } else if(c >= 0xff41 && c <= 0xff5a){ // fullwidth small alphabets ary[wi++] = widmode ? c - 0xfee0 : c; } else if(c >= 0xff10 && c <= 0xff19){ // fullwidth numbers ary[wi++] = widmode ? c - 0xfee0 : c; } else if(c == 0xff61){ // halfwidth full stop ary[wi++] = widmode ? 0x3002 : c; } else if(c == 0xff62){ // halfwidth left corner ary[wi++] = widmode ? 0x300c : c; } else if(c == 0xff63){ // halfwidth right corner ary[wi++] = widmode ? 0x300d : c; } else if(c == 0xff64){ // halfwidth comma ary[wi++] = widmode ? 0x3001 : c; } else if(c == 0xff65){ // halfwidth middle dot ary[wi++] = widmode ? 0x30fb : c; } else if(c == 0xff66){ // halfwidth wo ary[wi++] = widmode ? 0x30f2 : c; } else if(c >= 0xff67 && c <= 0xff6b){ // halfwidth small a-o ary[wi++] = widmode ? (c - 0xff67) * 2 + 0x30a1 : c; } else if(c >= 0xff6c && c <= 0xff6e){ // halfwidth small ya-yo ary[wi++] = widmode ? (c - 0xff6c) * 2 + 0x30e3 : c; } else if(c == 0xff6f){ // halfwidth small tu ary[wi++] = widmode ? 0x30c3 : c; } else if(c == 0xff70){ // halfwidth prolonged mark ary[wi++] = widmode ? 0x30fc : c; } else if(c >= 0xff71 && c <= 0xff75){ // halfwidth a-o if(widmode){ ary[wi] = (c - 0xff71) * 2 + 0x30a2; if(c == 0xff73 && i < num - 1 && ary[i+1] == 0xff9e){ ary[wi] = 0x30f4; i++; } wi++; } else { ary[wi++] = c; } } else if(c >= 0xff76 && c <= 0xff7a){ // halfwidth ka-ko if(widmode){ ary[wi] = (c - 0xff76) * 2 + 0x30ab; if(i < num - 1 && ary[i+1] == 0xff9e){ ary[wi]++; i++; } wi++; } else { ary[wi++] = c; } } else if(c >= 0xff7b && c <= 0xff7f){ // halfwidth sa-so if(widmode){ ary[wi] = (c - 0xff7b) * 2 + 0x30b5; if(i < num - 1 && ary[i+1] == 0xff9e){ ary[wi]++; i++; } wi++; } else { ary[wi++] = c; } } else if(c >= 0xff80 && c <= 0xff84){ // halfwidth ta-to if(widmode){ ary[wi] = (c - 0xff80) * 2 + 0x30bf + (c >= 0xff82 ? 1 : 0); if(i < num - 1 && ary[i+1] == 0xff9e){ ary[wi]++; i++; } wi++; } else { ary[wi++] = c; } } else if(c >= 0xff85 && c <= 0xff89){ // halfwidth na-no ary[wi++] = widmode ? c - 0xcebb : c; } else if(c >= 0xff8a && c <= 0xff8e){ // halfwidth ha-ho if(widmode){ ary[wi] = (c - 0xff8a) * 3 + 0x30cf; if(i < num - 1 && ary[i+1] == 0xff9e){ ary[wi]++; i++; } else if(i < num - 1 && ary[i+1] == 0xff9f){ ary[wi] += 2; i++; } wi++; } else { ary[wi++] = c; } } else if(c >= 0xff8f && c <= 0xff93){ // halfwidth ma-mo ary[wi++] = widmode ? c - 0xceb1 : c; } else if(c >= 0xff94 && c <= 0xff96){ // halfwidth ya-yo ary[wi++] = widmode ? (c - 0xff94) * 2 + 0x30e4 : c; } else if(c >= 0xff97 && c <= 0xff9b){ // halfwidth ra-ro ary[wi++] = widmode ? c - 0xceae : c; } else if(c == 0xff9c){ // halfwidth wa ary[wi++] = widmode ? 0x30ef : c; } else if(c == 0xff9d){ // halfwidth nn ary[wi++] = widmode ? 0x30f3 : c; } else { // otherwise ary[wi++] = c; } } else { // otherwise ary[wi++] = c; } } if(spcmode){ while(wi > 0 && ary[wi-1] == 0x0020){ wi--; } } return wi; } /* Generate a keyword-in-context string from a text and keywords. */ TCLIST *tcstrkwic(const char *str, const TCLIST *words, int width, int opts){ assert(str && words && width >= 0); TCLIST *texts = tclistnew(); int len = strlen(str); uint16_t *oary, *nary; TCMALLOC(oary, sizeof(*oary) * len + 1); TCMALLOC(nary, sizeof(*nary) * len + 1); int oanum, nanum; tcstrutftoucs(str, oary, &oanum); tcstrutftoucs(str, nary, &nanum); nanum = tcstrucsnorm(nary, nanum, TCUNLOWER | TCUNNOACC | TCUNWIDTH); if(nanum != oanum){ memcpy(nary, oary, sizeof(*oary) * oanum); for(int i = 0; i < oanum; i++){ if(nary[i] >= 'A' && nary[i] <= 'Z') nary[i] += 'a' - 'A'; } nanum = oanum; } int wnum = TCLISTNUM(words); TCLIST *uwords = tclistnew2(wnum); for(int i = 0; i < wnum; i++){ const char *word; int wsiz; TCLISTVAL(word, words, i, wsiz); uint16_t *uwary; TCMALLOC(uwary, sizeof(*uwary) * wsiz + 1); int uwnum; tcstrutftoucs(word, uwary, &uwnum); uwnum = tcstrucsnorm(uwary, uwnum, TCUNSPACE | TCUNLOWER | TCUNNOACC | TCUNWIDTH); if(uwnum > 0){ tclistpushmalloc(uwords, uwary, sizeof(*uwary) * uwnum); } else { TCFREE(uwary); } } wnum = TCLISTNUM(uwords); int ri = 0; int pi = 0; while(ri < nanum){ int step = 0; for(int i = 0; i < wnum; i++){ const char *val; int uwnum; TCLISTVAL(val, uwords, i, uwnum); uint16_t *uwary = (uint16_t *)val; uwnum /= sizeof(*uwary); if(ri + uwnum <= nanum){ int ci = 0; while(ci < uwnum && nary[ri+ci] == uwary[ci]){ ci++; } if(ci == uwnum){ int si = tclmax(ri - width, 0); if(opts & TCKWNOOVER) si = tclmax(si, pi); int ti = tclmin(ri + uwnum + width, nanum); char *tbuf; TCMALLOC(tbuf, (ti - si) * 5 + 1); int wi = 0; if(ri > si) wi += tcstrutfkwicputtext(oary, nary, si, ri, ri, tbuf + wi, uwords, opts); if(opts & TCKWMUTAB){ tbuf[wi++] = '\t'; } else if(opts & TCKWMUCTRL){ tbuf[wi++] = 0x02; } else if(opts & TCKWMUBRCT){ tbuf[wi++] = '['; } wi += tcstrucstoutf(oary + ri, ci, tbuf + wi); if(opts & TCKWMUTAB){ tbuf[wi++] = '\t'; } else if(opts & TCKWMUCTRL){ tbuf[wi++] = 0x03; } else if(opts & TCKWMUBRCT){ tbuf[wi++] = ']'; } if(ti > ri + ci) wi += tcstrutfkwicputtext(oary, nary, ri + ci, ti, nanum, tbuf + wi, uwords, opts); if(wi > 0){ tclistpushmalloc(texts, tbuf, wi); } else { TCFREE(tbuf); } if(ti > step) step = ti; if(step > pi) pi = step; if(opts & TCKWNOOVER) break; } } } if(ri == 0 && step < 1 && (opts & TCKWPULEAD)){ int ti = tclmin(ri + width * 2, nanum); if(ti > 0){ char *tbuf; TCMALLOC(tbuf, ti * 5 + 1); int wi = 0; wi += tcstrutfkwicputtext(oary, nary, 0, ti, nanum, tbuf + wi, uwords, opts); if(!(opts & TCKWNOOVER) && opts & TCKWMUTAB){ tbuf[wi++] = '\t'; tbuf[wi++] = '\t'; } tclistpushmalloc(texts, tbuf, wi); } step = ti; } if(opts & TCKWNOOVER){ ri = (step > 0) ? step : ri + 1; } else { ri++; } } tclistdel(uwords); TCFREE(nary); TCFREE(oary); return texts; } /* Tokenize a text separating by white space characters. */ TCLIST *tcstrtokenize(const char *str){ assert(str); TCLIST *tokens = tclistnew(); const unsigned char *rp = (unsigned char *)str; while(*rp != '\0'){ while(*rp <= ' '){ rp++; } if(*rp == '"'){ rp++; TCXSTR *buf = tcxstrnew(); while(*rp != '\0'){ if(*rp == '\\'){ rp++; if(*rp != '\0') TCXSTRCAT(buf, rp, 1); rp++; } else if(*rp == '"'){ rp++; break; } else { TCXSTRCAT(buf, rp, 1); rp++; } } int size = TCXSTRSIZE(buf); tclistpushmalloc(tokens, tcxstrtomalloc(buf), size); } else { const unsigned char *ep = rp; while(*ep > ' '){ ep++; } if(ep > rp) TCLISTPUSH(tokens, rp, ep - rp); if(*ep != '\0'){ rp = ep + 1; } else { break; } } } return tokens; } /* Create a list object by splitting a region by zero code. */ TCLIST *tcstrsplit2(const void *ptr, int size){ assert(ptr && size >= 0); TCLIST *list = tclistnew(); while(size >= 0){ const char *rp = ptr; const char *ep = (const char *)ptr + size; while(rp < ep){ if(*rp == '\0') break; rp++; } TCLISTPUSH(list, ptr, rp - (const char *)ptr); rp++; size -= rp - (const char *)ptr; ptr = rp; } return list; } /* Create a map object by splitting a string. */ TCMAP *tcstrsplit3(const char *str, const char *delims){ assert(str && delims); TCMAP *map = tcmapnew2(TCMAPTINYBNUM); const char *kbuf = NULL; int ksiz = 0; while(true){ const char *sp = str; while(*str != '\0' && !strchr(delims, *str)){ str++; } if(kbuf){ tcmapput(map, kbuf, ksiz, sp, str - sp); kbuf = NULL; } else { kbuf = sp; ksiz = str - sp; } if(*str == '\0') break; str++; } return map; } /* Create a map object by splitting a region by zero code. */ TCMAP *tcstrsplit4(const void *ptr, int size){ assert(ptr && size >= 0); TCMAP *map = tcmapnew2(tclmin(size / 6 + 1, TCMAPDEFBNUM)); const char *kbuf = NULL; int ksiz = 0; while(size >= 0){ const char *rp = ptr; const char *ep = (const char *)ptr + size; while(rp < ep){ if(*rp == '\0') break; rp++; } if(kbuf){ tcmapput(map, kbuf, ksiz, ptr, rp - (const char *)ptr); kbuf = NULL; } else { kbuf = ptr; ksiz = rp - (const char *)ptr; } rp++; size -= rp - (const char *)ptr; ptr = rp; } return map; } /* Create a region separated by zero code by joining all elements of a list object. */ void *tcstrjoin2(const TCLIST *list, int *sp){ assert(list && sp); int num = TCLISTNUM(list); int size = num + 1; for(int i = 0; i < num; i++){ size += TCLISTVALSIZ(list, i); } char *buf; TCMALLOC(buf, size); char *wp = buf; for(int i = 0; i < num; i++){ if(i > 0) *(wp++) = '\0'; int vsiz; const char *vbuf = tclistval(list, i, &vsiz); memcpy(wp, vbuf, vsiz); wp += vsiz; } *wp = '\0'; *sp = wp - buf; return buf; } /* Create a string by joining all records of a map object. */ char *tcstrjoin3(const TCMAP *map, char delim){ assert(map); int num = (int)TCMAPRNUM(map); int size = num * 2 + 1; TCMAPREC *cur = map->cur; tcmapiterinit((TCMAP *)map); const char *kbuf; int ksiz; while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){ int vsiz; tcmapiterval(kbuf, &vsiz); size += ksiz + vsiz; } char *buf; TCMALLOC(buf, size); char *wp = buf; tcmapiterinit((TCMAP *)map); bool first = true; while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){ if(first){ first = false; } else { *(wp++) = delim; } memcpy(wp, kbuf, ksiz); wp += ksiz; int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); *(wp++) = delim; memcpy(wp, vbuf, vsiz); wp += vsiz; } *wp = '\0'; ((TCMAP *)map)->cur = cur; return buf; } /* Create a region separated by zero code by joining all records of a map object. */ void *tcstrjoin4(const TCMAP *map, int *sp){ assert(map && sp); int num = (int)TCMAPRNUM(map); int size = num * 2 + 1; TCMAPREC *cur = map->cur; tcmapiterinit((TCMAP *)map); const char *kbuf; int ksiz; while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){ int vsiz; tcmapiterval(kbuf, &vsiz); size += ksiz + vsiz; } char *buf; TCMALLOC(buf, size); char *wp = buf; tcmapiterinit((TCMAP *)map); bool first = true; while((kbuf = tcmapiternext((TCMAP *)map, &ksiz)) != NULL){ if(first){ first = false; } else { *(wp++) = '\0'; } memcpy(wp, kbuf, ksiz); wp += ksiz; int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); *(wp++) = '\0'; memcpy(wp, vbuf, vsiz); wp += vsiz; } *wp = '\0'; *sp = wp - buf; ((TCMAP *)map)->cur = cur; return buf; } /* Sort top records of an array. */ void tctopsort(void *base, size_t nmemb, size_t size, size_t top, int(*compar)(const void *, const void *)){ assert(base && size > 0 && compar); if(nmemb < 1) return; if(top > nmemb) top = nmemb; char *bp = base; char *ep = bp + nmemb * size; char *rp = bp + size; int num = 1; char swap[size]; while(rp < ep){ if(num < top){ int cidx = num; while(cidx > 0){ int pidx = (cidx - 1) / 2; if(compar(bp + cidx * size, bp + pidx * size) <= 0) break; memcpy(swap, bp + cidx * size, size); memcpy(bp + cidx * size, bp + pidx * size, size); memcpy(bp + pidx * size, swap, size); cidx = pidx; } num++; } else if(compar(rp, bp) < 0){ memcpy(swap, bp, size); memcpy(bp, rp, size); memcpy(rp, swap, size); int pidx = 0; int bot = num / 2; while(pidx < bot){ int cidx = pidx * 2 + 1; if(cidx < num - 1 && compar(bp + cidx * size, bp + (cidx + 1) * size) < 0) cidx++; if(compar(bp + pidx * size, bp + cidx * size) > 0) break; memcpy(swap, bp + pidx * size, size); memcpy(bp + pidx * size, bp + cidx * size, size); memcpy(bp + cidx * size, swap, size); pidx = cidx; } } rp += size; } num = top - 1; while(num > 0){ memcpy(swap, bp, size); memcpy(bp, bp + num * size, size); memcpy(bp + num * size, swap, size); int pidx = 0; int bot = num / 2; while(pidx < bot){ int cidx = pidx * 2 + 1; if(cidx < num - 1 && compar(bp + cidx * size, bp + (cidx + 1) * size) < 0) cidx++; if(compar(bp + pidx * size, bp + cidx * size) > 0) break; memcpy(swap, bp + pidx * size, size); memcpy(bp + pidx * size, bp + cidx * size, size); memcpy(bp + cidx * size, swap, size); pidx = cidx; } num--; } } /* Suspend execution of the current thread. */ bool tcsleep(double sec){ if(!isnormal(sec) || sec <= 0.0) return false; if(sec <= 1.0 / sysconf(_SC_CLK_TCK)) return sched_yield() == 0; double integ, fract; fract = modf(sec, &integ); struct timespec req, rem; req.tv_sec = integ; req.tv_nsec = tclmin(fract * 1000.0 * 1000.0 * 1000.0, 999999999); while(nanosleep(&req, &rem) != 0){ if(errno != EINTR) return false; req = rem; } return true; } /* Get the current system information. */ TCMAP *tcsysinfo(void){ #if defined(_SYS_LINUX_) TCMAP *info = tcmapnew2(TCMAPTINYBNUM); struct rusage rbuf; memset(&rbuf, 0, sizeof(rbuf)); if(getrusage(RUSAGE_SELF, &rbuf) == 0){ tcmapprintf(info, "utime", "%0.6f", rbuf.ru_utime.tv_sec + rbuf.ru_utime.tv_usec / 1000000.0); tcmapprintf(info, "stime", "%0.6f", rbuf.ru_stime.tv_sec + rbuf.ru_stime.tv_usec / 1000000.0); } TCLIST *lines = tcreadfilelines("/proc/self/status"); if(lines){ int ln = tclistnum(lines); for(int i = 0; i < ln; i++){ const char *line = TCLISTVALPTR(lines, i); const char *rp = strchr(line, ':'); if(!rp) continue; rp++; while(*rp > '\0' && *rp <= ' '){ rp++; } if(tcstrifwm(line, "VmSize:")){ int64_t size = tcatoix(rp); if(size > 0) tcmapprintf(info, "size", "%lld", (long long)size); } else if(tcstrifwm(line, "VmRSS:")){ int64_t size = tcatoix(rp); if(size > 0) tcmapprintf(info, "rss", "%lld", (long long)size); } } tclistdel(lines); } lines = tcreadfilelines("/proc/meminfo"); if(lines){ int ln = tclistnum(lines); for(int i = 0; i < ln; i++){ const char *line = TCLISTVALPTR(lines, i); const char *rp = strchr(line, ':'); if(!rp) continue; rp++; while(*rp > '\0' && *rp <= ' '){ rp++; } if(tcstrifwm(line, "MemTotal:")){ int64_t size = tcatoix(rp); if(size > 0) tcmapprintf(info, "total", "%lld", (long long)size); } else if(tcstrifwm(line, "MemFree:")){ int64_t size = tcatoix(rp); if(size > 0) tcmapprintf(info, "free", "%lld", (long long)size); } else if(tcstrifwm(line, "Cached:")){ int64_t size = tcatoix(rp); if(size > 0) tcmapprintf(info, "cached", "%lld", (long long)size); } } tclistdel(lines); } lines = tcreadfilelines("/proc/cpuinfo"); if(lines){ int cnum = 0; int ln = tclistnum(lines); for(int i = 0; i < ln; i++){ const char *line = TCLISTVALPTR(lines, i); if(tcstrifwm(line, "processor")) cnum++; } if(cnum > 0) tcmapprintf(info, "corenum", "%lld", (long long)cnum); tclistdel(lines); } return info; #elif defined(_SYS_FREEBSD_) || defined(_SYS_MACOSX_) TCMAP *info = tcmapnew2(TCMAPTINYBNUM); struct rusage rbuf; memset(&rbuf, 0, sizeof(rbuf)); if(getrusage(RUSAGE_SELF, &rbuf) == 0){ tcmapprintf(info, "utime", "%0.6f", rbuf.ru_utime.tv_sec + rbuf.ru_utime.tv_usec / 1000000.0); tcmapprintf(info, "stime", "%0.6f", rbuf.ru_stime.tv_sec + rbuf.ru_stime.tv_usec / 1000000.0); long tck = sysconf(_SC_CLK_TCK); int64_t size = (((double)rbuf.ru_ixrss + rbuf.ru_idrss + rbuf.ru_isrss) / tck) * 1024.0; if(size > 0) tcmapprintf(info, "rss", "%lld", (long long)size); } return info; #else TCMAP *info = tcmapnew2(TCMAPTINYBNUM); struct rusage rbuf; memset(&rbuf, 0, sizeof(rbuf)); if(getrusage(RUSAGE_SELF, &rbuf) == 0){ tcmapprintf(info, "utime", "%0.6f", rbuf.ru_utime.tv_sec + rbuf.ru_utime.tv_usec / 1000000.0); tcmapprintf(info, "stime", "%0.6f", rbuf.ru_stime.tv_sec + rbuf.ru_stime.tv_usec / 1000000.0); } return info; #endif } /* Create a consistent hashing object. */ TCCHIDX *tcchidxnew(int range){ assert(range > 0); TCCHIDX *chidx; TCMALLOC(chidx, sizeof(*chidx)); int nnum = range * TCCHIDXVNNUM; TCCHIDXNODE *nodes; TCMALLOC(nodes, nnum * sizeof(*nodes)); unsigned int seed = 725; for(int i = 0; i < range; i++){ int end = (i + 1) * TCCHIDXVNNUM; for(int j = i * TCCHIDXVNNUM; j < end; j++){ nodes[j].seq = i; nodes[j].hash = (seed = seed * 123456761 + 211); } } qsort(nodes, nnum, sizeof(*nodes), tcchidxcmp); chidx->nodes = nodes; chidx->nnum = nnum; return chidx; } /* Delete a consistent hashing object. */ void tcchidxdel(TCCHIDX *chidx){ assert(chidx); TCFREE(chidx->nodes); TCFREE(chidx); } /* Get the consistent hashing value of a record. */ int tcchidxhash(TCCHIDX *chidx, const void *ptr, int size){ assert(chidx && ptr && size >= 0); uint32_t hash = 19771007; const char *rp = (char *)ptr + size; while(size--){ hash = (hash * 31) ^ *(uint8_t *)--rp; hash ^= hash << 7; } TCCHIDXNODE *nodes = chidx->nodes; int low = 0; int high = chidx->nnum; while(low < high){ int mid = (low + high) >> 1; uint32_t nhash = nodes[mid].hash; if(hash < nhash){ high = mid; } else if(hash > nhash){ low = mid + 1; } else { low = mid; break; } } if(low >= chidx->nnum) low = 0; return nodes[low].seq & INT_MAX; } /* Put a text into a KWIC buffer. `oary' specifies the original code array. `nary' specifies the normalized code array. `si' specifies the start index of the text. `ti' specifies the terminal index of the text. `end' specifies the end index of the code array. `buf' specifies the pointer to the output buffer. `uwords' specifies the list object of the words to be marked up. `opts' specifies the options. The return value is the length of the output. */ static int tcstrutfkwicputtext(const uint16_t *oary, const uint16_t *nary, int si, int ti, int end, char *buf, const TCLIST *uwords, int opts){ assert(oary && nary && si >= 0 && ti >= 0 && end >= 0 && buf && uwords); if(!(opts & TCKWNOOVER)) return tcstrucstoutf(oary + si, ti - si, buf); if(!(opts & TCKWMUTAB) && !(opts & TCKWMUCTRL) && !(opts & TCKWMUBRCT)) return tcstrucstoutf(oary + si, ti - si, buf); int wnum = TCLISTNUM(uwords); int ri = si; int wi = 0; while(ri < ti){ int step = 0; for(int i = 0; i < wnum; i++){ const char *val; int uwnum; TCLISTVAL(val, uwords, i, uwnum); uint16_t *uwary = (uint16_t *)val; uwnum /= sizeof(*uwary); if(ri + uwnum <= end){ int ci = 0; while(ci < uwnum && nary[ri+ci] == uwary[ci]){ ci++; } if(ci == uwnum){ if(opts & TCKWMUTAB){ buf[wi++] = '\t'; } else if(opts & TCKWMUCTRL){ buf[wi++] = 0x02; } else if(opts & TCKWMUBRCT){ buf[wi++] = '['; } wi += tcstrucstoutf(oary + ri, ci, buf + wi); if(opts & TCKWMUTAB){ buf[wi++] = '\t'; } else if(opts & TCKWMUCTRL){ buf[wi++] = 0x03; } else if(opts & TCKWMUBRCT){ buf[wi++] = ']'; } step = ri + ci; break; } } } if(step > 0){ ri = step; } else { wi += tcstrucstoutf(oary + ri, 1, buf + wi); ri++; } } return wi; } /* Compare two consistent hashing nodes. `a' specifies the pointer to one node object. `b' specifies the pointer to the other node object. The return value is positive if the former is big, negative if the latter is big, 0 if both are equivalent. */ static int tcchidxcmp(const void *a, const void *b){ if(((TCCHIDXNODE *)a)->hash == ((TCCHIDXNODE *)b)->hash) return 0; return ((TCCHIDXNODE *)a)->hash > ((TCCHIDXNODE *)b)->hash; } /************************************************************************************************* * filesystem utilities *************************************************************************************************/ #define TCFILEMODE 00644 // permission of a creating file #define TCIOBUFSIZ 16384 // size of an I/O buffer /* Get the canonicalized absolute path of a file. */ char *tcrealpath(const char *path){ assert(path); char buf[PATH_MAX+1]; if(realpath(path, buf)) return tcstrdup(buf); if(errno == ENOENT){ const char *pv = strrchr(path, MYPATHCHR); if(pv){ if(pv == path) return tcstrdup(path); char *prefix = tcmemdup(path, pv - path); if(!realpath(prefix, buf)){ TCFREE(prefix); return NULL; } TCFREE(prefix); pv++; } else { if(!realpath(MYCDIRSTR, buf)) return NULL; pv = path; } if(buf[0] == MYPATHCHR && buf[1] == '\0') buf[0] = '\0'; char *str; TCMALLOC(str, strlen(buf) + strlen(pv) + 2); sprintf(str, "%s%c%s", buf, MYPATHCHR, pv); return str; } return NULL; } /* Get the status information of a file. */ bool tcstatfile(const char *path, bool *isdirp, int64_t *sizep, int64_t *mtimep){ assert(path); struct stat sbuf; if(stat(path, &sbuf) != 0) return false; if(isdirp) *isdirp = S_ISDIR(sbuf.st_mode); if(sizep) *sizep = sbuf.st_size; if(mtimep) *mtimep = sbuf.st_mtime; return true; } /* Read whole data of a file. */ void *tcreadfile(const char *path, int limit, int *sp){ int fd = path ? open(path, O_RDONLY, TCFILEMODE) : 0; if(fd == -1) return NULL; if(fd == 0){ TCXSTR *xstr = tcxstrnew(); char buf[TCIOBUFSIZ]; limit = limit > 0 ? limit : INT_MAX; int rsiz; while((rsiz = read(fd, buf, tclmin(TCIOBUFSIZ, limit))) > 0){ TCXSTRCAT(xstr, buf, rsiz); limit -= rsiz; } if(sp) *sp = TCXSTRSIZE(xstr); return tcxstrtomalloc(xstr); } struct stat sbuf; if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){ close(fd); return NULL; } limit = limit > 0 ? tclmin((int)sbuf.st_size, limit) : sbuf.st_size; char *buf; TCMALLOC(buf, sbuf.st_size + 1); char *wp = buf; int rsiz; while((rsiz = read(fd, wp, limit - (wp - buf))) > 0){ wp += rsiz; } *wp = '\0'; close(fd); if(sp) *sp = wp - buf; return buf; } /* Read every line of a file. */ TCLIST *tcreadfilelines(const char *path){ int fd = path ? open(path, O_RDONLY, TCFILEMODE) : 0; if(fd == -1) return NULL; TCLIST *list = tclistnew(); TCXSTR *xstr = tcxstrnew(); char buf[TCIOBUFSIZ]; int rsiz; while((rsiz = read(fd, buf, TCIOBUFSIZ)) > 0){ for(int i = 0; i < rsiz; i++){ switch(buf[i]){ case '\r': break; case '\n': TCLISTPUSH(list, TCXSTRPTR(xstr), TCXSTRSIZE(xstr)); tcxstrclear(xstr); break; default: TCXSTRCAT(xstr, buf + i, 1); break; } } } TCLISTPUSH(list, TCXSTRPTR(xstr), TCXSTRSIZE(xstr)); tcxstrdel(xstr); if(path) close(fd); return list; } /* Write data into a file. */ bool tcwritefile(const char *path, const void *ptr, int size){ assert(ptr && size >= 0); int fd = 1; if(path && (fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, TCFILEMODE)) == -1) return false; bool err = false; if(!tcwrite(fd, ptr, size)) err = true; if(close(fd) == -1) err = true; return !err; } /* Copy a file. */ bool tccopyfile(const char *src, const char *dest){ int ifd = open(src, O_RDONLY, TCFILEMODE); if(ifd == -1) return false; int ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, TCFILEMODE); if(ofd == -1){ close(ifd); return false; } bool err = false; while(true){ char buf[TCIOBUFSIZ]; int size = read(ifd, buf, TCIOBUFSIZ); if(size > 0){ if(!tcwrite(ofd, buf, size)){ err = true; break; } } else if(size == -1){ if(errno != EINTR){ err = true; break; } } else { break; } } if(close(ofd) == -1) err = true; if(close(ifd) == -1) err = true; return !err; } /* Read names of files in a directory. */ TCLIST *tcreaddir(const char *path){ assert(path); DIR *DD; struct dirent *dp; if(!(DD = opendir(path))) return NULL; TCLIST *list = tclistnew(); while((dp = readdir(DD)) != NULL){ if(!strcmp(dp->d_name, MYCDIRSTR) || !strcmp(dp->d_name, MYPDIRSTR)) continue; TCLISTPUSH(list, dp->d_name, strlen(dp->d_name)); } closedir(DD); return list; } /* Expand a pattern into a list of matched paths. */ TCLIST *tcglobpat(const char *pattern){ assert(pattern); TCLIST *list = tclistnew(); glob_t gbuf; memset(&gbuf, 0, sizeof(gbuf)); if(glob(pattern, GLOB_ERR | GLOB_NOSORT, NULL, &gbuf) == 0){ for(int i = 0; i < gbuf.gl_pathc; i++){ tclistpush2(list, gbuf.gl_pathv[i]); } globfree(&gbuf); } return list; } /* Remove a file or a directory and its sub ones recursively. */ bool tcremovelink(const char *path){ assert(path); struct stat sbuf; if(lstat(path, &sbuf) == -1) return false; if(unlink(path) == 0) return true; TCLIST *list; if(!S_ISDIR(sbuf.st_mode) || !(list = tcreaddir(path))) return false; bool tail = path[0] != '\0' && path[strlen(path)-1] == MYPATHCHR; for(int i = 0; i < TCLISTNUM(list); i++){ const char *elem = TCLISTVALPTR(list, i); if(!strcmp(MYCDIRSTR, elem) || !strcmp(MYPDIRSTR, elem)) continue; char *cpath; if(tail){ cpath = tcsprintf("%s%s", path, elem); } else { cpath = tcsprintf("%s%c%s", path, MYPATHCHR, elem); } tcremovelink(cpath); TCFREE(cpath); } tclistdel(list); return rmdir(path) == 0 ? true : false; } /* Write data into a file. */ bool tcwrite(int fd, const void *buf, size_t size){ assert(fd >= 0 && buf && size >= 0); const char *rp = buf; do { int wb = write(fd, rp, size); switch(wb){ case -1: if(errno != EINTR) return false; case 0: break; default: rp += wb; size -= wb; break; } } while(size > 0); return true; } /* Read data from a file. */ bool tcread(int fd, void *buf, size_t size){ assert(fd >= 0 && buf && size >= 0); char *wp = buf; do { int rb = read(fd, wp, size); switch(rb){ case -1: if(errno != EINTR) return false; case 0: return size < 1; default: wp += rb; size -= rb; } } while(size > 0); return true; } /* Lock a file. */ bool tclock(int fd, bool ex, bool nb){ assert(fd >= 0); struct flock lock; memset(&lock, 0, sizeof(struct flock)); lock.l_type = ex ? F_WRLCK : F_RDLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; lock.l_pid = 0; while(fcntl(fd, nb ? F_SETLK : F_SETLKW, &lock) == -1){ if(errno != EINTR) return false; } return true; } /* Unlock a file. */ bool tcunlock(int fd){ assert(fd >= 0); struct flock lock; memset(&lock, 0, sizeof(struct flock)); lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; lock.l_pid = 0; while(fcntl(fd, F_SETLKW, &lock) == -1){ if(errno != EINTR) return false; } return true; } /* Execute a shell command. */ int tcsystem(const char **args, int anum){ assert(args && anum >= 0); if(anum < 1) return -1; TCXSTR *phrase = tcxstrnew3(anum * TCNUMBUFSIZ + 1); for(int i = 0; i < anum; i++){ const char *rp = args[i]; int len = strlen(rp); char *token; TCMALLOC(token, len * 2 + 1); char *wp = token; while(*rp != '\0'){ switch(*rp){ case '"': case '\\': case '$': case '`': *(wp++) = '\\'; *(wp++) = *rp; break; default: *(wp++) = *rp; break; } rp++; } *wp = '\0'; if(tcxstrsize(phrase)) tcxstrcat(phrase, " ", 1); tcxstrprintf(phrase, "\"%s\"", token); TCFREE(token); } int rv = system(tcxstrptr(phrase)); if(WIFEXITED(rv)){ rv = WEXITSTATUS(rv); } else { rv = INT_MAX; } tcxstrdel(phrase); return rv; } /************************************************************************************************* * encoding utilities *************************************************************************************************/ #define TCENCBUFSIZ 32 // size of a buffer for encoding name #define TCXMLATBNUM 31 // bucket number of XML attributes /* Encode a serial object with URL encoding. */ char *tcurlencode(const char *ptr, int size){ assert(ptr && size >= 0); char *buf; TCMALLOC(buf, size * 3 + 1); char *wp = buf; for(int i = 0; i < size; i++){ int c = ((unsigned char *)ptr)[i]; if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c != '\0' && strchr("_-.!~*'()", c))){ *(wp++) = c; } else { wp += sprintf(wp, "%%%02X", c); } } *wp = '\0'; return buf; } /* Decode a string encoded with URL encoding. */ char *tcurldecode(const char *str, int *sp){ assert(str && sp); char *buf = tcstrdup(str); char *wp = buf; while(*str != '\0'){ if(*str == '%'){ str++; if(((str[0] >= '0' && str[0] <= '9') || (str[0] >= 'A' && str[0] <= 'F') || (str[0] >= 'a' && str[0] <= 'f')) && ((str[1] >= '0' && str[1] <= '9') || (str[1] >= 'A' && str[1] <= 'F') || (str[1] >= 'a' && str[1] <= 'f'))){ unsigned char c = *str; if(c >= 'A' && c <= 'Z') c += 'a' - 'A'; if(c >= 'a' && c <= 'z'){ *wp = c - 'a' + 10; } else { *wp = c - '0'; } *wp *= 0x10; str++; c = *str; if(c >= 'A' && c <= 'Z') c += 'a' - 'A'; if(c >= 'a' && c <= 'z'){ *wp += c - 'a' + 10; } else { *wp += c - '0'; } str++; wp++; } else { break; } } else if(*str == '+'){ *wp = ' '; str++; wp++; } else { *wp = *str; str++; wp++; } } *wp = '\0'; *sp = wp - buf; return buf; } /* Break up a URL into elements. */ TCMAP *tcurlbreak(const char *str){ assert(str); TCMAP *map = tcmapnew2(TCMAPTINYBNUM); char *trim = tcstrdup(str); tcstrtrim(trim); const char *rp = trim; char *norm; TCMALLOC(norm, strlen(trim) * 3 + 1); char *wp = norm; while(*rp != '\0'){ if(*rp > 0x20 && *rp < 0x7f){ *(wp++) = *rp; } else { wp += sprintf(wp, "%%%02X", *(unsigned char *)rp); } rp++; } *wp = '\0'; rp = norm; tcmapput2(map, "self", rp); bool serv = false; if(tcstrifwm(rp, "http://")){ tcmapput2(map, "scheme", "http"); rp += 7; serv = true; } else if(tcstrifwm(rp, "https://")){ tcmapput2(map, "scheme", "https"); rp += 8; serv = true; } else if(tcstrifwm(rp, "ftp://")){ tcmapput2(map, "scheme", "ftp"); rp += 6; serv = true; } else if(tcstrifwm(rp, "sftp://")){ tcmapput2(map, "scheme", "sftp"); rp += 7; serv = true; } else if(tcstrifwm(rp, "ftps://")){ tcmapput2(map, "scheme", "ftps"); rp += 7; serv = true; } else if(tcstrifwm(rp, "tftp://")){ tcmapput2(map, "scheme", "tftp"); rp += 7; serv = true; } else if(tcstrifwm(rp, "ldap://")){ tcmapput2(map, "scheme", "ldap"); rp += 7; serv = true; } else if(tcstrifwm(rp, "ldaps://")){ tcmapput2(map, "scheme", "ldaps"); rp += 8; serv = true; } else if(tcstrifwm(rp, "file://")){ tcmapput2(map, "scheme", "file"); rp += 7; serv = true; } char *ep; if((ep = strchr(rp, '#')) != NULL){ tcmapput2(map, "fragment", ep + 1); *ep = '\0'; } if((ep = strchr(rp, '?')) != NULL){ tcmapput2(map, "query", ep + 1); *ep = '\0'; } if(serv){ if((ep = strchr(rp, '/')) != NULL){ tcmapput2(map, "path", ep); *ep = '\0'; } else { tcmapput2(map, "path", "/"); } if((ep = strchr(rp, '@')) != NULL){ *ep = '\0'; if(rp[0] != '\0') tcmapput2(map, "authority", rp); rp = ep + 1; } if((ep = strchr(rp, ':')) != NULL){ if(ep[1] != '\0') tcmapput2(map, "port", ep + 1); *ep = '\0'; } if(rp[0] != '\0') tcmapput2(map, "host", rp); } else { tcmapput2(map, "path", rp); } TCFREE(norm); TCFREE(trim); if((rp = tcmapget2(map, "path")) != NULL){ if((ep = strrchr(rp, '/')) != NULL){ if(ep[1] != '\0') tcmapput2(map, "file", ep + 1); } else { tcmapput2(map, "file", rp); } } if((rp = tcmapget2(map, "file")) != NULL && (!strcmp(rp, ".") || !strcmp(rp, ".."))) tcmapout2(map, "file"); return map; } /* Resolve a relative URL with an absolute URL. */ char *tcurlresolve(const char *base, const char *target){ assert(base && target); const char *vbuf, *path; char *tmp, *wp, *enc; while(*base > '\0' && *base <= ' '){ base++; } while(*target > '\0' && *target <= ' '){ target++; } if(*target == '\0') target = base; TCXSTR *rbuf = tcxstrnew(); TCMAP *telems = tcurlbreak(target); int port = 80; TCMAP *belems = tcurlbreak(tcmapget2(telems, "scheme") ? target : base); if((vbuf = tcmapget2(belems, "scheme")) != NULL){ tcxstrcat2(rbuf, vbuf); TCXSTRCAT(rbuf, "://", 3); if(!tcstricmp(vbuf, "https")){ port = 443; } else if(!tcstricmp(vbuf, "ftp")){ port = 21; } else if(!tcstricmp(vbuf, "sftp")){ port = 115; } else if(!tcstricmp(vbuf, "ftps")){ port = 22; } else if(!tcstricmp(vbuf, "tftp")){ port = 69; } else if(!tcstricmp(vbuf, "ldap")){ port = 389; } else if(!tcstricmp(vbuf, "ldaps")){ port = 636; } } else { tcxstrcat2(rbuf, "http://"); } int vsiz; if((vbuf = tcmapget2(belems, "authority")) != NULL){ if((wp = strchr(vbuf, ':')) != NULL){ *wp = '\0'; tmp = tcurldecode(vbuf, &vsiz); enc = tcurlencode(tmp, vsiz); tcxstrcat2(rbuf, enc); TCFREE(enc); TCFREE(tmp); TCXSTRCAT(rbuf, ":", 1); wp++; tmp = tcurldecode(wp, &vsiz); enc = tcurlencode(tmp, vsiz); tcxstrcat2(rbuf, enc); TCFREE(enc); TCFREE(tmp); } else { tmp = tcurldecode(vbuf, &vsiz); enc = tcurlencode(tmp, vsiz); tcxstrcat2(rbuf, enc); TCFREE(enc); TCFREE(tmp); } TCXSTRCAT(rbuf, "@", 1); } if((vbuf = tcmapget2(belems, "host")) != NULL){ tmp = tcurldecode(vbuf, &vsiz); tcstrtolower(tmp); enc = tcurlencode(tmp, vsiz); tcxstrcat2(rbuf, enc); TCFREE(enc); TCFREE(tmp); } else { TCXSTRCAT(rbuf, "localhost", 9); } int num; char numbuf[TCNUMBUFSIZ]; if((vbuf = tcmapget2(belems, "port")) != NULL && (num = tcatoi(vbuf)) != port && num > 0){ sprintf(numbuf, ":%d", num); tcxstrcat2(rbuf, numbuf); } if(!(path = tcmapget2(telems, "path"))) path = "/"; if(path[0] == '\0' && (vbuf = tcmapget2(belems, "path")) != NULL) path = vbuf; if(path[0] == '\0') path = "/"; TCLIST *bpaths = tclistnew(); TCLIST *opaths; if(path[0] != '/' && (vbuf = tcmapget2(belems, "path")) != NULL){ opaths = tcstrsplit(vbuf, "/"); } else { opaths = tcstrsplit("/", "/"); } TCFREE(tclistpop2(opaths)); for(int i = 0; i < TCLISTNUM(opaths); i++){ vbuf = tclistval(opaths, i, &vsiz); if(vsiz < 1 || !strcmp(vbuf, ".")) continue; if(!strcmp(vbuf, "..")){ TCFREE(tclistpop2(bpaths)); } else { TCLISTPUSH(bpaths, vbuf, vsiz); } } tclistdel(opaths); opaths = tcstrsplit(path, "/"); for(int i = 0; i < TCLISTNUM(opaths); i++){ vbuf = tclistval(opaths, i, &vsiz); if(vsiz < 1 || !strcmp(vbuf, ".")) continue; if(!strcmp(vbuf, "..")){ TCFREE(tclistpop2(bpaths)); } else { TCLISTPUSH(bpaths, vbuf, vsiz); } } tclistdel(opaths); for(int i = 0; i < TCLISTNUM(bpaths); i++){ vbuf = TCLISTVALPTR(bpaths, i); if(strchr(vbuf, '%')){ tmp = tcurldecode(vbuf, &vsiz); } else { tmp = tcstrdup(vbuf); } enc = tcurlencode(tmp, strlen(tmp)); TCXSTRCAT(rbuf, "/", 1); tcxstrcat2(rbuf, enc); TCFREE(enc); TCFREE(tmp); } if(tcstrbwm(path, "/")) TCXSTRCAT(rbuf, "/", 1); tclistdel(bpaths); if((vbuf = tcmapget2(telems, "query")) != NULL || (*target == '#' && (vbuf = tcmapget2(belems, "query")) != NULL)){ TCXSTRCAT(rbuf, "?", 1); TCLIST *qelems = tcstrsplit(vbuf, "&;"); for(int i = 0; i < TCLISTNUM(qelems); i++){ vbuf = TCLISTVALPTR(qelems, i); if(i > 0) TCXSTRCAT(rbuf, "&", 1); if((wp = strchr(vbuf, '=')) != NULL){ *wp = '\0'; tmp = tcurldecode(vbuf, &vsiz); enc = tcurlencode(tmp, vsiz); tcxstrcat2(rbuf, enc); TCFREE(enc); TCFREE(tmp); TCXSTRCAT(rbuf, "=", 1); wp++; tmp = tcurldecode(wp, &vsiz); enc = tcurlencode(tmp, strlen(tmp)); tcxstrcat2(rbuf, enc); TCFREE(enc); TCFREE(tmp); } else { tmp = tcurldecode(vbuf, &vsiz); enc = tcurlencode(tmp, vsiz); tcxstrcat2(rbuf, enc); TCFREE(enc); TCFREE(tmp); } } tclistdel(qelems); } if((vbuf = tcmapget2(telems, "fragment")) != NULL){ tmp = tcurldecode(vbuf, &vsiz); enc = tcurlencode(tmp, vsiz); TCXSTRCAT(rbuf, "#", 1); tcxstrcat2(rbuf, enc); TCFREE(enc); TCFREE(tmp); } tcmapdel(belems); tcmapdel(telems); return tcxstrtomalloc(rbuf); } /* Encode a serial object with Base64 encoding. */ char *tcbaseencode(const char *ptr, int size){ assert(ptr && size >= 0); char *tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const unsigned char *obj = (const unsigned char *)ptr; char *buf; TCMALLOC(buf, 4 * (size + 2) / 3 + 1); char *wp = buf; for(int i = 0; i < size; i += 3){ switch(size - i){ case 1: *wp++ = tbl[obj[0] >> 2]; *wp++ = tbl[(obj[0] & 3) << 4]; *wp++ = '='; *wp++ = '='; break; case 2: *wp++ = tbl[obj[0] >> 2]; *wp++ = tbl[((obj[0] & 3) << 4) + (obj[1] >> 4)]; *wp++ = tbl[(obj[1] & 0xf) << 2]; *wp++ = '='; break; default: *wp++ = tbl[obj[0] >> 2]; *wp++ = tbl[((obj[0] & 3) << 4) + (obj[1] >> 4)]; *wp++ = tbl[((obj[1] & 0xf) << 2) + (obj[2] >> 6)]; *wp++ = tbl[obj[2] & 0x3f]; break; } obj += 3; } *wp = '\0'; return buf; } /* Decode a string encoded with Base64 encoding. */ char *tcbasedecode(const char *str, int *sp){ assert(str && sp); int cnt = 0; int bpos = 0; int eqcnt = 0; int len = strlen(str); unsigned char *obj; TCMALLOC(obj, len + 4); unsigned char *wp = obj; while(bpos < len && eqcnt == 0){ int bits = 0; int i; for(i = 0; bpos < len && i < 4; bpos++){ if(str[bpos] >= 'A' && str[bpos] <= 'Z'){ bits = (bits << 6) | (str[bpos] - 'A'); i++; } else if(str[bpos] >= 'a' && str[bpos] <= 'z'){ bits = (bits << 6) | (str[bpos] - 'a' + 26); i++; } else if(str[bpos] >= '0' && str[bpos] <= '9'){ bits = (bits << 6) | (str[bpos] - '0' + 52); i++; } else if(str[bpos] == '+'){ bits = (bits << 6) | 62; i++; } else if(str[bpos] == '/'){ bits = (bits << 6) | 63; i++; } else if(str[bpos] == '='){ bits <<= 6; i++; eqcnt++; } } if(i == 0 && bpos >= len) continue; switch(eqcnt){ case 0: *wp++ = (bits >> 16) & 0xff; *wp++ = (bits >> 8) & 0xff; *wp++ = bits & 0xff; cnt += 3; break; case 1: *wp++ = (bits >> 16) & 0xff; *wp++ = (bits >> 8) & 0xff; cnt += 2; break; case 2: *wp++ = (bits >> 16) & 0xff; cnt += 1; break; } } obj[cnt] = '\0'; *sp = cnt; return (char *)obj; } /* Encode a serial object with Quoted-printable encoding. */ char *tcquoteencode(const char *ptr, int size){ assert(ptr && size >= 0); const unsigned char *rp = (const unsigned char *)ptr; char *buf; TCMALLOC(buf, size * 3 + 1); char *wp = buf; int cols = 0; for(int i = 0; i < size; i++){ if(rp[i] == '=' || (rp[i] < 0x20 && rp[i] != '\r' && rp[i] != '\n' && rp[i] != '\t') || rp[i] > 0x7e){ wp += sprintf(wp, "=%02X", rp[i]); cols += 3; } else { *(wp++) = rp[i]; cols++; } } *wp = '\0'; return buf; } /* Decode a string encoded with Quoted-printable encoding. */ char *tcquotedecode(const char *str, int *sp){ assert(str && sp); char *buf; TCMALLOC(buf, strlen(str) + 1); char *wp = buf; for(; *str != '\0'; str++){ if(*str == '='){ str++; if(*str == '\0'){ break; } else if(str[0] == '\r' && str[1] == '\n'){ str++; } else if(str[0] != '\n' && str[0] != '\r'){ if(*str >= 'A' && *str <= 'Z'){ *wp = (*str - 'A' + 10) * 16; } else if(*str >= 'a' && *str <= 'z'){ *wp = (*str - 'a' + 10) * 16; } else { *wp = (*str - '0') * 16; } str++; if(*str == '\0') break; if(*str >= 'A' && *str <= 'Z'){ *wp += *str - 'A' + 10; } else if(*str >= 'a' && *str <= 'z'){ *wp += *str - 'a' + 10; } else { *wp += *str - '0'; } wp++; } } else { *wp = *str; wp++; } } *wp = '\0'; *sp = wp - buf; return buf; } /* Encode a string with MIME encoding. */ char *tcmimeencode(const char *str, const char *encname, bool base){ assert(str && encname); int len = strlen(str); char *buf; TCMALLOC(buf, len * 3 + strlen(encname) + 16); char *wp = buf; wp += sprintf(wp, "=?%s?%c?", encname, base ? 'B' : 'Q'); char *enc = base ? tcbaseencode(str, len) : tcquoteencode(str, len); wp += sprintf(wp, "%s?=", enc); TCFREE(enc); return buf; } /* Decode a string encoded with MIME encoding. */ char *tcmimedecode(const char *str, char *enp){ assert(str); if(enp) sprintf(enp, "US-ASCII"); char *buf; TCMALLOC(buf, strlen(str) + 1); char *wp = buf; while(*str != '\0'){ if(tcstrfwm(str, "=?")){ str += 2; const char *pv = str; const char *ep = strchr(str, '?'); if(!ep) continue; if(enp && ep - pv < TCENCBUFSIZ){ memcpy(enp, pv, ep - pv); enp[ep-pv] = '\0'; } pv = ep + 1; bool quoted = (*pv == 'Q' || *pv == 'q'); if(*pv != '\0') pv++; if(*pv != '\0') pv++; if(!(ep = strchr(pv, '?'))) continue; char *tmp; TCMEMDUP(tmp, pv, ep - pv); int len; char *dec = quoted ? tcquotedecode(tmp, &len) : tcbasedecode(tmp, &len); wp += sprintf(wp, "%s", dec); TCFREE(dec); TCFREE(tmp); str = ep + 1; if(*str != '\0') str++; } else { *(wp++) = *str; str++; } } *wp = '\0'; return buf; } /* Split a string of MIME into headers and the body. */ char *tcmimebreak(const char *ptr, int size, TCMAP *headers, int *sp){ assert(ptr && size >= 0 && sp); const char *head = NULL; int hlen = 0; for(int i = 0; i < size; i++){ if(i < size - 4 && ptr[i] == '\r' && ptr[i+1] == '\n' && ptr[i+2] == '\r' && ptr[i+3] == '\n'){ head = ptr; hlen = i; ptr += i + 4; size -= i + 4; break; } else if(i < size - 2 && ptr[i] == '\n' && ptr[i+1] == '\n'){ head = ptr; hlen = i; ptr += i + 2; size -= i + 2; break; } } if(head && headers){ char *hbuf; TCMALLOC(hbuf, hlen + 1); int wi = 0; for(int i = 0; i < hlen; i++){ if(head[i] == '\r') continue; if(i < hlen - 1 && head[i] == '\n' && (head[i+1] == ' ' || head[i+1] == '\t')){ hbuf[wi++] = ' '; i++; } else { hbuf[wi++] = head[i]; } } hbuf[wi] = '\0'; TCLIST *list = tcstrsplit(hbuf, "\n"); int ln = TCLISTNUM(list); for(int i = 0; i < ln; i++){ const char *line = TCLISTVALPTR(list, i); const char *pv = strchr(line, ':'); if(pv){ char *name; TCMEMDUP(name, line, pv - line); for(int j = 0; name[j] != '\0'; j++){ if(name[j] >= 'A' && name[j] <= 'Z') name[j] -= 'A' - 'a'; } pv++; while(*pv == ' ' || *pv == '\t'){ pv++; } tcmapput2(headers, name, pv); TCFREE(name); } } tclistdel(list); TCFREE(hbuf); const char *pv = tcmapget2(headers, "content-type"); if(pv){ const char *ep = strchr(pv, ';'); if(ep){ tcmapput(headers, "TYPE", 4, pv, ep - pv); do { ep++; while(ep[0] == ' '){ ep++; } if(tcstrifwm(ep, "charset=")){ ep += 8; while(*ep > '\0' && *ep <= ' '){ ep++; } if(ep[0] == '"') ep++; pv = ep; while(ep[0] != '\0' && ep[0] != ' ' && ep[0] != '"' && ep[0] != ';'){ ep++; } tcmapput(headers, "CHARSET", 7, pv, ep - pv); } else if(tcstrifwm(ep, "boundary=")){ ep += 9; while(*ep > '\0' && *ep <= ' '){ ep++; } if(ep[0] == '"'){ ep++; pv = ep; while(ep[0] != '\0' && ep[0] != '"'){ ep++; } } else { pv = ep; while(ep[0] != '\0' && ep[0] != ' ' && ep[0] != '"' && ep[0] != ';'){ ep++; } } tcmapput(headers, "BOUNDARY", 8, pv, ep - pv); } } while((ep = strchr(ep, ';')) != NULL); } else { tcmapput(headers, "TYPE", 4, pv, strlen(pv)); } } if((pv = tcmapget2(headers, "content-disposition")) != NULL){ char *ep = strchr(pv, ';'); if(ep){ tcmapput(headers, "DISPOSITION", 11, pv, ep - pv); do { ep++; while(ep[0] == ' '){ ep++; } if(tcstrifwm(ep, "filename=")){ ep += 9; if(ep[0] == '"') ep++; pv = ep; while(ep[0] != '\0' && ep[0] != '"'){ ep++; } tcmapput(headers, "FILENAME", 8, pv, ep - pv); } else if(tcstrifwm(ep, "name=")){ ep += 5; if(ep[0] == '"') ep++; pv = ep; while(ep[0] != '\0' && ep[0] != '"'){ ep++; } tcmapput(headers, "NAME", 4, pv, ep - pv); } } while((ep = strchr(ep, ';')) != NULL); } else { tcmapput(headers, "DISPOSITION", 11, pv, strlen(pv)); } } } *sp = size; char *rv; TCMEMDUP(rv, ptr, size); return rv; } /* Split multipart data of MIME into its parts. */ TCLIST *tcmimeparts(const char *ptr, int size, const char *boundary){ assert(ptr && size >= 0 && boundary); TCLIST *list = tclistnew(); int blen = strlen(boundary); if(blen < 1) return list; const char *pv = NULL; for(int i = 0; i < size; i++){ if(ptr[i] == '-' && ptr[i+1] == '-' && i + 2 + blen < size && tcstrfwm(ptr + i + 2, boundary) && strchr("\t\n\v\f\r ", ptr[i+2+blen])){ pv = ptr + i + 2 + blen; if(*pv == '\r') pv++; if(*pv == '\n') pv++; size -= pv - ptr; ptr = pv; break; } } if(!pv) return list; for(int i = 0; i < size; i++){ if(ptr[i] == '-' && ptr[i+1] == '-' && i + 2 + blen < size && tcstrfwm(ptr + i + 2, boundary) && strchr("\t\n\v\f\r -", ptr[i+2+blen])){ const char *ep = ptr + i; if(ep > ptr && ep[-1] == '\n') ep--; if(ep > ptr && ep[-1] == '\r') ep--; if(ep > pv) TCLISTPUSH(list, pv, ep - pv); pv = ptr + i + 2 + blen; if(*pv == '\r') pv++; if(*pv == '\n') pv++; } } return list; } /* Encode a serial object with hexadecimal encoding. */ char *tchexencode(const char *ptr, int size){ assert(ptr && size >= 0); const unsigned char *rp = (const unsigned char *)ptr; char *buf; TCMALLOC(buf, size * 2 + 1); char *wp = buf; for(int i = 0; i < size; i++){ wp += sprintf(wp, "%02x", rp[i]); } *wp = '\0'; return buf; } /* Decode a string encoded with hexadecimal encoding. */ char *tchexdecode(const char *str, int *sp){ assert(str && sp); int len = strlen(str); char *buf; TCMALLOC(buf, len + 1); char *wp = buf; for(int i = 0; i < len; i += 2){ while(str[i] >= '\0' && str[i] <= ' '){ i++; } int num = 0; int c = str[i]; if(c == '\0') break; if(c >= '0' && c <= '9'){ num = c - '0'; } else if(c >= 'a' && c <= 'f'){ num = c - 'a' + 10; } else if(c >= 'A' && c <= 'F'){ num = c - 'A' + 10; } else if(c == '\0'){ break; } c = str[i+1]; if(c >= '0' && c <= '9'){ num = num * 0x10 + c - '0'; } else if(c >= 'a' && c <= 'f'){ num = num * 0x10 + c - 'a' + 10; } else if(c >= 'A' && c <= 'F'){ num = num * 0x10 + c - 'A' + 10; } else if(c == '\0'){ break; } *(wp++) = num; } *wp = '\0'; *sp = wp - buf; return buf; } /* Compress a serial object with Packbits encoding. */ char *tcpackencode(const char *ptr, int size, int *sp){ assert(ptr && size >= 0 && sp); char *buf; TCMALLOC(buf, size * 2 + 1); char *wp = buf; const char *end = ptr + size; while(ptr < end){ char *sp = wp; const char *rp = ptr + 1; int step = 1; while(rp < end && step < 0x7f && *rp == *ptr){ step++; rp++; } if(step <= 1 && rp < end){ wp = sp + 1; *(wp++) = *ptr; while(rp < end && step < 0x7f && *rp != *(rp - 1)){ *(wp++) = *rp; step++; rp++; } if(rp < end && *(rp - 1) == *rp){ wp--; rp--; step--; } *sp = step == 1 ? 1 : -step; } else { *(wp++) = step; *(wp++) = *ptr; } ptr += step; } *sp = wp - buf; return buf; } /* Decompress a serial object compressed with Packbits encoding. */ char *tcpackdecode(const char *ptr, int size, int *sp){ assert(ptr && size >= 0 && sp); int asiz = size * 3; char *buf; TCMALLOC(buf, asiz + 1); int wi = 0; const char *end = ptr + size; while(ptr < end){ int step = abs(*ptr); if(wi + step >= asiz){ asiz = asiz * 2 + step; TCREALLOC(buf, buf, asiz + 1); } if(*(ptr++) >= 0){ memset(buf + wi, *ptr, step); ptr++; } else { step = tclmin(step, end - ptr); memcpy(buf + wi, ptr, step); ptr += step; } wi += step; } buf[wi] = '\0'; *sp = wi; return buf; } /* Compress a serial object with Deflate encoding. */ char *tcdeflate(const char *ptr, int size, int *sp){ assert(ptr && size >= 0 && sp); if(!_tc_deflate) return NULL; return _tc_deflate(ptr, size, sp, _TCZMZLIB); } /* Decompress a serial object compressed with Deflate encoding. */ char *tcinflate(const char *ptr, int size, int *sp){ assert(ptr && size >= 0 && sp); if(!_tc_inflate) return NULL; return _tc_inflate(ptr, size, sp, _TCZMZLIB); } /* Compress a serial object with GZIP encoding. */ char *tcgzipencode(const char *ptr, int size, int *sp){ assert(ptr && size >= 0 && sp); if(!_tc_deflate) return NULL; return _tc_deflate(ptr, size, sp, _TCZMGZIP); } /* Decompress a serial object compressed with GZIP encoding. */ char *tcgzipdecode(const char *ptr, int size, int *sp){ assert(ptr && size >= 0 && sp); if(!_tc_inflate) return NULL; return _tc_inflate(ptr, size, sp, _TCZMGZIP); } /* Get the CRC32 checksum of a serial object. */ unsigned int tcgetcrc(const char *ptr, int size){ assert(ptr && size >= 0); if(!_tc_getcrc) return 0; return _tc_getcrc(ptr, size); } /* Compress a serial object with BZIP2 encoding. */ char *tcbzipencode(const char *ptr, int size, int *sp){ assert(ptr && size >= 0 && sp); if(!_tc_bzcompress) return NULL; return _tc_bzcompress(ptr, size, sp); } /* Decompress a serial object compressed with BZIP2 encoding. */ char *tcbzipdecode(const char *ptr, int size, int *sp){ assert(ptr && size >= 0 && sp); if(!_tc_bzdecompress) return NULL; return _tc_bzdecompress(ptr, size, sp); } /* Encode an array of nonnegative integers with BER encoding. */ char *tcberencode(const unsigned int *ary, int anum, int *sp){ assert(ary && anum >= 0 && sp); char *buf; TCMALLOC(buf, anum * (sizeof(int) + 1) + 1); char *wp = buf; for(int i = 0; i < anum; i++){ unsigned int num = ary[i]; if(num < (1 << 7)){ *(wp++) = num; } else if(num < (1 << 14)){ *(wp++) = (num >> 7) | 0x80; *(wp++) = num & 0x7f; } else if(num < (1 << 21)){ *(wp++) = (num >> 14) | 0x80; *(wp++) = ((num >> 7) & 0x7f) | 0x80; *(wp++) = num & 0x7f; } else if(num < (1 << 28)){ *(wp++) = (num >> 21) | 0x80; *(wp++) = ((num >> 14) & 0x7f) | 0x80; *(wp++) = ((num >> 7) & 0x7f) | 0x80; *(wp++) = num & 0x7f; } else { *(wp++) = (num >> 28) | 0x80; *(wp++) = ((num >> 21) & 0x7f) | 0x80; *(wp++) = ((num >> 14) & 0x7f) | 0x80; *(wp++) = ((num >> 7) & 0x7f) | 0x80; *(wp++) = num & 0x7f; } } *sp = wp - buf; return buf; } /* Decode a serial object encoded with BER encoding. */ unsigned int *tcberdecode(const char *ptr, int size, int *np){ assert(ptr && size >= 0 && np); unsigned int *buf; TCMALLOC(buf, size * sizeof(*buf) + 1); unsigned int *wp = buf; while(size > 0){ unsigned int num = 0; int c; do { c = *(unsigned char *)ptr; num = num * 0x80 + (c & 0x7f); ptr++; size--; } while(c >= 0x80 && size > 0); *(wp++) = num; } *np = wp - buf; return buf; } /* Escape meta characters in a string with the entity references of XML. */ char *tcxmlescape(const char *str){ assert(str); const char *rp = str; int bsiz = 0; while(*rp != '\0'){ switch(*rp){ case '&': bsiz += 5; break; case '<': bsiz += 4; break; case '>': bsiz += 4; break; case '"': bsiz += 6; break; default: bsiz++; break; } rp++; } char *buf; TCMALLOC(buf, bsiz + 1); char *wp = buf; while(*str != '\0'){ switch(*str){ case '&': memcpy(wp, "&", 5); wp += 5; break; case '<': memcpy(wp, "<", 4); wp += 4; break; case '>': memcpy(wp, ">", 4); wp += 4; break; case '"': memcpy(wp, """, 6); wp += 6; break; default: *(wp++) = *str; break; } str++; } *wp = '\0'; return buf; } /* Unescape entity references in a string of XML. */ char *tcxmlunescape(const char *str){ assert(str); char *buf; TCMALLOC(buf, strlen(str) + 1); char *wp = buf; while(*str != '\0'){ if(*str == '&'){ if(tcstrfwm(str, "&")){ *(wp++) = '&'; str += 5; } else if(tcstrfwm(str, "<")){ *(wp++) = '<'; str += 4; } else if(tcstrfwm(str, ">")){ *(wp++) = '>'; str += 4; } else if(tcstrfwm(str, """)){ *(wp++) = '"'; str += 6; } else { *(wp++) = *(str++); } } else { *(wp++) = *(str++); } } *wp = '\0'; return buf; } /************************************************************************************************* * encoding utilities (for experts) *************************************************************************************************/ /* Encode a map object into a string in the x-www-form-urlencoded format. */ char *tcwwwformencode(const TCMAP *params){ assert(params); TCXSTR *xstr = tcxstrnew3(tcmaprnum(params) * TCXSTRUNIT * 3 + 1); TCMAPREC *cur = params->cur; tcmapiterinit((TCMAP *)params); const char *kbuf; int ksiz; while((kbuf = tcmapiternext((TCMAP *)params, &ksiz)) != NULL){ int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); char *kenc = tcurlencode(kbuf, ksiz); char *venc = tcurlencode(vbuf, vsiz); if(TCXSTRSIZE(xstr) > 0) TCXSTRCAT(xstr, "&", 1); tcxstrcat2(xstr, kenc); TCXSTRCAT(xstr, "=", 1); tcxstrcat2(xstr, venc); TCFREE(venc); TCFREE(kenc); } ((TCMAP *)params)->cur = cur; return tcxstrtomalloc(xstr); } /* Decode a query string in the x-www-form-urlencoded format. */ void tcwwwformdecode(const char *str, TCMAP *params){ assert(str && params); tcwwwformdecode2(str, strlen(str), NULL, params); } /* Decode a data region in the x-www-form-urlencoded or multipart-form-data format. */ void tcwwwformdecode2(const void *ptr, int size, const char *type, TCMAP *params){ assert(ptr && size >= 0 && params); if(type && tcstrfwm(tcstrskipspc(type), "multipart/")){ const char *brd = strstr(type, "boundary="); if(brd){ brd += 9; if(*brd == '"') brd++; char *bstr = tcstrdup(brd); char *wp = strchr(bstr, ';'); if(wp) *wp = '\0'; wp = strchr(bstr, '"'); if(wp) *wp = '\0'; TCLIST *parts = tcmimeparts(ptr, size, bstr); int pnum = tclistnum(parts); for(int i = 0; i < pnum; i++){ int psiz; const char *part = tclistval(parts, i, &psiz); TCMAP *hmap = tcmapnew2(TCMAPTINYBNUM); int bsiz; char *body = tcmimebreak(part, psiz, hmap, &bsiz); int nsiz; const char *name = tcmapget(hmap, "NAME", 4, &nsiz); char numbuf[TCNUMBUFSIZ]; if(!name){ nsiz = sprintf(numbuf, "part:%d", i + 1); name = numbuf; } const char *tenc = tcmapget2(hmap, "content-transfer-encoding"); if(tenc){ if(tcstrifwm(tenc, "base64")){ char *ebuf = tcbasedecode(body, &bsiz); TCFREE(body); body = ebuf; } else if(tcstrifwm(tenc, "quoted-printable")){ char *ebuf = tcquotedecode(body, &bsiz); TCFREE(body); body = ebuf; } } tcmapputkeep(params, name, nsiz, body, bsiz); const char *fname = tcmapget2(hmap, "FILENAME"); if(fname){ if(*fname == '/'){ fname = strrchr(fname, '/') + 1; } else if(((*fname >= 'a' && *fname <= 'z') || (*fname >= 'A' && *fname <= 'Z')) && fname[1] == ':' && fname[2] == '\\'){ fname = strrchr(fname, '\\') + 1; } if(*fname != '\0'){ char key[nsiz+10]; sprintf(key, "%s_filename", name); tcmapput2(params, key, fname); } } tcfree(body); tcmapdel(hmap); } tclistdel(parts); tcfree(bstr); } } else { const char *rp = ptr; const char *pv = rp; const char *ep = rp + size; char stack[TCIOBUFSIZ]; while(rp < ep){ if(*rp == '&' || *rp == ';'){ while(pv < rp && *pv > '\0' && *pv <= ' '){ pv++; } if(rp > pv){ int len = rp - pv; char *rbuf; if(len < sizeof(stack)){ rbuf = stack; } else { TCMALLOC(rbuf, len + 1); } memcpy(rbuf, pv, len); rbuf[len] = '\0'; char *sep = strchr(rbuf, '='); if(sep){ *(sep++) = '\0'; } else { sep = ""; } int ksiz; char *kbuf = tcurldecode(rbuf, &ksiz); int vsiz; char *vbuf = tcurldecode(sep, &vsiz); if(!tcmapputkeep(params, kbuf, ksiz, vbuf, vsiz)){ tcmapputcat(params, kbuf, ksiz, "", 1); tcmapputcat(params, kbuf, ksiz, vbuf, vsiz); } TCFREE(vbuf); TCFREE(kbuf); if(rbuf != stack) TCFREE(rbuf); } pv = rp + 1; } rp++; } while(pv < rp && *pv > '\0' && *pv <= ' '){ pv++; } if(rp > pv){ int len = rp - pv; char *rbuf; if(len < sizeof(stack)){ rbuf = stack; } else { TCMALLOC(rbuf, len + 1); } memcpy(rbuf, pv, len); rbuf[len] = '\0'; char *sep = strchr(rbuf, '='); if(sep){ *(sep++) = '\0'; } else { sep = ""; } int ksiz; char *kbuf = tcurldecode(rbuf, &ksiz); int vsiz; char *vbuf = tcurldecode(sep, &vsiz); if(!tcmapputkeep(params, kbuf, ksiz, vbuf, vsiz)){ tcmapputcat(params, kbuf, ksiz, "", 1); tcmapputcat(params, kbuf, ksiz, vbuf, vsiz); } TCFREE(vbuf); TCFREE(kbuf); if(rbuf != stack) TCFREE(rbuf); } } } /* Split an XML string into tags and text sections. */ TCLIST *tcxmlbreak(const char *str){ assert(str); TCLIST *list = tclistnew(); int i = 0; int pv = 0; bool tag = false; char *ep; while(true){ if(str[i] == '\0'){ if(i > pv) TCLISTPUSH(list, str + pv, i - pv); break; } else if(!tag && str[i] == '<'){ if(str[i+1] == '!' && str[i+2] == '-' && str[i+3] == '-'){ if(i > pv) TCLISTPUSH(list, str + pv, i - pv); if((ep = strstr(str + i, "-->")) != NULL){ TCLISTPUSH(list, str + i, ep - str - i + 3); i = ep - str + 2; pv = i + 1; } } else if(str[i+1] == '!' && str[i+2] == '[' && tcstrifwm(str + i, " pv) TCLISTPUSH(list, str + pv, i - pv); if((ep = strstr(str + i, "]]>")) != NULL){ i += 9; TCXSTR *xstr = tcxstrnew(); while(str + i < ep){ if(str[i] == '&'){ TCXSTRCAT(xstr, "&", 5); } else if(str[i] == '<'){ TCXSTRCAT(xstr, "<", 4); } else if(str[i] == '>'){ TCXSTRCAT(xstr, ">", 4); } else { TCXSTRCAT(xstr, str + i, 1); } i++; } if(TCXSTRSIZE(xstr) > 0) TCLISTPUSH(list, TCXSTRPTR(xstr), TCXSTRSIZE(xstr)); tcxstrdel(xstr); i = ep - str + 2; pv = i + 1; } } else { if(i > pv) TCLISTPUSH(list, str + pv, i - pv); tag = true; pv = i; } } else if(tag && str[i] == '>'){ if(i > pv) TCLISTPUSH(list, str + pv, i - pv + 1); tag = false; pv = i + 1; } i++; } return list; } /* Get the map of attributes of an XML tag. */ TCMAP *tcxmlattrs(const char *str){ assert(str); TCMAP *map = tcmapnew2(TCXMLATBNUM); const unsigned char *rp = (unsigned char *)str; while(*rp == '<' || *rp == '/' || *rp == '?' || *rp == '!' || *rp == ' '){ rp++; } const unsigned char *key = rp; while(*rp > 0x20 && *rp != '/' && *rp != '>'){ rp++; } tcmapputkeep(map, "", 0, (char *)key, rp - key); while(*rp != '\0'){ while(*rp != '\0' && (*rp <= 0x20 || *rp == '/' || *rp == '?' || *rp == '>')){ rp++; } key = rp; while(*rp > 0x20 && *rp != '/' && *rp != '>' && *rp != '='){ rp++; } int ksiz = rp - key; while(*rp != '\0' && (*rp == '=' || *rp <= 0x20)){ rp++; } const unsigned char *val; int vsiz; if(*rp == '"'){ rp++; val = rp; while(*rp != '\0' && *rp != '"'){ rp++; } vsiz = rp - val; } else if(*rp == '\''){ rp++; val = rp; while(*rp != '\0' && *rp != '\''){ rp++; } vsiz = rp - val; } else { val = rp; while(*rp > 0x20 && *rp != '"' && *rp != '\'' && *rp != '>'){ rp++; } vsiz = rp - val; } if(*rp != '\0') rp++; if(ksiz > 0){ char *copy; TCMEMDUP(copy, val, vsiz); char *raw = tcxmlunescape(copy); tcmapputkeep(map, (char *)key, ksiz, raw, strlen(raw)); TCFREE(raw); TCFREE(copy); } } return map; } /* Escape meta characters in a string with backslash escaping of the C language. */ char *tccstrescape(const char *str){ assert(str); int asiz = TCXSTRUNIT * 2; char *buf; TCMALLOC(buf, asiz + 4); int wi = 0; bool hex = false; int c; while((c = *(unsigned char *)str) != '\0'){ if(wi >= asiz){ asiz *= 2; TCREALLOC(buf, buf, asiz + 4); } if(c < ' ' || c == 0x7f || c == '"' || c == '\'' || c == '\\'){ switch(c){ case '\t': wi += sprintf(buf + wi, "\\t"); break; case '\n': wi += sprintf(buf + wi, "\\n"); break; case '\r': wi += sprintf(buf + wi, "\\r"); break; case '\\': wi += sprintf(buf + wi, "\\\\"); break; default: wi += sprintf(buf + wi, "\\x%02X", c); hex = true; break; } } else { if(hex && ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))){ wi += sprintf(buf + wi, "\\x%02X", c); hex = true; } else { buf[wi++] = c; hex = false; } } str++; } buf[wi] = '\0'; return buf; } /* Unescape a string escaped by backslash escaping of the C language. */ char *tccstrunescape(const char *str){ assert(str); int asiz = TCXSTRUNIT * 2; char *buf; TCMALLOC(buf, asiz + 4); int wi = 0; int c; while((c = *(unsigned char *)str) != '\0'){ if(wi >= asiz){ asiz *= 2; TCREALLOC(buf, buf, asiz + 4); } if(c == '\\' && str[1] != '\0'){ str++; int si = wi; switch(*str){ case 'a': buf[wi++] = '\a'; break; case 'b': buf[wi++] = '\b'; break; case 't': buf[wi++] = '\t'; break; case 'n': buf[wi++] = '\n'; break; case 'v': buf[wi++] = '\v'; break; case 'f': buf[wi++] = '\f'; break; case 'r': buf[wi++] = '\r'; break; } if(si == wi){ c = *str; if(c == 'x'){ str++; int code = 0; for(int i = 0; i < 2; i++){ c = *str; if(c >= '0' && c <= '9'){ code = code * 0x10 + c - '0'; } else if(c >= 'A' && c <= 'F'){ code = code * 0x10 + c - 'A' + 10; } else if(c >= 'a' && c <= 'f'){ code = code * 0x10 + c - 'a' + 10; } else { break; } str++; } buf[wi++] = code; } else if(c == 'u' || c == 'U'){ int len = (c == 'U') ? 8 : 4; str++; int code = 0; for(int i = 0; i < len; i++){ c = *str; if(c >= '0' && c <= '9'){ code = code * 0x10 + c - '0'; } else if(c >= 'A' && c <= 'F'){ code = code * 0x10 + c - 'A' + 10; } else if(c >= 'a' && c <= 'f'){ code = code * 0x10 + c - 'a' + 10; } else { break; } str++; } uint16_t ary[1]; ary[0] = code; wi += tcstrucstoutf(ary, 1, buf + wi); } else if(c >= '0' && c <= '8'){ int code = 0; for(int i = 0; i < 3; i++){ c = *str; if(c >= '0' && c <= '7'){ code = code * 8 + c - '0'; } else { break; } str++; } buf[wi++] = code; } else if(c != '\0'){ buf[wi++] = c; str++; } } else { str++; } } else { buf[wi++] = c; str++; } } buf[wi] = '\0'; return buf; } /* Escape meta characters in a string with backslash escaping of JSON. */ char *tcjsonescape(const char *str){ assert(str); int asiz = TCXSTRUNIT * 2; char *buf; TCMALLOC(buf, asiz + 6); int wi = 0; int c; while((c = *(unsigned char *)str) != '\0'){ if(wi >= asiz){ asiz *= 2; TCREALLOC(buf, buf, asiz + 6); } if(c < ' ' || c == 0x7f || c == '"' || c == '\'' || c == '\\'){ switch(c){ case '\t': wi += sprintf(buf + wi, "\\t"); break; case '\n': wi += sprintf(buf + wi, "\\n"); break; case '\r': wi += sprintf(buf + wi, "\\r"); break; case '\\': wi += sprintf(buf + wi, "\\\\"); break; default: wi += sprintf(buf + wi, "\\u%04X", c); break; } } else { buf[wi++] = c; } str++; } buf[wi] = '\0'; return buf; } /* Unescape a string escaped by backslash escaping of JSON. */ char *tcjsonunescape(const char *str){ assert(str); return tccstrunescape(str); } /************************************************************************************************* * template serializer *************************************************************************************************/ #define TCTMPLUNIT 65536 // allocation unit size of a template string #define TCTMPLMAXDEP 256 // maximum depth of template blocks #define TCTMPLBEGSEP "[%" // default beginning separator #define TCTMPLENDSEP "%]" // default beginning separator #define TCTYPRFXLIST "[list]\0:" // type prefix for a list object #define TCTYPRFXMAP "[map]\0:" // type prefix for a list object /* private function prototypes */ static TCLIST *tctmpltokenize(const char *expr); static int tctmpldumpeval(TCXSTR *xstr, const char *expr, const TCLIST *elems, int cur, int num, const TCMAP **stack, int depth); static const char *tctmpldumpevalvar(const TCMAP **stack, int depth, const char *name, int *sp, int *np); /* Create a template object. */ TCTMPL *tctmplnew(void){ TCTMPL *tmpl; TCMALLOC(tmpl, sizeof(*tmpl)); tmpl->elems = NULL; tmpl->begsep = NULL; tmpl->endsep = NULL; tmpl->conf = tcmapnew2(TCMAPTINYBNUM); return tmpl; } /* Delete a template object. */ void tctmpldel(TCTMPL *tmpl){ assert(tmpl); tcmapdel(tmpl->conf); if(tmpl->endsep) TCFREE(tmpl->endsep); if(tmpl->begsep) TCFREE(tmpl->begsep); if(tmpl->elems) tclistdel(tmpl->elems); TCFREE(tmpl); } /* Set the separator strings of a template object. */ void tctmplsetsep(TCTMPL *tmpl, const char *begsep, const char *endsep){ assert(tmpl && begsep && endsep); if(tmpl->endsep) TCFREE(tmpl->endsep); if(tmpl->begsep) TCFREE(tmpl->begsep); tmpl->begsep = tcstrdup(begsep); tmpl->endsep = tcstrdup(endsep); } /* Load a template string into a template object. */ void tctmplload(TCTMPL *tmpl, const char *str){ assert(tmpl && str); const char *begsep = tmpl->begsep; if(!begsep) begsep = TCTMPLBEGSEP; const char *endsep = tmpl->endsep; if(!endsep) endsep = TCTMPLENDSEP; int beglen = strlen(begsep); int endlen = strlen(endsep); if(beglen < 1 || endlen < 1) return; int begchr = *begsep; int endchr = *endsep; if(tmpl->elems) tclistdel(tmpl->elems); tcmapclear(tmpl->conf); TCLIST *elems = tclistnew(); const char *rp = str; const char *pv = rp; while(*rp != '\0'){ if(*rp == begchr && tcstrfwm(rp, begsep)){ if(rp > pv) TCLISTPUSH(elems, pv, rp - pv); rp += beglen; pv = rp; while(*rp != '\0'){ if(*rp == endchr && tcstrfwm(rp, endsep)){ bool chop = false; while(pv < rp && *pv > '\0' && *pv <= ' '){ pv++; } if(*pv == '"'){ pv++; const char *sp = pv; while(pv < rp && *pv != '"'){ pv++; } if(pv > sp) TCLISTPUSH(elems, sp, pv - sp); } else if(*pv == '\''){ pv++; const char *sp = pv; while(pv < rp && *pv != '\''){ pv++; } if(pv > sp) TCLISTPUSH(elems, sp, pv - sp); } else { const char *ep = rp; if(ep > pv && ep[-1] == '\\'){ ep--; chop = true; } while(ep > pv && ((unsigned char *)ep)[-1] <= ' '){ ep--; } int len = ep - pv; char *buf; TCMALLOC(buf, len + 1); *buf = '\0'; memcpy(buf + 1, pv, len); tclistpushmalloc(elems, buf, len + 1); if(tcstrfwm(pv, "CONF")){ const char *expr = (char *)TCLISTVALPTR(elems, TCLISTNUM(elems) - 1) + 1; TCLIST *tokens = tctmpltokenize(expr); int tnum = TCLISTNUM(tokens); if(tnum > 1 && !strcmp(TCLISTVALPTR(tokens, 0), "CONF")){ const char *name = TCLISTVALPTR(tokens, 1); const char *value = (tnum > 2) ? TCLISTVALPTR(tokens, 2) : ""; tcmapput2(tmpl->conf, name, value); } tclistdel(tokens); } } rp += endlen; if(chop){ if(*rp == '\r') rp++; if(*rp == '\n') rp++; } break; } rp++; } pv = rp; } else { rp++; } } if(rp > pv) TCLISTPUSH(elems, pv, rp - pv); tmpl->elems = elems; } /* Load a template string from a file into a template object. */ bool tctmplload2(TCTMPL *tmpl, const char *path){ assert(tmpl && path); char *str = tcreadfile(path, -1, NULL); if(!str) return false; tctmplload(tmpl, str); TCFREE(str); return true; } /* Serialize the template string of a template object. */ char *tctmpldump(TCTMPL *tmpl, const TCMAP *vars){ assert(tmpl && vars); TCXSTR *xstr = tcxstrnew3(TCTMPLUNIT); TCLIST *elems = tmpl->elems; if(elems){ TCMAP *svars = tcmapnew2(TCMAPTINYBNUM); int cur = 0; int num = TCLISTNUM(elems); const TCMAP *stack[TCTMPLMAXDEP]; int depth = 0; stack[depth++] = tmpl->conf; stack[depth++] = svars; stack[depth++] = vars; while(cur < num){ const char *elem; int esiz; TCLISTVAL(elem, elems, cur, esiz); if(*elem == '\0' && esiz > 0){ cur = tctmpldumpeval(xstr, elem + 1, elems, cur, num, stack, depth); } else { TCXSTRCAT(xstr, elem, esiz); cur++; } } tcmapdel(svars); } return tcxstrtomalloc(xstr); } /* Get the value of a configuration variable of a template object. */ const char *tctmplconf(TCTMPL *tmpl, const char *name){ assert(tmpl && name); return tcmapget2(tmpl->conf, name); } /* Store a list object into a list object with the type information. */ void tclistpushlist(TCLIST *list, const TCLIST *obj){ assert(list && obj); char vbuf[sizeof(TCTYPRFXLIST) - 1 + sizeof(obj)]; memcpy(vbuf, TCTYPRFXLIST, sizeof(TCTYPRFXLIST) - 1); memcpy(vbuf + sizeof(TCTYPRFXLIST) - 1, &obj, sizeof(obj)); tclistpush(list, vbuf, sizeof(vbuf)); } /* Store a map object into a list object with the type information. */ void tclistpushmap(TCLIST *list, const TCMAP *obj){ assert(list && obj); char vbuf[sizeof(TCTYPRFXMAP) - 1 + sizeof(obj)]; memcpy(vbuf, TCTYPRFXMAP, sizeof(TCTYPRFXMAP) - 1); memcpy(vbuf + sizeof(TCTYPRFXMAP) - 1, &obj, sizeof(obj)); tclistpush(list, vbuf, sizeof(vbuf)); } /* Store a list object into a map object with the type information. */ void tcmapputlist(TCMAP *map, const char *kstr, const TCLIST *obj){ assert(map && kstr && obj); char vbuf[sizeof(TCTYPRFXLIST) - 1 + sizeof(obj)]; memcpy(vbuf, TCTYPRFXLIST, sizeof(TCTYPRFXLIST) - 1); memcpy(vbuf + sizeof(TCTYPRFXLIST) - 1, &obj, sizeof(obj)); tcmapput(map, kstr, strlen(kstr), vbuf, sizeof(vbuf)); } /* Store a map object into a map object with the type information. */ void tcmapputmap(TCMAP *map, const char *kstr, const TCMAP *obj){ assert(map && kstr && obj); char vbuf[sizeof(TCTYPRFXMAP) - 1 + sizeof(obj)]; memcpy(vbuf, TCTYPRFXMAP, sizeof(TCTYPRFXMAP) - 1); memcpy(vbuf + sizeof(TCTYPRFXMAP) - 1, &obj, sizeof(obj)); tcmapput(map, kstr, strlen(kstr), vbuf, sizeof(vbuf)); } /* Tokenize an template expression. `expr' specifies the expression. The return value is a list object of tokens. */ static TCLIST *tctmpltokenize(const char *expr){ TCLIST *tokens = tclistnew(); const unsigned char *rp = (unsigned char *)expr; while(*rp != '\0'){ while(*rp > '\0' && *rp <= ' '){ rp++; } const unsigned char *pv = rp; if(*rp == '"'){ pv++; rp++; while(*rp != '\0' && *rp != '"'){ rp++; } TCLISTPUSH(tokens, pv, rp - pv); if(*rp == '"') rp++; } else if(*rp == '\''){ pv++; rp++; while(*rp != '\0' && *rp != '\''){ rp++; } TCLISTPUSH(tokens, pv, rp - pv); if(*rp == '\'') rp++; } else { while(*rp > ' '){ rp++; } if(rp > pv) TCLISTPUSH(tokens, pv, rp - pv); } } return tokens; } /* Evaluate an template expression. `xstr' specifies the output buffer. `expr' specifies the expression. `elems' specifies the list of elements. `cur' specifies the current offset of the elements. `num' specifies the number of the elements. `stack' specifies the variable scope stack. `depth' specifies the current depth of the stack. The return value is the next offset of the elements. */ static int tctmpldumpeval(TCXSTR *xstr, const char *expr, const TCLIST *elems, int cur, int num, const TCMAP **stack, int depth){ assert(expr && elems && cur >= 0 && num >= 0 && stack && depth >= 0); cur++; TCLIST *tokens = tctmpltokenize(expr); int tnum = TCLISTNUM(tokens); if(tnum > 0){ const char *cmd = TCLISTVALPTR(tokens, 0); if(!strcmp(cmd, "IF")){ bool sign = true; const char *eq = NULL; const char *inc = NULL; bool prt = false; const char *rx = NULL; for(int i = 1; i < tnum; i++){ const char *token = TCLISTVALPTR(tokens, i); if(!strcmp(token, "NOT")){ sign = !sign; } else if(!strcmp(token, "EQ")){ if(++i < tnum) eq = TCLISTVALPTR(tokens, i); } else if(!strcmp(token, "INC")){ if(++i < tnum) inc = TCLISTVALPTR(tokens, i); } else if(!strcmp(token, "PRT")){ prt = true; } else if(!strcmp(token, "RX")){ if(++i < tnum) rx = TCLISTVALPTR(tokens, i); } } TCXSTR *altxstr = NULL; if(xstr){ const char *name = (tnum > 1) ? TCLISTVALPTR(tokens, 1) : "__"; int vsiz, vnum; const char *vbuf = tctmpldumpevalvar(stack, depth, name, &vsiz, &vnum); char numbuf[TCNUMBUFSIZ]; if(vbuf && vnum >= 0){ vsiz = sprintf(numbuf, "%d", vnum); vbuf = numbuf; } bool bval = false; if(vbuf){ if(eq){ if(!strcmp(vbuf, eq)) bval = true; } else if(inc){ if(strstr(vbuf, inc)) bval = true; } else if(prt){ if(*tcstrskipspc(vbuf) != '\0') bval = true; } else if(rx){ if(tcregexmatch(vbuf, rx)) bval = true; } else { bval = true; } } if(bval != sign){ altxstr = xstr; xstr = NULL; } } while(cur < num){ const char *elem; int esiz; TCLISTVAL(elem, elems, cur, esiz); if(*elem == '\0' && esiz > 0){ cur = tctmpldumpeval(xstr, elem + 1, elems, cur, num, stack, depth); if(!strcmp(elem + 1, "ELSE")){ xstr = altxstr; } else if(!strcmp(elem + 1, "END")){ break; } } else { if(xstr) TCXSTRCAT(xstr, elem, esiz); cur++; } } } else if(!strcmp(cmd, "FOREACH")){ const TCLIST *list = NULL; if(xstr){ const char *name = (tnum > 1) ? TCLISTVALPTR(tokens, 1) : ""; int vsiz, vnum; const char *vbuf = tctmpldumpevalvar(stack, depth, name, &vsiz, &vnum); if(vbuf && vsiz == sizeof(TCTYPRFXLIST) - 1 + sizeof(list) && !memcmp(vbuf, TCTYPRFXLIST, sizeof(TCTYPRFXLIST) - 1)){ memcpy(&list, vbuf + sizeof(TCTYPRFXLIST) - 1, sizeof(list)); } } if(list && TCLISTNUM(list) > 0){ const char *name = (tnum > 2) ? TCLISTVALPTR(tokens, 2) : ""; TCMAP *vars = tcmapnew2(1); if(depth < TCTMPLMAXDEP){ stack[depth] = vars; depth++; } int lnum = TCLISTNUM(list); int beg = cur; for(int i = 0; i < lnum; i++){ const char *vbuf; int vsiz; TCLISTVAL(vbuf, list, i, vsiz); if(vsiz == sizeof(TCTYPRFXLIST) - 1 + sizeof(TCLIST *) && !memcmp(vbuf, TCTYPRFXLIST, sizeof(TCTYPRFXLIST) - 1)){ TCLIST *obj; memcpy(&obj, vbuf + sizeof(TCTYPRFXLIST) - 1, sizeof(obj)); tcmapputlist(vars, name, obj); } else if(vsiz == sizeof(TCTYPRFXMAP) - 1 + sizeof(TCMAP *) && !memcmp(vbuf, TCTYPRFXMAP, sizeof(TCTYPRFXMAP) - 1)){ TCMAP *obj; memcpy(&obj, vbuf + sizeof(TCTYPRFXMAP) - 1, sizeof(obj)); tcmapputmap(vars, name, obj); } else { tcmapput2(vars, name, vbuf); } cur = beg; while(cur < num){ const char *elem; int esiz; TCLISTVAL(elem, elems, cur, esiz); if(*elem == '\0' && esiz > 0){ cur = tctmpldumpeval(xstr, elem + 1, elems, cur, num, stack, depth); if(!strcmp(elem + 1, "END")) break; } else { if(xstr) TCXSTRCAT(xstr, elem, esiz); cur++; } } } tcmapdel(vars); } else { while(cur < num){ const char *elem; int esiz; TCLISTVAL(elem, elems, cur, esiz); if(*elem == '\0' && esiz > 0){ cur = tctmpldumpeval(NULL, elem + 1, elems, cur, num, stack, depth); if(!strcmp(elem + 1, "END")) break; } else { cur++; } } } } else if(!strcmp(cmd, "SET")){ if(xstr){ const char *name = (tnum > 1) ? TCLISTVALPTR(tokens, 1) : ""; const char *value = (tnum > 2) ? TCLISTVALPTR(tokens, 2) : ""; tcmapput2((TCMAP *)stack[1], name, value); } } else if(xstr){ int vsiz, vnum; const char *vbuf = tctmpldumpevalvar(stack, depth, cmd, &vsiz, &vnum); char numbuf[TCNUMBUFSIZ]; if(vbuf && vnum >= 0){ vsiz = sprintf(numbuf, "%d", vnum); vbuf = numbuf; } const char *enc = ""; const char *def = NULL; for(int i = 1; i < tnum; i++){ const char *token = TCLISTVALPTR(tokens, i); if(!strcmp(token, "ENC")){ if(++i < tnum) enc = TCLISTVALPTR(tokens, i); } else if(!strcmp(token, "DEF")){ if(++i < tnum) def = TCLISTVALPTR(tokens, i); } } if(!vbuf && def){ vbuf = def; vsiz = strlen(def); } if(vbuf){ if(!strcmp(enc, "URL")){ char *ebuf = tcurlencode(vbuf, vsiz); tcxstrcat2(xstr, ebuf); TCFREE(ebuf); } else if(!strcmp(enc, "BASE")){ char *ebuf = tcbaseencode(vbuf, vsiz); tcxstrcat2(xstr, ebuf); TCFREE(ebuf); } else if(!strcmp(enc, "QUOTE")){ char *ebuf = tcquoteencode(vbuf, vsiz); tcxstrcat2(xstr, ebuf); TCFREE(ebuf); } else if(!strcmp(enc, "HEX")){ char *ebuf = tchexencode(vbuf, vsiz); tcxstrcat2(xstr, ebuf); TCFREE(ebuf); } else if(!strcmp(enc, "XML")){ char *ebuf = tcxmlescape(vbuf); tcxstrcat2(xstr, ebuf); TCFREE(ebuf); } else if(!strcmp(enc, "CSTR")){ char *ebuf = tccstrescape(vbuf); tcxstrcat2(xstr, ebuf); TCFREE(ebuf); } else if(!strcmp(enc, "JSON")){ char *ebuf = tcjsonescape(vbuf); tcxstrcat2(xstr, ebuf); TCFREE(ebuf); } else if(!strcmp(enc, "MD5")){ char ebuf[48]; tcmd5hash(vbuf, vsiz, ebuf); tcxstrcat2(xstr, ebuf); } else { tcxstrcat2(xstr, vbuf); } } } } tclistdel(tokens); return cur; } /* Evaluate a variable of a template expression. `stack' specifies the variable scope stack. `depth' specifies the current depth of the stack. `name' specifies the variable name. `sp' specifies the length of the region of the return value. `np' specifies the number information of the return value. The return value is the pointer to the region of the evaluated value. */ static const char *tctmpldumpevalvar(const TCMAP **stack, int depth, const char *name, int *sp, int *np){ assert(stack && depth >= 0 && name && sp && np); const char *result = NULL; TCLIST *tokens = tcstrsplit(name, "."); int tnum = TCLISTNUM(tokens); if(tnum > 0){ const char *token; int tsiz; TCLISTVAL(token, tokens, 0, tsiz); for(int i = depth - 1; i >= 0; i--){ const TCMAP *vars = stack[i]; int vsiz; const char *vbuf = tcmapget(vars, token, tsiz, &vsiz); int tidx = 1; if(vbuf){ while(vbuf){ if(vsiz == sizeof(TCTYPRFXLIST) - 1 + sizeof(TCLIST *) && !memcmp(vbuf, TCTYPRFXLIST, sizeof(TCTYPRFXLIST) - 1)){ result = vbuf; *sp = vsiz; TCLIST *list; memcpy(&list, vbuf + sizeof(TCTYPRFXLIST) - 1, sizeof(list)); *np = tclistnum(list); break; } else if(vsiz == sizeof(TCTYPRFXMAP) - 1 + sizeof(TCMAP *) && !memcmp(vbuf, TCTYPRFXMAP, sizeof(TCTYPRFXMAP) - 1)){ if(tidx < tnum){ memcpy(&vars, vbuf + sizeof(TCTYPRFXMAP) - 1, sizeof(TCMAP *)); TCLISTVAL(token, tokens, tidx, tsiz); vbuf = tcmapget(vars, token, tsiz, &vsiz); tidx++; } else { result = vbuf; *sp = vsiz; TCMAP *map; memcpy(&map, vbuf + sizeof(TCTYPRFXMAP) - 1, sizeof(map)); *np = tcmaprnum(map); break; } } else { result = vbuf; *sp = vsiz; *np = -1; break; } } break; } } } tclistdel(tokens); return result; } /************************************************************************************************* * pointer list *************************************************************************************************/ /* Create a pointer list object. */ TCPTRLIST *tcptrlistnew(void){ TCPTRLIST *ptrlist; TCMALLOC(ptrlist, sizeof(*ptrlist)); ptrlist->anum = TCLISTUNIT; TCMALLOC(ptrlist->array, sizeof(ptrlist->array[0]) * ptrlist->anum); ptrlist->start = 0; ptrlist->num = 0; return ptrlist; } /* Create a pointer list object with expecting the number of elements. */ TCPTRLIST *tcptrlistnew2(int anum){ TCPTRLIST *ptrlist; TCMALLOC(ptrlist, sizeof(*ptrlist)); if(anum < 1) anum = 1; ptrlist->anum = anum; TCMALLOC(ptrlist->array, sizeof(ptrlist->array[0]) * ptrlist->anum); ptrlist->start = 0; ptrlist->num = 0; return ptrlist; } /* Copy a pointer list object. */ TCPTRLIST *tcptrlistdup(const TCPTRLIST *ptrlist){ assert(ptrlist); int num = ptrlist->num; if(num < 1) return tcptrlistnew(); void **array = ptrlist->array + ptrlist->start; TCPTRLIST *nptrlist; TCMALLOC(nptrlist, sizeof(*nptrlist)); void **narray; TCMALLOC(narray, sizeof(*narray) * num); memcpy(narray, array, sizeof(*narray) * num); nptrlist->anum = num; nptrlist->array = narray; nptrlist->start = 0; nptrlist->num = num; return nptrlist; } /* Delete a pointer list object. */ void tcptrlistdel(TCPTRLIST *ptrlist){ assert(ptrlist); TCFREE(ptrlist->array); TCFREE(ptrlist); } /* Get the number of elements of a pointer list object. */ int tcptrlistnum(const TCPTRLIST *ptrlist){ assert(ptrlist); return ptrlist->num; } /* Get the pointer to the region of an element of a pointer list object. */ void *tcptrlistval(const TCPTRLIST *ptrlist, int index){ assert(ptrlist && index >= 0); if(index >= ptrlist->num) return NULL; return ptrlist->array[ptrlist->start+index]; } /* Add an element at the end of a pointer list object. */ void tcptrlistpush(TCPTRLIST *ptrlist, void *ptr){ assert(ptrlist && ptr); int index = ptrlist->start + ptrlist->num; if(index >= ptrlist->anum){ ptrlist->anum += ptrlist->num + 1; TCREALLOC(ptrlist->array, ptrlist->array, ptrlist->anum * sizeof(ptrlist->array[0])); } ptrlist->array[index] = ptr; ptrlist->num++; } /* Remove an element of the end of a pointer list object. */ void *tcptrlistpop(TCPTRLIST *ptrlist){ assert(ptrlist); if(ptrlist->num < 1) return NULL; int index = ptrlist->start + ptrlist->num - 1; ptrlist->num--; return ptrlist->array[index]; } /* Add an element at the top of a pointer list object. */ void tcptrlistunshift(TCPTRLIST *ptrlist, void *ptr){ assert(ptrlist && ptr); if(ptrlist->start < 1){ if(ptrlist->start + ptrlist->num >= ptrlist->anum){ ptrlist->anum += ptrlist->num + 1; TCREALLOC(ptrlist->array, ptrlist->array, ptrlist->anum * sizeof(ptrlist->array[0])); } ptrlist->start = ptrlist->anum - ptrlist->num; memmove(ptrlist->array + ptrlist->start, ptrlist->array, ptrlist->num * sizeof(ptrlist->array[0])); } ptrlist->start--; ptrlist->array[ptrlist->start] = ptr; ptrlist->num++; } /* Remove an element of the top of a pointer list object. */ void *tcptrlistshift(TCPTRLIST *ptrlist){ assert(ptrlist); if(ptrlist->num < 1) return NULL; int index = ptrlist->start; ptrlist->start++; ptrlist->num--; void *rv = ptrlist->array[index]; if((ptrlist->start & 0xff) == 0 && ptrlist->start > (ptrlist->num >> 1)){ memmove(ptrlist->array, ptrlist->array + ptrlist->start, ptrlist->num * sizeof(ptrlist->array[0])); ptrlist->start = 0; } return rv; } /* Add an element at the specified location of a pointer list object. */ void tcptrlistinsert(TCPTRLIST *ptrlist, int index, void *ptr){ assert(ptrlist && index >= 0 && ptr); if(index > ptrlist->num) return; index += ptrlist->start; if(ptrlist->start + ptrlist->num >= ptrlist->anum){ ptrlist->anum += ptrlist->num + 1; TCREALLOC(ptrlist->array, ptrlist->array, ptrlist->anum * sizeof(ptrlist->array[0])); } memmove(ptrlist->array + index + 1, ptrlist->array + index, sizeof(ptrlist->array[0]) * (ptrlist->start + ptrlist->num - index)); ptrlist->array[index] = ptr; ptrlist->num++; } /* Remove an element at the specified location of a pointer list object. */ void *tcptrlistremove(TCPTRLIST *ptrlist, int index){ assert(ptrlist && index >= 0); if(index >= ptrlist->num) return NULL; index += ptrlist->start; void *rv = ptrlist->array[index]; ptrlist->num--; memmove(ptrlist->array + index, ptrlist->array + index + 1, sizeof(ptrlist->array[0]) * (ptrlist->start + ptrlist->num - index)); return rv; } /* Overwrite an element at the specified location of a pointer list object. */ void tcptrlistover(TCPTRLIST *ptrlist, int index, void *ptr){ assert(ptrlist && index >= 0 && ptr); if(index >= ptrlist->num) return; index += ptrlist->start; ptrlist->array[index] = ptr; } /* Clear a pointer list object. */ void tcptrlistclear(TCPTRLIST *ptrlist){ assert(ptrlist); ptrlist->start = 0; ptrlist->num = 0; } /************************************************************************************************* * features for experts *************************************************************************************************/ #define TCBSENCUNIT 8192 // unit size of TCBS encoding #define TCBWTCNTMIN 64 // minimum element number of counting sort #define TCBWTCNTLV 4 // maximum recursion level of counting sort #define TCBWTBUFNUM 16384 // number of elements of BWT buffer typedef struct { // type of structure for a BWT character int fchr; // character code of the first character int tchr; // character code of the last character } TCBWTREC; /* private function prototypes */ static void tcglobalinit(void); static void tcglobaldestroy(void); static void tcbwtsortstrcount(const char **arrays, int anum, int len, int level); static void tcbwtsortstrinsert(const char **arrays, int anum, int len, int skip); static void tcbwtsortstrheap(const char **arrays, int anum, int len, int skip); static void tcbwtsortchrcount(unsigned char *str, int len); static void tcbwtsortchrinsert(unsigned char *str, int len); static void tcbwtsortreccount(TCBWTREC *arrays, int anum); static void tcbwtsortrecinsert(TCBWTREC *array, int anum); static int tcbwtsearchrec(TCBWTREC *array, int anum, int tchr); static void tcmtfencode(char *ptr, int size); static void tcmtfdecode(char *ptr, int size); static int tcgammaencode(const char *ptr, int size, char *obuf); static int tcgammadecode(const char *ptr, int size, char *obuf); /* Get the message string corresponding to an error code. */ const char *tcerrmsg(int ecode){ switch(ecode){ case TCESUCCESS: return "success"; case TCETHREAD: return "threading error"; case TCEINVALID: return "invalid operation"; case TCENOFILE: return "file not found"; case TCENOPERM: return "no permission"; case TCEMETA: return "invalid meta data"; case TCERHEAD: return "invalid record header"; case TCEOPEN: return "open error"; case TCECLOSE: return "close error"; case TCETRUNC: return "trunc error"; case TCESYNC: return "sync error"; case TCESTAT: return "stat error"; case TCESEEK: return "seek error"; case TCEREAD: return "read error"; case TCEWRITE: return "write error"; case TCEMMAP: return "mmap error"; case TCELOCK: return "lock error"; case TCEUNLINK: return "unlink error"; case TCERENAME: return "rename error"; case TCEMKDIR: return "mkdir error"; case TCERMDIR: return "rmdir error"; case TCEKEEP: return "existing record"; case TCENOREC: return "no record found"; case TCEMISC: return "miscellaneous error"; } return "unknown error"; } /* Show error message on the standard error output and exit. */ void *tcmyfatal(const char *message){ assert(message); if(tcfatalfunc){ tcfatalfunc(message); } else { fprintf(stderr, "fatal error: %s\n", message); } exit(1); return NULL; } /* Allocate a large nullified region. */ void *tczeromap(uint64_t size){ #if defined(_SYS_LINUX_) assert(size > 0); void *ptr = mmap(0, sizeof(size) + size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if(ptr == MAP_FAILED) tcmyfatal("out of memory"); *(uint64_t *)ptr = size; return (char *)ptr + sizeof(size); #else assert(size > 0); void *ptr; TCCALLOC(ptr, 1, size); return ptr; #endif } /* Free a large nullfied region. */ void tczerounmap(void *ptr){ #if defined(_SYS_LINUX_) assert(ptr); uint64_t size = *((uint64_t *)ptr - 1); munmap((char *)ptr - sizeof(size), sizeof(size) + size); #else assert(ptr); TCFREE(ptr); #endif } /* Global mutex object. */ static pthread_once_t tcglobalonce = PTHREAD_ONCE_INIT; static pthread_rwlock_t tcglobalmutex; static pthread_mutex_t tcpathmutex; static TCMAP *tcpathmap; /* Lock the global mutex object. */ bool tcglobalmutexlock(void){ pthread_once(&tcglobalonce, tcglobalinit); return pthread_rwlock_wrlock(&tcglobalmutex) == 0; } /* Lock the global mutex object by shared locking. */ bool tcglobalmutexlockshared(void){ pthread_once(&tcglobalonce, tcglobalinit); return pthread_rwlock_rdlock(&tcglobalmutex) == 0; } /* Unlock the global mutex object. */ bool tcglobalmutexunlock(void){ return pthread_rwlock_unlock(&tcglobalmutex) == 0; } /* Lock the absolute path of a file. */ bool tcpathlock(const char *path){ assert(path); pthread_once(&tcglobalonce, tcglobalinit); if(pthread_mutex_lock(&tcpathmutex) != 0) return false; bool err = false; if(tcpathmap && !tcmapputkeep2(tcpathmap, path, "")) err = true; if(pthread_mutex_unlock(&tcpathmutex) != 0) err = true; return !err; } /* Unock the absolute path of a file. */ bool tcpathunlock(const char *path){ assert(path); pthread_once(&tcglobalonce, tcglobalinit); if(pthread_mutex_lock(&tcpathmutex) != 0) return false; bool err = false; if(tcpathmap && !tcmapout2(tcpathmap, path)) err = true; if(pthread_mutex_unlock(&tcpathmutex) != 0) err = true; return !err; } /* Convert an integer to the string as binary numbers. */ int tcnumtostrbin(uint64_t num, char *buf, int col, int fc){ assert(buf); char *wp = buf; int len = sizeof(num) * 8; bool zero = true; while(len-- > 0){ if(num & (1ULL << 63)){ *(wp++) = '1'; zero = false; } else if(!zero){ *(wp++) = '0'; } num <<= 1; } if(col > 0){ if(col > sizeof(num) * 8) col = sizeof(num) * 8; len = col - (wp - buf); if(len > 0){ memmove(buf + len, buf, wp - buf); for(int i = 0; i < len; i++){ buf[i] = fc; } wp += len; } } else if(zero){ *(wp++) = '0'; } *wp = '\0'; return wp - buf; } /* Compare keys of two records by lexical order. */ int tccmplexical(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){ assert(aptr && asiz >= 0 && bptr && bsiz >= 0); int rv; TCCMPLEXICAL(rv, aptr, asiz, bptr, bsiz); return rv; } /* Compare two keys as decimal strings of real numbers. */ int tccmpdecimal(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){ assert(aptr && asiz >= 0 && bptr && bsiz >= 0); const unsigned char *arp = (unsigned char *)aptr; int alen = asiz; while(alen > 0 && (*arp <= ' ' || *arp == 0x7f)){ arp++; alen--; } int64_t anum = 0; int asign = 1; if(alen > 0 && *arp == '-'){ arp++; alen--; asign = -1; } while(alen > 0){ int c = *arp; if(c < '0' || c > '9') break; anum = anum * 10 + c - '0'; arp++; alen--; } anum *= asign; const unsigned char *brp = (unsigned char *)bptr; int blen = bsiz; while(blen > 0 && (*brp <= ' ' || *brp == 0x7f)){ brp++; blen--; } int64_t bnum = 0; int bsign = 1; if(blen > 0 && *brp == '-'){ brp++; blen--; bsign = -1; } while(blen > 0){ int c = *brp; if(c < '0' || c > '9') break; bnum = bnum * 10 + c - '0'; brp++; blen--; } bnum *= bsign; if(anum < bnum) return -1; if(anum > bnum) return 1; if((alen > 1 && *arp == '.') || (blen > 1 && *brp == '.')){ long double aflt = 0; if(alen > 1 && *arp == '.'){ arp++; alen--; if(alen > TCLDBLCOLMAX) alen = TCLDBLCOLMAX; long double base = 10; while(alen > 0){ if(*arp < '0' || *arp > '9') break; aflt += (*arp - '0') / base; arp++; alen--; base *= 10; } aflt *= asign; } long double bflt = 0; if(blen > 1 && *brp == '.'){ brp++; blen--; if(blen > TCLDBLCOLMAX) blen = TCLDBLCOLMAX; long double base = 10; while(blen > 0){ if(*brp < '0' || *brp > '9') break; bflt += (*brp - '0') / base; brp++; blen--; base *= 10; } bflt *= bsign; } if(aflt < bflt) return -1; if(aflt > bflt) return 1; } int rv; TCCMPLEXICAL(rv, aptr, asiz, bptr, bsiz); return rv; } /* Compare two keys as 32-bit integers in the native byte order. */ int tccmpint32(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){ assert(aptr && bptr); int32_t anum, bnum; if(asiz == sizeof(int32_t)){ memcpy(&anum, aptr, sizeof(int32_t)); } else if(asiz < sizeof(int32_t)){ memset(&anum, 0, sizeof(int32_t)); memcpy(&anum, aptr, asiz); } else { memcpy(&anum, aptr, sizeof(int32_t)); } if(bsiz == sizeof(int32_t)){ memcpy(&bnum, bptr, sizeof(int32_t)); } else if(bsiz < sizeof(int32_t)){ memset(&bnum, 0, sizeof(int32_t)); memcpy(&bnum, bptr, bsiz); } else { memcpy(&bnum, bptr, sizeof(int32_t)); } return (anum < bnum) ? -1 : anum > bnum; } /* Compare two keys as 64-bit integers in the native byte order. */ int tccmpint64(const char *aptr, int asiz, const char *bptr, int bsiz, void *op){ assert(aptr && bptr); int64_t anum, bnum; if(asiz == sizeof(int64_t)){ memcpy(&anum, aptr, sizeof(int64_t)); } else if(asiz < sizeof(int64_t)){ memset(&anum, 0, sizeof(int64_t)); memcpy(&anum, aptr, asiz); } else { memcpy(&anum, aptr, sizeof(int64_t)); } if(bsiz == sizeof(int64_t)){ memcpy(&bnum, bptr, sizeof(int64_t)); } else if(bsiz < sizeof(int64_t)){ memset(&bnum, 0, sizeof(int64_t)); memcpy(&bnum, bptr, bsiz); } else { memcpy(&bnum, bptr, sizeof(int64_t)); } return (anum < bnum) ? -1 : anum > bnum; } /* Compress a serial object with TCBS encoding. */ char *tcbsencode(const char *ptr, int size, int *sp){ assert(ptr && size >= 0 && sp); char *result; TCMALLOC(result, (size * 7) / 3 + (size / TCBSENCUNIT + 1) * sizeof(uint16_t) + TCBSENCUNIT * 2 + 0x200); char *pv = result + size + 0x100; char *wp = pv; char *tp = pv + size + 0x100; const char *end = ptr + size; while(ptr < end){ int usiz = tclmin(TCBSENCUNIT, end - ptr); memcpy(tp, ptr, usiz); memcpy(tp + usiz, ptr, usiz); char *sp = wp; uint16_t idx = 0; wp += sizeof(idx); const char *arrays[usiz+1]; for(int i = 0; i < usiz; i++){ arrays[i] = tp + i; } const char *fp = arrays[0]; if(usiz >= TCBWTCNTMIN){ tcbwtsortstrcount(arrays, usiz, usiz, 0); } else if(usiz > 1){ tcbwtsortstrinsert(arrays, usiz, usiz, 0); } for(int i = 0; i < usiz; i++){ int tidx = arrays[i] - fp; if(tidx == 0){ idx = i; *(wp++) = ptr[usiz-1]; } else { *(wp++) = ptr[tidx-1]; } } idx = TCHTOIS(idx); memcpy(sp, &idx, sizeof(idx)); ptr += TCBSENCUNIT; } size = wp - pv; tcmtfencode(pv, size); int nsiz = tcgammaencode(pv, size, result); *sp = nsiz; return result; } /* Decompress a serial object compressed with TCBS encoding. */ char *tcbsdecode(const char *ptr, int size, int *sp){ char *result; TCMALLOC(result, size * 9 + 0x200); char *wp = result + size + 0x100; int nsiz = tcgammadecode(ptr, size, wp); tcmtfdecode(wp, nsiz); ptr = wp; wp = result; const char *end = ptr + nsiz; while(ptr < end){ uint16_t idx; memcpy(&idx, ptr, sizeof(idx)); idx = TCITOHS(idx); ptr += sizeof(idx); int usiz = tclmin(TCBSENCUNIT, end - ptr); if(idx >= usiz) idx = 0; char rbuf[usiz+1]; memcpy(rbuf, ptr, usiz); if(usiz >= TCBWTCNTMIN){ tcbwtsortchrcount((unsigned char *)rbuf, usiz); } else if(usiz > 0){ tcbwtsortchrinsert((unsigned char *)rbuf, usiz); } int fnums[0x100], tnums[0x100]; memset(fnums, 0, sizeof(fnums)); memset(tnums, 0, sizeof(tnums)); TCBWTREC array[usiz+1]; TCBWTREC *rp = array; for(int i = 0; i < usiz; i++){ int fc = *(unsigned char *)(rbuf + i); rp->fchr = (fc << 23) + fnums[fc]++; int tc = *(unsigned char *)(ptr + i); rp->tchr = (tc << 23) + tnums[tc]++; rp++; } unsigned int fchr = array[idx].fchr; if(usiz >= TCBWTCNTMIN){ tcbwtsortreccount(array, usiz); } else if(usiz > 1){ tcbwtsortrecinsert(array, usiz); } for(int i = 0; i < usiz; i++){ if(array[i].fchr == fchr){ idx = i; break; } } for(int i = 0; i < usiz; i++){ *(wp++) = array[idx].fchr >> 23; idx = tcbwtsearchrec(array, usiz, array[idx].fchr); } ptr += usiz; } *wp = '\0'; *sp = wp - result; return result; } /* Encode a serial object with BWT encoding. */ char *tcbwtencode(const char *ptr, int size, int *idxp){ assert(ptr && size >= 0 && idxp); if(size < 1){ *idxp = 0; char *rv; TCMEMDUP(rv, "", 0); return rv; } char *result; TCMALLOC(result, size * 3 + 1); char *tp = result + size + 1; memcpy(tp, ptr, size); memcpy(tp + size, ptr, size); const char *abuf[TCBWTBUFNUM]; const char **arrays = abuf; if(size > TCBWTBUFNUM) TCMALLOC(arrays, sizeof(*arrays) * size); for(int i = 0; i < size; i++){ arrays[i] = tp + i; } const char *fp = arrays[0]; if(size >= TCBWTCNTMIN){ tcbwtsortstrcount(arrays, size, size, -1); } else if(size > 1){ tcbwtsortstrinsert(arrays, size, size, 0); } for(int i = 0; i < size; i++){ int idx = arrays[i] - fp; if(idx == 0){ *idxp = i; result[i] = ptr[size-1]; } else { result[i] = ptr[idx-1]; } } if(arrays != abuf) TCFREE(arrays); result[size] = '\0'; return result; } /* Decode a serial object encoded with BWT encoding. */ char *tcbwtdecode(const char *ptr, int size, int idx){ assert(ptr && size >= 0); if(size < 1 || idx < 0){ char *rv; TCMEMDUP(rv, "", 0); return rv; } if(idx >= size) idx = 0; char *result; TCMALLOC(result, size + 1); memcpy(result, ptr, size); if(size >= TCBWTCNTMIN){ tcbwtsortchrcount((unsigned char *)result, size); } else { tcbwtsortchrinsert((unsigned char *)result, size); } int fnums[0x100], tnums[0x100]; memset(fnums, 0, sizeof(fnums)); memset(tnums, 0, sizeof(tnums)); TCBWTREC abuf[TCBWTBUFNUM]; TCBWTREC *array = abuf; if(size > TCBWTBUFNUM) TCMALLOC(array, sizeof(*array) * size); TCBWTREC *rp = array; for(int i = 0; i < size; i++){ int fc = *(unsigned char *)(result + i); rp->fchr = (fc << 23) + fnums[fc]++; int tc = *(unsigned char *)(ptr + i); rp->tchr = (tc << 23) + tnums[tc]++; rp++; } unsigned int fchr = array[idx].fchr; if(size >= TCBWTCNTMIN){ tcbwtsortreccount(array, size); } else if(size > 1){ tcbwtsortrecinsert(array, size); } for(int i = 0; i < size; i++){ if(array[i].fchr == fchr){ idx = i; break; } } char *wp = result; for(int i = 0; i < size; i++){ *(wp++) = array[idx].fchr >> 23; idx = tcbwtsearchrec(array, size, array[idx].fchr); } *wp = '\0'; if(array != abuf) TCFREE(array); return result; } /* Get the binary logarithm of an integer. */ long tclog2l(long num){ if(num <= 1) return 0; num >>= 1; long rv = 0; while(num > 0){ rv++; num >>= 1; } return rv; } /* Get the binary logarithm of a real number. */ double tclog2d(double num){ return log(num) / log(2); } /* Get the aligned offset of a file offset. */ uint64_t tcpagealign(uint64_t off){ int ps = sysconf(_SC_PAGESIZE); int diff = off & (ps - 1); return (diff > 0) ? off + ps - diff : off; } /* Initialize the global mutex object */ static void tcglobalinit(void){ if(!TCUSEPTHREAD){ memset(&tcglobalmutex, 0, sizeof(tcglobalmutex)); memset(&tcpathmutex, 0, sizeof(tcpathmutex)); } if(pthread_rwlock_init(&tcglobalmutex, NULL) != 0) tcmyfatal("rwlock error"); if(pthread_mutex_init(&tcpathmutex, NULL) != 0) tcmyfatal("mutex error"); tcpathmap = tcmapnew2(TCMAPTINYBNUM); atexit(tcglobaldestroy); } /* Destroy the global mutex object */ static void tcglobaldestroy(void){ tcmapdel(tcpathmap); pthread_mutex_destroy(&tcpathmutex); pthread_rwlock_destroy(&tcglobalmutex); } /* Sort BWT string arrays by dicrionary order by counting sort. `array' specifies an array of string arrays. `anum' specifies the number of the array. `len' specifies the size of each string. `level' specifies the level of recursion. */ static void tcbwtsortstrcount(const char **arrays, int anum, int len, int level){ assert(arrays && anum >= 0 && len >= 0); const char *nbuf[TCBWTBUFNUM]; const char **narrays = nbuf; if(anum > TCBWTBUFNUM) TCMALLOC(narrays, sizeof(*narrays) * anum); int count[0x100], accum[0x100]; memset(count, 0, sizeof(count)); int skip = level < 0 ? 0 : level; for(int i = 0; i < anum; i++){ count[((unsigned char *)arrays[i])[skip]]++; } memcpy(accum, count, sizeof(count)); for(int i = 1; i < 0x100; i++){ accum[i] = accum[i-1] + accum[i]; } for(int i = 0; i < anum; i++){ narrays[--accum[((unsigned char *)arrays[i])[skip]]] = arrays[i]; } int off = 0; if(level >= 0 && level < TCBWTCNTLV){ for(int i = 0; i < 0x100; i++){ int c = count[i]; if(c > 1){ if(c >= TCBWTCNTMIN){ tcbwtsortstrcount(narrays + off, c, len, level + 1); } else { tcbwtsortstrinsert(narrays + off, c, len, skip + 1); } } off += c; } } else { for(int i = 0; i < 0x100; i++){ int c = count[i]; if(c > 1){ if(c >= TCBWTCNTMIN){ tcbwtsortstrheap(narrays + off, c, len, skip + 1); } else { tcbwtsortstrinsert(narrays + off, c, len, skip + 1); } } off += c; } } memcpy(arrays, narrays, anum * sizeof(*narrays)); if(narrays != nbuf) TCFREE(narrays); } /* Sort BWT string arrays by dicrionary order by insertion sort. `array' specifies an array of string arrays. `anum' specifies the number of the array. `len' specifies the size of each string. `skip' specifies the number of skipped bytes. */ static void tcbwtsortstrinsert(const char **arrays, int anum, int len, int skip){ assert(arrays && anum >= 0 && len >= 0); for(int i = 1; i < anum; i++){ int cmp = 0; const unsigned char *ap = (unsigned char *)arrays[i-1]; const unsigned char *bp = (unsigned char *)arrays[i]; for(int j = skip; j < len; j++){ if(ap[j] != bp[j]){ cmp = ap[j] - bp[j]; break; } } if(cmp > 0){ const char *swap = arrays[i]; int j; for(j = i; j > 0; j--){ int cmp = 0; const unsigned char *ap = (unsigned char *)arrays[j-1]; const unsigned char *bp = (unsigned char *)swap; for(int k = skip; k < len; k++){ if(ap[k] != bp[k]){ cmp = ap[k] - bp[k]; break; } } if(cmp < 0) break; arrays[j] = arrays[j-1]; } arrays[j] = swap; } } } /* Sort BWT string arrays by dicrionary order by heap sort. `array' specifies an array of string arrays. `anum' specifies the number of the array. `len' specifies the size of each string. `skip' specifies the number of skipped bytes. */ static void tcbwtsortstrheap(const char **arrays, int anum, int len, int skip){ assert(arrays && anum >= 0 && len >= 0); anum--; int bottom = (anum >> 1) + 1; int top = anum; while(bottom > 0){ bottom--; int mybot = bottom; int i = mybot * 2; while(i <= top){ if(i < top){ int cmp = 0; const unsigned char *ap = (unsigned char *)arrays[i+1]; const unsigned char *bp = (unsigned char *)arrays[i]; for(int j = skip; j < len; j++){ if(ap[j] != bp[j]){ cmp = ap[j] - bp[j]; break; } } if(cmp > 0) i++; } int cmp = 0; const unsigned char *ap = (unsigned char *)arrays[mybot]; const unsigned char *bp = (unsigned char *)arrays[i]; for(int j = skip; j < len; j++){ if(ap[j] != bp[j]){ cmp = ap[j] - bp[j]; break; } } if(cmp >= 0) break; const char *swap = arrays[mybot]; arrays[mybot] = arrays[i]; arrays[i] = swap; mybot = i; i = mybot * 2; } } while(top > 0){ const char *swap = arrays[0]; arrays[0] = arrays[top]; arrays[top] = swap; top--; int mybot = bottom; int i = mybot * 2; while(i <= top){ if(i < top){ int cmp = 0; const unsigned char *ap = (unsigned char *)arrays[i+1]; const unsigned char *bp = (unsigned char *)arrays[i]; for(int j = 0; j < len; j++){ if(ap[j] != bp[j]){ cmp = ap[j] - bp[j]; break; } } if(cmp > 0) i++; } int cmp = 0; const unsigned char *ap = (unsigned char *)arrays[mybot]; const unsigned char *bp = (unsigned char *)arrays[i]; for(int j = 0; j < len; j++){ if(ap[j] != bp[j]){ cmp = ap[j] - bp[j]; break; } } if(cmp >= 0) break; swap = arrays[mybot]; arrays[mybot] = arrays[i]; arrays[i] = swap; mybot = i; i = mybot * 2; } } } /* Sort BWT characters by code number by counting sort. `str' specifies a string. `len' specifies the length of the string. */ static void tcbwtsortchrcount(unsigned char *str, int len){ assert(str && len >= 0); int cnt[0x100]; memset(cnt, 0, sizeof(cnt)); for(int i = 0; i < len; i++){ cnt[str[i]]++; } int pos = 0; for(int i = 0; i < 0x100; i++){ memset(str + pos, i, cnt[i]); pos += cnt[i]; } } /* Sort BWT characters by code number by insertion sort. `str' specifies a string. `len' specifies the length of the string. */ static void tcbwtsortchrinsert(unsigned char *str, int len){ assert(str && len >= 0); for(int i = 1; i < len; i++){ if(str[i-1] - str[i] > 0){ unsigned char swap = str[i]; int j; for(j = i; j > 0; j--){ if(str[j-1] - swap < 0) break; str[j] = str[j-1]; } str[j] = swap; } } } /* Sort BWT records by code number by counting sort. `array' specifies an array of records. `anum' specifies the number of the array. */ static void tcbwtsortreccount(TCBWTREC *array, int anum){ assert(array && anum >= 0); TCBWTREC nbuf[TCBWTBUFNUM]; TCBWTREC *narray = nbuf; if(anum > TCBWTBUFNUM) TCMALLOC(narray, sizeof(*narray) * anum); int count[0x100], accum[0x100]; memset(count, 0, sizeof(count)); for(int i = 0; i < anum; i++){ count[array[i].tchr>>23]++; } memcpy(accum, count, sizeof(count)); for(int i = 1; i < 0x100; i++){ accum[i] = accum[i-1] + accum[i]; } for(int i = 0; i < 0x100; i++){ accum[i] -= count[i]; } for(int i = 0; i < anum; i++){ narray[accum[array[i].tchr>>23]++] = array[i]; } memcpy(array, narray, anum * sizeof(*narray)); if(narray != nbuf) TCFREE(narray); } /* Sort BWT records by code number by insertion sort. `array' specifies an array of records.. `anum' specifies the number of the array. */ static void tcbwtsortrecinsert(TCBWTREC *array, int anum){ assert(array && anum >= 0); for(int i = 1; i < anum; i++){ if(array[i-1].tchr - array[i].tchr > 0){ TCBWTREC swap = array[i]; int j; for(j = i; j > 0; j--){ if(array[j-1].tchr - swap.tchr < 0) break; array[j] = array[j-1]; } array[j] = swap; } } } /* Search the element of BWT records. `array' specifies an array of records. `anum' specifies the number of the array. `tchr' specifies the last code number. */ static int tcbwtsearchrec(TCBWTREC *array, int anum, int tchr){ assert(array && anum >= 0); int bottom = 0; int top = anum; int mid; do { mid = (bottom + top) >> 1; if(array[mid].tchr == tchr){ return mid; } else if(array[mid].tchr < tchr){ bottom = mid + 1; if(bottom >= anum) break; } else { top = mid - 1; } } while(bottom <= top); return -1; } /* Initialization table for MTF encoder. */ const unsigned char tcmtftable[] = { 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f, 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f, 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f, 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f, 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f, 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f, 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f, 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf, 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf, 0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf, 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf, 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef, 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff }; /* Encode a region with MTF encoding. `ptr' specifies the pointer to the region. `size' specifies the size of the region. */ static void tcmtfencode(char *ptr, int size){ unsigned char table1[0x100], table2[0x100], *table, *another; assert(ptr && size >= 0); memcpy(table1, tcmtftable, sizeof(tcmtftable)); table = table1; another = table2; const char *end = ptr + size; char *wp = ptr; while(ptr < end){ unsigned char c = *ptr; unsigned char *tp = table; unsigned char *tend = table + 0x100; while(tp < tend && *tp != c){ tp++; } int idx = tp - table; *(wp++) = idx; if(idx > 0){ memcpy(another, &c, 1); memcpy(another + 1, table, idx); memcpy(another + 1 + idx, table + idx + 1, 255 - idx); unsigned char *swap = table; table = another; another = swap; } ptr++; } } /* Decode a region compressed with MTF encoding. `ptr' specifies the pointer to the region. `size' specifies the size of the region. */ static void tcmtfdecode(char *ptr, int size){ assert(ptr && size >= 0); unsigned char table1[0x100], table2[0x100], *table, *another; assert(ptr && size >= 0); memcpy(table1, tcmtftable, sizeof(tcmtftable)); table = table1; another = table2; const char *end = ptr + size; char *wp = ptr; while(ptr < end){ int idx = *(unsigned char *)ptr; unsigned char c = table[idx]; *(wp++) = c; if(idx > 0){ memcpy(another, &c, 1); memcpy(another + 1, table, idx); memcpy(another + 1 + idx, table + idx + 1, 255 - idx); unsigned char *swap = table; table = another; another = swap; } ptr++; } } /* Encode a region with Elias gamma encoding. `ptr' specifies the pointer to the region. `size' specifies the size of the region. `obuf' specifies the pointer to the output buffer. The return value is the size of the output buffer. */ static int tcgammaencode(const char *ptr, int size, char *obuf){ assert(ptr && size >= 0 && obuf); TCBITSTRM strm; TCBITSTRMINITW(strm, obuf); const char *end = ptr + size; while(ptr < end){ unsigned int c = *(unsigned char *)ptr; if(!c){ TCBITSTRMCAT(strm, 1); } else { c++; int plen = 8; while(plen > 0 && !(c & (1 << plen))){ plen--; } int jlen = plen; while(jlen-- > 0){ TCBITSTRMCAT(strm, 0); } while(plen >= 0){ int sign = (c & (1 << plen)) > 0; TCBITSTRMCAT(strm, sign); plen--; } } ptr++; } TCBITSTRMSETEND(strm); return TCBITSTRMSIZE(strm); } /* Decode a region compressed with Elias gamma encoding. `ptr' specifies the pointer to the region. `size' specifies the size of the region. `obuf' specifies the pointer to the output buffer. The return value is the size of the output buffer. */ static int tcgammadecode(const char *ptr, int size, char *obuf){ assert(ptr && size >= 0 && obuf); char *wp = obuf; TCBITSTRM strm; TCBITSTRMINITR(strm, ptr, size); int bnum = TCBITSTRMNUM(strm); while(bnum > 0){ int sign; TCBITSTRMREAD(strm, sign); bnum--; if(sign){ *(wp++) = 0; } else { int plen = 1; while(bnum > 0){ TCBITSTRMREAD(strm, sign); bnum--; if(sign) break; plen++; } unsigned int c = 1; while(bnum > 0 && plen-- > 0){ TCBITSTRMREAD(strm, sign); bnum--; c = (c << 1) + (sign > 0); } *(wp++) = c - 1; } } return wp - obuf; } // END OF FILE tokyocabinet-1.4.48/tcfmttest.c0000644000175000017500000010557312013574446015507 0ustar mikiomikio/************************************************************************************************* * The test cases of the fixed-length database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" #define RECBUFSIZ 48 // buffer for records #define EXHEADSIZ 256 // expected header size typedef struct { // type of structure for write thread TCFDB *fdb; int rnum; bool rnd; int id; } TARGWRITE; typedef struct { // type of structure for read thread TCFDB *fdb; int rnum; bool wb; bool rnd; int id; } TARGREAD; typedef struct { // type of structure for remove thread TCFDB *fdb; int rnum; bool rnd; int id; } TARGREMOVE; typedef struct { // type of structure for wicked thread TCFDB *fdb; int rnum; bool nc; int id; TCMAP *map; } TARGWICKED; typedef struct { // type of structure for typical thread TCFDB *fdb; int rnum; bool nc; int rratio; int id; } TARGTYPICAL; /* global variables */ const char *g_progname; // program name unsigned int g_randseed; // random seed int g_dbgfd; // debugging output /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void iprintf(const char *format, ...); static void iputchar(int c); static void eprint(TCFDB *fdb, int line, const char *func); static void mprint(TCFDB *fdb); static void sysprint(void); static int myrand(int range); static int myrandnd(int range); static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op); static int runwrite(int argc, char **argv); static int runread(int argc, char **argv); static int runremove(int argc, char **argv); static int runwicked(int argc, char **argv); static int runtypical(int argc, char **argv); static int procwrite(const char *path, int tnum, int rnum, int width, int64_t limsiz, int omode, bool rnd); static int procread(const char *path, int tnum, int omode, bool wb, bool rnd); static int procremove(const char *path, int tnum, int omode, bool rnd); static int procwicked(const char *path, int tnum, int rnum, int omode, bool nc); static int proctypical(const char *path, int tnum, int rnum, int width, int64_t limsiz, int omode, bool nc, int rratio); static void *threadwrite(void *targ); static void *threadread(void *targ); static void *threadremove(void *targ); static void *threadwicked(void *targ); static void *threadtypical(void *targ); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; const char *ebuf = getenv("TCRNDSEED"); g_randseed = ebuf ? tcatoix(ebuf) : tctime() * 1000; srand(g_randseed); ebuf = getenv("TCDBGFD"); g_dbgfd = ebuf ? tcatoix(ebuf) : UINT16_MAX; if(argc < 2) usage(); int 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], "remove")){ rv = runremove(argc, argv); } else if(!strcmp(argv[1], "wicked")){ rv = runwicked(argc, argv); } else if(!strcmp(argv[1], "typical")){ rv = runtypical(argc, argv); } else { usage(); } if(rv != 0){ printf("FAILED: TCRNDSEED=%u PID=%d", g_randseed, (int)getpid()); for(int i = 0; i < argc; i++){ printf(" %s", argv[i]); } printf("\n\n"); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: test cases of the fixed-length database API of Tokyo Cabinet\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s write [-nl|-nb] [-rnd] path tnum rnum [width [limsiz]]\n", g_progname); fprintf(stderr, " %s read [-nl|-nb] [-wb] [-rnd] path tnum\n", g_progname); fprintf(stderr, " %s remove [-nl|-nb] [-rnd] path tnum\n", g_progname); fprintf(stderr, " %s wicked [-nl|-nb] [-nc] path tnum rnum\n", g_progname); fprintf(stderr, " %s typical [-nl|-nb] [-nc] [-rr num] path tnum rnum [width [limsiz]]\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); } /* print a character and flush the buffer */ static void iputchar(int c){ putchar(c); fflush(stdout); } /* print error message of fixed-length database */ static void eprint(TCFDB *fdb, int line, const char *func){ const char *path = tcfdbpath(fdb); int ecode = tcfdbecode(fdb); fprintf(stderr, "%s: %s: %d: %s: error: %d: %s\n", g_progname, path ? path : "-", line, func, ecode, tcfdberrmsg(ecode)); } /* print members of fixed-length database */ static void mprint(TCFDB *fdb){ if(fdb->cnt_writerec < 0) return; iprintf("minimum ID number: %llu\n", (unsigned long long)tcfdbmin(fdb)); iprintf("maximum ID number: %llu\n", (unsigned long long)tcfdbmax(fdb)); iprintf("width of the value: %u\n", (unsigned int)tcfdbwidth(fdb)); iprintf("limit file size: %llu\n", (unsigned long long)tcfdblimsiz(fdb)); iprintf("limit ID number: %llu\n", (unsigned long long)tcfdblimid(fdb)); iprintf("cnt_writerec: %lld\n", (long long)fdb->cnt_writerec); iprintf("cnt_readrec: %lld\n", (long long)fdb->cnt_readrec); iprintf("cnt_truncfile: %lld\n", (long long)fdb->cnt_truncfile); } /* print system information */ static void sysprint(void){ TCMAP *info = tcsysinfo(); if(info){ tcmapiterinit(info); const char *kbuf; while((kbuf = tcmapiternext2(info)) != NULL){ iprintf("sys_%s: %s\n", kbuf, tcmapiterval2(kbuf)); } tcmapdel(info); } } /* get a random number */ static int myrand(int range){ if(range < 2) return 0; int high = (unsigned int)rand() >> 4; int low = range * (rand() / (RAND_MAX + 1.0)); low &= (unsigned int)INT_MAX >> 4; return (high + low) % range; } /* get a random number based on normal distribution */ static int myrandnd(int range){ int num = (int)tcdrandnd(range >> 1, range / 10); return (num < 0 || num >= range) ? 0 : num; } /* iterator function */ static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op){ unsigned int sum = 0; while(--ksiz >= 0){ sum += ((char *)kbuf)[ksiz]; } while(--vsiz >= 0){ sum += ((char *)vbuf)[vsiz]; } return myrand(100 + (sum & 0xff)) > 0; } /* parse arguments of write command */ static int runwrite(int argc, char **argv){ char *path = NULL; char *tstr = NULL; char *rstr = NULL; char *wstr = NULL; char *lstr = NULL; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!wstr){ wstr = argv[i]; } else if(!lstr){ lstr = argv[i]; } else { usage(); } } if(!path || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int width = wstr ? tcatoix(wstr) : -1; int64_t limsiz = lstr ? tcatoix(lstr) : -1; int rv = procwrite(path, tnum, rnum, width, limsiz, omode, rnd); return rv; } /* parse arguments of read command */ static int runread(int argc, char **argv){ char *path = NULL; char *tstr = NULL; int omode = 0; bool wb = false; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else if(!strcmp(argv[i], "-wb")){ wb = true; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else { usage(); } } if(!path || !tstr) usage(); int tnum = tcatoix(tstr); if(tnum < 1) usage(); int rv = procread(path, tnum, omode, wb, rnd); return rv; } /* parse arguments of remove command */ static int runremove(int argc, char **argv){ char *path = NULL; char *tstr = NULL; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else { usage(); } } if(!path || !tstr) usage(); int tnum = tcatoix(tstr); if(tnum < 1) usage(); int rv = procremove(path, tnum, omode, rnd); return rv; } /* parse arguments of wicked command */ static int runwicked(int argc, char **argv){ char *path = NULL; char *tstr = NULL; char *rstr = NULL; int omode = 0; bool nc = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else if(!strcmp(argv[i], "-nc")){ nc = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!path || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int rv = procwicked(path, tnum, rnum, omode, nc); return rv; } /* parse arguments of typical command */ static int runtypical(int argc, char **argv){ char *path = NULL; char *tstr = NULL; char *rstr = NULL; char *wstr = NULL; char *lstr = NULL; int omode = 0; int rratio = -1; bool nc = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-nl")){ omode |= FDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= FDBOLCKNB; } else if(!strcmp(argv[i], "-nc")){ nc = true; } else if(!strcmp(argv[i], "-rr")){ if(++i >= argc) usage(); rratio = tcatoix(argv[i]); } else { usage(); } } else if(!path){ path = argv[i]; } else if(!tstr){ tstr = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!wstr){ wstr = argv[i]; } else if(!lstr){ lstr = argv[i]; } else { usage(); } } if(!path || !tstr || !rstr) usage(); int tnum = tcatoix(tstr); int rnum = tcatoix(rstr); if(tnum < 1 || rnum < 1) usage(); int width = wstr ? tcatoix(wstr) : -1; int64_t limsiz = lstr ? tcatoix(lstr) : -1; int rv = proctypical(path, tnum, rnum, width, limsiz, omode, nc, rratio); return rv; } /* perform write command */ static int procwrite(const char *path, int tnum, int rnum, int width, int64_t limsiz, int omode, bool rnd){ iprintf("\n seed=%u path=%s tnum=%d rnum=%d width=%d limsiz=%lld" " omode=%d rnd=%d\n\n", g_randseed, path, tnum, rnum, width, (long long)limsiz, omode, rnd); bool err = false; double stime = tctime(); TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(!tcfdbsetmutex(fdb)){ eprint(fdb, __LINE__, "tcfdbsetmutex"); err = true; } if(!tcfdbtune(fdb, width, limsiz)){ eprint(fdb, __LINE__, "tcfdbtune"); err = true; } if(!tcfdbopen(fdb, path, FDBOWRITER | FDBOCREAT | FDBOTRUNC | omode)){ eprint(fdb, __LINE__, "tcfdbopen"); err = true; } TARGWRITE targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].fdb = fdb; targs[0].rnum = rnum; targs[0].rnd = rnd; targs[0].id = 0; if(threadwrite(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].fdb = fdb; targs[i].rnum = rnum; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadwrite, targs + i) != 0){ eprint(fdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(fdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcfdbrnum(fdb)); iprintf("size: %llu\n", (unsigned long long)tcfdbfsiz(fdb)); mprint(fdb); sysprint(); if(!tcfdbclose(fdb)){ eprint(fdb, __LINE__, "tcfdbclose"); err = true; } tcfdbdel(fdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform read command */ static int procread(const char *path, int tnum, int omode, bool wb, bool rnd){ iprintf("\n seed=%u path=%s tnum=%d omode=%d wb=%d rnd=%d\n\n", g_randseed, path, tnum, omode, wb, rnd); bool err = false; double stime = tctime(); TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(!tcfdbsetmutex(fdb)){ eprint(fdb, __LINE__, "tcfdbsetmutex"); err = true; } if(!tcfdbopen(fdb, path, FDBOREADER | omode)){ eprint(fdb, __LINE__, "tcfdbopen"); err = true; } int rnum = tcfdbrnum(fdb) / tnum; TARGREAD targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].fdb = fdb; targs[0].rnum = rnum; targs[0].wb = wb; targs[0].rnd = rnd; targs[0].id = 0; if(threadread(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].fdb = fdb; targs[i].rnum = rnum; targs[i].wb = wb; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadread, targs + i) != 0){ eprint(fdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(fdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcfdbrnum(fdb)); iprintf("size: %llu\n", (unsigned long long)tcfdbfsiz(fdb)); mprint(fdb); sysprint(); if(!tcfdbclose(fdb)){ eprint(fdb, __LINE__, "tcfdbclose"); err = true; } tcfdbdel(fdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform remove command */ static int procremove(const char *path, int tnum, int omode, bool rnd){ iprintf("\n seed=%u path=%s tnum=%d omode=%d rnd=%d\n\n", g_randseed, path, tnum, omode, rnd); bool err = false; double stime = tctime(); TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(!tcfdbsetmutex(fdb)){ eprint(fdb, __LINE__, "tcfdbsetmutex"); err = true; } if(!tcfdbopen(fdb, path, FDBOWRITER | omode)){ eprint(fdb, __LINE__, "tcfdbopen"); err = true; } int rnum = tcfdbrnum(fdb) / tnum; TARGREMOVE targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].fdb = fdb; targs[0].rnum = rnum; targs[0].rnd = rnd; targs[0].id = 0; if(threadremove(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].fdb = fdb; targs[i].rnum = rnum; targs[i].rnd = rnd; targs[i].id = i; if(pthread_create(threads + i, NULL, threadremove, targs + i) != 0){ eprint(fdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(fdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcfdbrnum(fdb)); iprintf("size: %llu\n", (unsigned long long)tcfdbfsiz(fdb)); mprint(fdb); sysprint(); if(!tcfdbclose(fdb)){ eprint(fdb, __LINE__, "tcfdbclose"); err = true; } tcfdbdel(fdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform wicked command */ static int procwicked(const char *path, int tnum, int rnum, int omode, bool nc){ iprintf("\n seed=%u path=%s tnum=%d rnum=%d omode=%d nc=%d\n\n", g_randseed, path, tnum, rnum, omode, nc); bool err = false; double stime = tctime(); TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(!tcfdbsetmutex(fdb)){ eprint(fdb, __LINE__, "tcfdbsetmutex"); err = true; } if(!tcfdbtune(fdb, RECBUFSIZ * 2, EXHEADSIZ + (RECBUFSIZ * 2 + sizeof(int)) * rnum * tnum)){ eprint(fdb, __LINE__, "tcfdbtune"); err = true; } if(!tcfdbopen(fdb, path, FDBOWRITER | FDBOCREAT | FDBOTRUNC | omode)){ eprint(fdb, __LINE__, "tcfdbopen"); err = true; } if(!tcfdbiterinit(fdb)){ eprint(fdb, __LINE__, "tcfdbiterinit"); err = true; } TARGWICKED targs[tnum]; pthread_t threads[tnum]; TCMAP *map = tcmapnew(); if(tnum == 1){ targs[0].fdb = fdb; targs[0].rnum = rnum; targs[0].nc = nc; targs[0].id = 0; targs[0].map = map; if(threadwicked(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].fdb = fdb; targs[i].rnum = rnum; targs[i].nc = nc; targs[i].id = i; targs[i].map = map; if(pthread_create(threads + i, NULL, threadwicked, targs + i) != 0){ eprint(fdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(fdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } if(!nc){ if(!tcfdbsync(fdb)){ eprint(fdb, __LINE__, "tcfdbsync"); err = true; } if(tcfdbrnum(fdb) != tcmaprnum(map)){ eprint(fdb, __LINE__, "(validation)"); err = true; } int end = rnum * tnum; for(int i = 1; i <= end && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", i); int vsiz; const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz); int rsiz; char *rbuf = tcfdbget2(fdb, kbuf, ksiz, &rsiz); if(vbuf){ iputchar('.'); if(vsiz > tcfdbwidth(fdb)) vsiz = tcfdbwidth(fdb); if(!rbuf){ eprint(fdb, __LINE__, "tcfdbget"); err = true; } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(fdb, __LINE__, "(validation)"); err = true; } } else { iputchar('*'); if(rbuf || tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "(validation)"); err = true; } } tcfree(rbuf); if(i % 50 == 0) iprintf(" (%08d)\n", i); } if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum); } tcmapdel(map); iprintf("record number: %llu\n", (unsigned long long)tcfdbrnum(fdb)); iprintf("size: %llu\n", (unsigned long long)tcfdbfsiz(fdb)); mprint(fdb); sysprint(); if(!tcfdbclose(fdb)){ eprint(fdb, __LINE__, "tcfdbclose"); err = true; } tcfdbdel(fdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform typical command */ static int proctypical(const char *path, int tnum, int rnum, int width, int64_t limsiz, int omode, bool nc, int rratio){ iprintf("\n seed=%u path=%s tnum=%d rnum=%d width=%d limsiz=%lld" " omode=%d nc=%d rratio=%d\n\n", g_randseed, path, tnum, rnum, width, (long long)limsiz, omode, nc, rratio); bool err = false; double stime = tctime(); TCFDB *fdb = tcfdbnew(); if(g_dbgfd >= 0) tcfdbsetdbgfd(fdb, g_dbgfd); if(!tcfdbsetmutex(fdb)){ eprint(fdb, __LINE__, "tcfdbsetmutex"); err = true; } if(!tcfdbtune(fdb, width, limsiz)){ eprint(fdb, __LINE__, "tcfdbtune"); err = true; } if(!tcfdbopen(fdb, path, FDBOWRITER | FDBOCREAT | FDBOTRUNC | omode)){ eprint(fdb, __LINE__, "tcfdbopen"); err = true; } TARGTYPICAL targs[tnum]; pthread_t threads[tnum]; if(tnum == 1){ targs[0].fdb = fdb; targs[0].rnum = rnum; targs[0].nc = nc; targs[0].rratio = rratio; targs[0].id = 0; if(threadtypical(targs) != NULL) err = true; } else { for(int i = 0; i < tnum; i++){ targs[i].fdb = fdb; targs[i].rnum = rnum; targs[i].nc = nc; targs[i].rratio= rratio; targs[i].id = i; if(pthread_create(threads + i, NULL, threadtypical, targs + i) != 0){ eprint(fdb, __LINE__, "pthread_create"); targs[i].id = -1; err = true; } } for(int i = 0; i < tnum; i++){ if(targs[i].id == -1) continue; void *rv; if(pthread_join(threads[i], &rv) != 0){ eprint(fdb, __LINE__, "pthread_join"); err = true; } else if(rv){ err = true; } } } iprintf("record number: %llu\n", (unsigned long long)tcfdbrnum(fdb)); iprintf("size: %llu\n", (unsigned long long)tcfdbfsiz(fdb)); mprint(fdb); sysprint(); if(!tcfdbclose(fdb)){ eprint(fdb, __LINE__, "tcfdbclose"); err = true; } tcfdbdel(fdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* thread the write function */ static void *threadwrite(void *targ){ TCFDB *fdb = ((TARGWRITE *)targ)->fdb; int rnum = ((TARGWRITE *)targ)->rnum; bool rnd = ((TARGWRITE *)targ)->rnd; int id = ((TARGWRITE *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", base + (rnd ? myrand(i) + 1 : i)); if(!tcfdbput2(fdb, buf, len, buf, len)){ eprint(fdb, __LINE__, "tcfdbput2"); err = true; break; } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the read function */ static void *threadread(void *targ){ TCFDB *fdb = ((TARGREAD *)targ)->fdb; int rnum = ((TARGREAD *)targ)->rnum; bool wb = ((TARGREAD *)targ)->wb; bool rnd = ((TARGREAD *)targ)->rnd; int id = ((TARGREAD *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum && !err; i++){ uint64_t kid = base + (rnd ? myrandnd(i) + 1 : i); int vsiz; if(wb){ char vbuf[RECBUFSIZ]; int vsiz = tcfdbget4(fdb, kid, vbuf, RECBUFSIZ); if(vsiz < 0 && (!rnd || tcfdbecode(fdb) != TCENOREC)){ eprint(fdb, __LINE__, "tcfdbget4"); err = true; } } else { char *vbuf = tcfdbget(fdb, kid, &vsiz); if(!vbuf && (!rnd || tcfdbecode(fdb) != TCENOREC)){ eprint(fdb, __LINE__, "tcfdbget"); err = true; } tcfree(vbuf); } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the remove function */ static void *threadremove(void *targ){ TCFDB *fdb = ((TARGREMOVE *)targ)->fdb; int rnum = ((TARGREMOVE *)targ)->rnum; bool rnd = ((TARGREMOVE *)targ)->rnd; int id = ((TARGREMOVE *)targ)->id; bool err = false; int base = id * rnum; for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", base + (rnd ? myrand(i + 1) + 1 : i)); if(!tcfdbout2(fdb, kbuf, ksiz) && (!rnd || tcfdbecode(fdb) != TCENOREC)){ eprint(fdb, __LINE__, "tcfdbout2"); err = true; break; } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } return err ? "error" : NULL; } /* thread the wicked function */ static void *threadwicked(void *targ){ TCFDB *fdb = ((TARGWICKED *)targ)->fdb; int rnum = ((TARGWICKED *)targ)->rnum; bool nc = ((TARGWICKED *)targ)->nc; int id = ((TARGWICKED *)targ)->id; TCMAP *map = ((TARGWICKED *)targ)->map; bool err = false; for(int i = 1; i <= rnum && !err; i++){ uint64_t kid = myrand(rnum * (id + 1)) + 1; char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%llu", (unsigned long long)kid); char vbuf[RECBUFSIZ]; int vsiz = myrand(RECBUFSIZ); memset(vbuf, '*', vsiz); vbuf[vsiz] = '\0'; char *rbuf; if(!nc) tcglobalmutexlock(); switch(myrand(16)){ case 0: if(id == 0) iputchar('0'); if(!tcfdbput2(fdb, kbuf, ksiz, vbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbput2"); err = true; } if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: if(id == 0) iputchar('1'); if(!tcfdbput3(fdb, kbuf, vbuf)){ eprint(fdb, __LINE__, "tcfdbput3"); err = true; } if(!nc) tcmapput2(map, kbuf, vbuf); break; case 2: if(id == 0) iputchar('2'); if(!tcfdbputkeep2(fdb, kbuf, ksiz, vbuf, vsiz) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbputkeep2"); err = true; } if(!nc) tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 3: if(id == 0) iputchar('3'); if(!tcfdbputkeep3(fdb, kbuf, vbuf) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbputkeep3"); err = true; } if(!nc) tcmapputkeep2(map, kbuf, vbuf); break; case 4: if(id == 0) iputchar('4'); if(!tcfdbputcat2(fdb, kbuf, ksiz, vbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbputcat2"); err = true; } if(!nc) tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 5: if(id == 0) iputchar('5'); if(!tcfdbputcat3(fdb, kbuf, vbuf)){ eprint(fdb, __LINE__, "tcfdbputcat3"); err = true; } if(!nc) tcmapputcat2(map, kbuf, vbuf); break; case 6: if(id == 0) iputchar('6'); if(myrand(2) == 0){ if(!tcfdbout2(fdb, kbuf, ksiz) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbout2"); err = true; } if(!nc) tcmapout(map, kbuf, ksiz); } break; case 7: if(id == 0) iputchar('7'); if(myrand(2) == 0){ if(!tcfdbout3(fdb, kbuf) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbout3"); err = true; } if(!nc) tcmapout2(map, kbuf); } break; case 8: if(id == 0) iputchar('8'); if(!(rbuf = tcfdbget2(fdb, kbuf, ksiz, &vsiz))){ if(tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbget2"); err = true; } rbuf = tcsprintf("[%d]", myrand(i + 1)); vsiz = strlen(rbuf); } vsiz += myrand(vsiz); if(myrand(3) == 0) vsiz += PATH_MAX; rbuf = tcrealloc(rbuf, vsiz + 1); for(int j = 0; j < vsiz; j++){ rbuf[j] = myrand(0x100); } if(!tcfdbput2(fdb, kbuf, ksiz, rbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbput2"); err = true; } if(!nc) tcmapput(map, kbuf, ksiz, rbuf, vsiz); tcfree(rbuf); break; case 9: if(id == 0) iputchar('9'); if(!(rbuf = tcfdbget2(fdb, kbuf, ksiz, &vsiz)) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbget2"); err = true; } tcfree(rbuf); break; case 10: if(id == 0) iputchar('A'); if(!(rbuf = tcfdbget3(fdb, kbuf)) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbge3"); err = true; } tcfree(rbuf); break; case 11: if(id == 0) iputchar('B'); if(myrand(1) == 0) vsiz = 1; if((vsiz = tcfdbget4(fdb, kid, vbuf, vsiz)) < 0 && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbget4"); err = true; } break; case 14: if(id == 0) iputchar('E'); if(myrand(rnum / 50) == 0){ if(!tcfdbiterinit(fdb)){ eprint(fdb, __LINE__, "tcfdbiterinit"); err = true; } } for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){ if(tcfdbiternext(fdb) < 1){ int ecode = tcfdbecode(fdb); if(ecode != TCEINVALID && ecode != TCENOREC){ eprint(fdb, __LINE__, "tcfdbiternext"); err = true; } } } break; default: if(id == 0) iputchar('@'); if(tcfdbtranbegin(fdb)){ if(myrand(2) == 0){ if(!tcfdbput2(fdb, kbuf, ksiz, vbuf, vsiz)){ eprint(fdb, __LINE__, "tcfdbput"); err = true; } if(!nc) tcmapput(map, kbuf, ksiz, vbuf, vsiz); } else { if(!tcfdbout2(fdb, kbuf, ksiz) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbout"); err = true; } if(!nc) tcmapout(map, kbuf, ksiz); } if(nc && myrand(2) == 0){ if(!tcfdbtranabort(fdb)){ eprint(fdb, __LINE__, "tcfdbtranabort"); err = true; } } else { if(!tcfdbtrancommit(fdb)){ eprint(fdb, __LINE__, "tcfdbtrancommit"); err = true; } } } else { eprint(fdb, __LINE__, "tcfdbtranbegin"); err = true; } if(myrand(1000) == 0){ if(!tcfdbforeach(fdb, iterfunc, NULL)){ eprint(fdb, __LINE__, "tcfdbforeach"); err = true; } } if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX); break; } if(!nc) tcglobalmutexunlock(); if(id == 0){ if(i % 50 == 0) iprintf(" (%08d)\n", i); if(id == 0 && i == rnum / 4){ if(!tcfdboptimize(fdb, RECBUFSIZ, -1) && tcfdbecode(fdb) != TCEINVALID){ eprint(fdb, __LINE__, "tcfdboptimize"); err = true; } if(!tcfdbiterinit(fdb)){ eprint(fdb, __LINE__, "tcfdbiterinit"); err = true; } } } } return err ? "error" : NULL; } /* thread the typical function */ static void *threadtypical(void *targ){ TCFDB *fdb = ((TARGTYPICAL *)targ)->fdb; int rnum = ((TARGTYPICAL *)targ)->rnum; bool nc = ((TARGTYPICAL *)targ)->nc; int rratio = ((TARGTYPICAL *)targ)->rratio; int id = ((TARGTYPICAL *)targ)->id; bool err = false; TCMAP *map = (!nc && id == 0) ? tcmapnew2(rnum + 1) : NULL; int base = id * rnum; int mrange = tclmax(50 + rratio, 100); int width = tcfdbwidth(fdb); for(int i = 1; !err && i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", base + myrandnd(i) + 1); int rnd = myrand(mrange); if(rnd < 10){ if(!tcfdbput2(fdb, buf, len, buf, len)){ eprint(fdb, __LINE__, "tcfdbput2"); err = true; } if(map) tcmapput(map, buf, len, buf, len); } else if(rnd < 15){ if(!tcfdbputkeep2(fdb, buf, len, buf, len) && tcfdbecode(fdb) != TCEKEEP){ eprint(fdb, __LINE__, "tcfdbputkeep2"); err = true; } if(map) tcmapputkeep(map, buf, len, buf, len); } else if(rnd < 20){ if(!tcfdbputcat2(fdb, buf, len, buf, len)){ eprint(fdb, __LINE__, "tcfdbputcat2"); err = true; } if(map) tcmapputcat(map, buf, len, buf, len); } else if(rnd < 25){ if(!tcfdbout2(fdb, buf, len) && tcfdbecode(fdb) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbout"); err = true; } if(map) tcmapout(map, buf, len); } else if(rnd < 26){ if(myrand(10) == 0 && !tcfdbiterinit(fdb) && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbiterinit"); err = true; } for(int j = 0; !err && j < 10; j++){ if(tcfdbiternext(fdb) < 1 && tcfdbecode(fdb) != TCEINVALID && tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbiternext"); err = true; } } } else { int vsiz; char *vbuf = tcfdbget2(fdb, buf, len, &vsiz); if(vbuf){ if(map){ int msiz = 0; const char *mbuf = tcmapget(map, buf, len, &msiz); if(msiz > width) msiz = width; if(!mbuf || msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){ eprint(fdb, __LINE__, "(validation)"); err = true; } } tcfree(vbuf); } else { if(tcfdbecode(fdb) != TCENOREC){ eprint(fdb, __LINE__, "tcfdbget"); err = true; } if(map && tcmapget(map, buf, len, &vsiz)){ eprint(fdb, __LINE__, "(validation)"); err = true; } } } if(id == 0 && rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(map){ tcmapiterinit(map); int ksiz; const char *kbuf; while(!err && (kbuf = tcmapiternext(map, &ksiz)) != NULL){ int vsiz; char *vbuf = tcfdbget2(fdb, kbuf, ksiz, &vsiz); if(vbuf){ int msiz = 0; const char *mbuf = tcmapget(map, kbuf, ksiz, &msiz); if(msiz > width) msiz = width; if(!mbuf || msiz != vsiz || memcmp(mbuf, vbuf, vsiz)){ eprint(fdb, __LINE__, "(validation)"); err = true; } tcfree(vbuf); } else { eprint(fdb, __LINE__, "(validation)"); err = true; } } tcmapdel(map); } return err ? "error" : NULL; } // END OF FILE tokyocabinet-1.4.48/tcbtest.c0000644000175000017500000023365212013574446015142 0ustar mikiomikio/************************************************************************************************* * The test cases of the B+ tree database API * Copyright (C) 2006-2012 FAL Labs * This file is part of Tokyo Cabinet. * 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA. *************************************************************************************************/ #include #include #include "myconf.h" #define RECBUFSIZ 48 // buffer for records /* global variables */ const char *g_progname; // program name unsigned int g_randseed; // random seed int g_dbgfd; // debugging output /* function prototypes */ int main(int argc, char **argv); static void usage(void); static void iprintf(const char *format, ...); static void iputchar(int c); static void eprint(TCBDB *bdb, int line, const char *func); static void mprint(TCBDB *bdb); static void sysprint(void); static int myrand(int range); static void *pdprocfunc(const void *vbuf, int vsiz, int *sp, void *op); static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op); static int runwrite(int argc, char **argv); static int runread(int argc, char **argv); static int runremove(int argc, char **argv); static int runrcat(int argc, char **argv); static int runqueue(int argc, char **argv); static int runmisc(int argc, char **argv); static int runwicked(int argc, char **argv); static int procwrite(const char *path, int rnum, int lmemb, int nmemb, int bnum, int apow, int fpow, bool mt, TCCMP cmp, int opts, int lcnum, int ncnum, int xmsiz, int dfunit, int lsmax, int capnum, int omode, bool rnd); static int procread(const char *path, bool mt, TCCMP cmp, int lcnum, int ncnum, int xmsiz, int dfunit, int omode, bool wb, bool rnd); static int procremove(const char *path, bool mt, TCCMP cmp, int lcnum, int ncnum, int xmsiz, int dfunit, int omode, bool rnd); static int procrcat(const char *path, int rnum, int lmemb, int nmemb, int bnum, int apow, int fpow, bool mt, TCCMP cmp, int opts, int lcnum, int ncnum, int xmsiz, int dfunit, int lsmax, int capnum, int omode, int pnum, bool dai, bool dad, bool rl, bool ru); static int procqueue(const char *path, int rnum, int lmemb, int nmemb, int bnum, int apow, int fpow, bool mt, TCCMP cmp, int opts, int lcnum, int ncnum, int xmsiz, int dfunit, int lsmax, int capnum, int omode); static int procmisc(const char *path, int rnum, bool mt, int opts, int omode); static int procwicked(const char *path, int rnum, bool mt, int opts, int omode); /* main routine */ int main(int argc, char **argv){ g_progname = argv[0]; const char *ebuf = getenv("TCRNDSEED"); g_randseed = ebuf ? tcatoix(ebuf) : tctime() * 1000; srand(g_randseed); ebuf = getenv("TCDBGFD"); g_dbgfd = ebuf ? tcatoix(ebuf) : UINT16_MAX; if(argc < 2) usage(); int 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], "remove")){ rv = runremove(argc, argv); } else if(!strcmp(argv[1], "rcat")){ rv = runrcat(argc, argv); } else if(!strcmp(argv[1], "queue")){ rv = runqueue(argc, argv); } else if(!strcmp(argv[1], "misc")){ rv = runmisc(argc, argv); } else if(!strcmp(argv[1], "wicked")){ rv = runwicked(argc, argv); } else { usage(); } if(rv != 0){ printf("FAILED: TCRNDSEED=%u PID=%d", g_randseed, (int)getpid()); for(int i = 0; i < argc; i++){ printf(" %s", argv[i]); } printf("\n\n"); } return rv; } /* print the usage and exit */ static void usage(void){ fprintf(stderr, "%s: test cases of the B+ tree database API of Tokyo Cabinet\n", g_progname); fprintf(stderr, "\n"); fprintf(stderr, "usage:\n"); fprintf(stderr, " %s 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]]]]]\n", g_progname); fprintf(stderr, " %s read [-mt] [-cd|-ci|-cj] [-lc num] [-nc num] [-xm num] [-df num]" " [-nl|-nb] [-wb] [-rnd] path\n", g_progname); fprintf(stderr, " %s remove [-mt] [-cd|-ci|-cj] [-lc num] [-nc num] [-xm num] [-df num]" " [-nl|-nb] [-rnd] path\n", g_progname); fprintf(stderr, " %s 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]]]]]\n", g_progname); fprintf(stderr, " %s 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]]]]]\n", g_progname); fprintf(stderr, " %s misc [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path rnum\n", g_progname); fprintf(stderr, " %s wicked [-mt] [-tl] [-td|-tb|-tt|-tx] [-nl|-nb] path 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); } /* print a character and flush the buffer */ static void iputchar(int c){ putchar(c); fflush(stdout); } /* print error message of hash database */ static void eprint(TCBDB *bdb, int line, const char *func){ const char *path = tcbdbpath(bdb); int ecode = tcbdbecode(bdb); fprintf(stderr, "%s: %s: %d: %s: error: %d: %s\n", g_progname, path ? path : "-", line, func, ecode, tcbdberrmsg(ecode)); } /* print members of B+ tree database */ static void mprint(TCBDB *bdb){ if(bdb->hdb->cnt_writerec < 0) return; iprintf("max leaf member: %d\n", tcbdblmemb(bdb)); iprintf("max node member: %d\n", tcbdbnmemb(bdb)); iprintf("leaf number: %lld\n", (long long)tcbdblnum(bdb)); iprintf("node number: %lld\n", (long long)tcbdbnnum(bdb)); iprintf("bucket number: %lld\n", (long long)tcbdbbnum(bdb)); iprintf("used bucket number: %lld\n", (long long)tcbdbbnumused(bdb)); iprintf("cnt_saveleaf: %lld\n", (long long)bdb->cnt_saveleaf); iprintf("cnt_loadleaf: %lld\n", (long long)bdb->cnt_loadleaf); iprintf("cnt_killleaf: %lld\n", (long long)bdb->cnt_killleaf); iprintf("cnt_adjleafc: %lld\n", (long long)bdb->cnt_adjleafc); iprintf("cnt_savenode: %lld\n", (long long)bdb->cnt_savenode); iprintf("cnt_loadnode: %lld\n", (long long)bdb->cnt_loadnode); iprintf("cnt_adjnodec: %lld\n", (long long)bdb->cnt_adjnodec); iprintf("cnt_writerec: %lld\n", (long long)bdb->hdb->cnt_writerec); iprintf("cnt_reuserec: %lld\n", (long long)bdb->hdb->cnt_reuserec); iprintf("cnt_moverec: %lld\n", (long long)bdb->hdb->cnt_moverec); iprintf("cnt_readrec: %lld\n", (long long)bdb->hdb->cnt_readrec); iprintf("cnt_searchfbp: %lld\n", (long long)bdb->hdb->cnt_searchfbp); iprintf("cnt_insertfbp: %lld\n", (long long)bdb->hdb->cnt_insertfbp); iprintf("cnt_splicefbp: %lld\n", (long long)bdb->hdb->cnt_splicefbp); iprintf("cnt_dividefbp: %lld\n", (long long)bdb->hdb->cnt_dividefbp); iprintf("cnt_mergefbp: %lld\n", (long long)bdb->hdb->cnt_mergefbp); iprintf("cnt_reducefbp: %lld\n", (long long)bdb->hdb->cnt_reducefbp); iprintf("cnt_appenddrp: %lld\n", (long long)bdb->hdb->cnt_appenddrp); iprintf("cnt_deferdrp: %lld\n", (long long)bdb->hdb->cnt_deferdrp); iprintf("cnt_flushdrp: %lld\n", (long long)bdb->hdb->cnt_flushdrp); iprintf("cnt_adjrecc: %lld\n", (long long)bdb->hdb->cnt_adjrecc); iprintf("cnt_defrag: %lld\n", (long long)bdb->hdb->cnt_defrag); iprintf("cnt_shiftrec: %lld\n", (long long)bdb->hdb->cnt_shiftrec); iprintf("cnt_trunc: %lld\n", (long long)bdb->hdb->cnt_trunc); } /* print system information */ static void sysprint(void){ TCMAP *info = tcsysinfo(); if(info){ tcmapiterinit(info); const char *kbuf; while((kbuf = tcmapiternext2(info)) != NULL){ iprintf("sys_%s: %s\n", kbuf, tcmapiterval2(kbuf)); } tcmapdel(info); } } /* get a random number */ static int myrand(int range){ if(range < 2) return 0; int high = (unsigned int)rand() >> 4; int low = range * (rand() / (RAND_MAX + 1.0)); low &= (unsigned int)INT_MAX >> 4; return (high + low) % range; } /* duplication callback function */ static void *pdprocfunc(const void *vbuf, int vsiz, int *sp, void *op){ if(op){ char *buf = NULL; int len = 0; switch((int)(intptr_t)op){ case 1: len = vsiz + 1; buf = tcmalloc(len + 1); memset(buf, '*', len); break; case 2: buf = (void *)-1; break; } *sp = len; return buf; } if(myrand(4) == 0) return (void *)-1; if(myrand(2) == 0) return NULL; int len = myrand(RECBUFSIZ); char buf[RECBUFSIZ]; memset(buf, '*', len); *sp = len; return tcmemdup(buf, len); } /* iterator function */ static bool iterfunc(const void *kbuf, int ksiz, const void *vbuf, int vsiz, void *op){ unsigned int sum = 0; while(--ksiz >= 0){ sum += ((char *)kbuf)[ksiz]; } while(--vsiz >= 0){ sum += ((char *)vbuf)[vsiz]; } return myrand(100 + (sum & 0xff)) > 0; } /* parse arguments of write command */ static int runwrite(int argc, char **argv){ char *path = NULL; char *rstr = NULL; char *lmstr = NULL; char *nmstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; bool mt = false; TCCMP cmp = NULL; int opts = 0; int lcnum = 0; int ncnum = 0; int xmsiz = -1; int dfunit = 0; int lsmax = 0; int capnum = 0; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-cd")){ cmp = tccmpdecimal; } else if(!strcmp(argv[i], "-ci")){ cmp = tccmpint32; } else if(!strcmp(argv[i], "-cj")){ cmp = tccmpint64; } else if(!strcmp(argv[i], "-tl")){ opts |= BDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= BDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= BDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= BDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= BDBTEXCODEC; } else if(!strcmp(argv[i], "-lc")){ if(++i >= argc) usage(); lcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nc")){ if(++i >= argc) usage(); ncnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-ls")){ if(++i >= argc) usage(); lsmax = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-ca")){ if(++i >= argc) usage(); capnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!lmstr){ lmstr = argv[i]; } else if(!nmstr){ nmstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int lmemb = lmstr ? tcatoix(lmstr) : -1; int nmemb = nmstr ? tcatoix(nmstr) : -1; int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = procwrite(path, rnum, lmemb, nmemb, bnum, apow, fpow, mt, cmp, opts, lcnum, ncnum, xmsiz, dfunit, lsmax, capnum, omode, rnd); return rv; } /* parse arguments of read command */ static int runread(int argc, char **argv){ char *path = NULL; bool mt = false; TCCMP cmp = NULL; int lcnum = 0; int ncnum = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; bool wb = false; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-cd")){ cmp = tccmpdecimal; } else if(!strcmp(argv[i], "-ci")){ cmp = tccmpint32; } else if(!strcmp(argv[i], "-cj")){ cmp = tccmpint64; } else if(!strcmp(argv[i], "-lc")){ if(++i >= argc) usage(); lcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nc")){ if(++i >= argc) usage(); ncnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else if(!strcmp(argv[i], "-wb")){ wb = true; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procread(path, mt, cmp, lcnum, ncnum, xmsiz, dfunit, omode, wb, rnd); return rv; } /* parse arguments of remove command */ static int runremove(int argc, char **argv){ char *path = NULL; bool mt = false; TCCMP cmp = NULL; int lcnum = 0; int ncnum = 0; int xmsiz = -1; int dfunit = 0; int omode = 0; bool rnd = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-cd")){ cmp = tccmpdecimal; } else if(!strcmp(argv[i], "-ci")){ cmp = tccmpint32; } else if(!strcmp(argv[i], "-cj")){ cmp = tccmpint64; } else if(!strcmp(argv[i], "-lc")){ if(++i >= argc) usage(); lcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nc")){ if(++i >= argc) usage(); ncnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else if(!strcmp(argv[i], "-rnd")){ rnd = true; } else { usage(); } } else if(!path){ path = argv[i]; } else { usage(); } } if(!path) usage(); int rv = procremove(path, mt, cmp, lcnum, ncnum, xmsiz, dfunit, omode, rnd); return rv; } /* parse arguments of rcat command */ static int runrcat(int argc, char **argv){ char *path = NULL; char *rstr = NULL; char *lmstr = NULL; char *nmstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; bool mt = false; TCCMP cmp = NULL; int opts = 0; int lcnum = 0; int ncnum = 0; int xmsiz = -1; int dfunit = 0; int lsmax = 0; int capnum = 0; int omode = 0; int pnum = 0; bool dai = false; bool dad = false; bool rl = false; bool ru = false; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-cd")){ cmp = tccmpdecimal; } else if(!strcmp(argv[i], "-ci")){ cmp = tccmpint32; } else if(!strcmp(argv[i], "-cj")){ cmp = tccmpint64; } else if(!strcmp(argv[i], "-tl")){ opts |= BDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= BDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= BDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= BDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= BDBTEXCODEC; } else if(!strcmp(argv[i], "-lc")){ if(++i >= argc) usage(); lcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nc")){ if(++i >= argc) usage(); ncnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-ls")){ if(++i >= argc) usage(); lsmax = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-ca")){ if(++i >= argc) usage(); capnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else if(!strcmp(argv[i], "-pn")){ if(++i >= argc) usage(); pnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-dai")){ dai = true; } else if(!strcmp(argv[i], "-dad")){ dad = true; } else if(!strcmp(argv[i], "-rl")){ rl = true; } else if(!strcmp(argv[i], "-ru")){ ru = true; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!lmstr){ lmstr = argv[i]; } else if(!nmstr){ nmstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int lmemb = lmstr ? tcatoix(lmstr) : -1; int nmemb = nmstr ? tcatoix(nmstr) : -1; int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = procrcat(path, rnum, lmemb, nmemb, bnum, apow, fpow, mt, cmp, opts, lcnum, ncnum, xmsiz, dfunit, lsmax, capnum, omode, pnum, dai, dad, rl, ru); return rv; } /* parse arguments of queue command */ static int runqueue(int argc, char **argv){ char *path = NULL; char *rstr = NULL; char *lmstr = NULL; char *nmstr = NULL; char *bstr = NULL; char *astr = NULL; char *fstr = NULL; bool mt = false; TCCMP cmp = NULL; int opts = 0; int lcnum = 0; int ncnum = 0; int xmsiz = -1; int dfunit = 0; int lsmax = 0; int capnum = 0; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-cd")){ cmp = tccmpdecimal; } else if(!strcmp(argv[i], "-ci")){ cmp = tccmpint32; } else if(!strcmp(argv[i], "-cj")){ cmp = tccmpint64; } else if(!strcmp(argv[i], "-tl")){ opts |= BDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= BDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= BDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= BDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= BDBTEXCODEC; } else if(!strcmp(argv[i], "-lc")){ if(++i >= argc) usage(); lcnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nc")){ if(++i >= argc) usage(); ncnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-xm")){ if(++i >= argc) usage(); xmsiz = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-df")){ if(++i >= argc) usage(); dfunit = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-ls")){ if(++i >= argc) usage(); lsmax = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-ca")){ if(++i >= argc) usage(); capnum = tcatoix(argv[i]); } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else if(!lmstr){ lmstr = argv[i]; } else if(!nmstr){ nmstr = argv[i]; } else if(!bstr){ bstr = argv[i]; } else if(!astr){ astr = argv[i]; } else if(!fstr){ fstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int lmemb = lmstr ? tcatoix(lmstr) : -1; int nmemb = nmstr ? tcatoix(nmstr) : -1; int bnum = bstr ? tcatoix(bstr) : -1; int apow = astr ? tcatoix(astr) : -1; int fpow = fstr ? tcatoix(fstr) : -1; int rv = procqueue(path, rnum, lmemb, nmemb, bnum, apow, fpow, mt, cmp, opts, lcnum, ncnum, xmsiz, dfunit, lsmax, capnum, omode); return rv; } /* parse arguments of misc command */ static int runmisc(int argc, char **argv){ char *path = NULL; char *rstr = NULL; bool mt = false; int opts = 0; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-tl")){ opts |= BDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= BDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= BDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= BDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= BDBTEXCODEC; } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int rv = procmisc(path, rnum, mt, opts, omode); return rv; } /* parse arguments of wicked command */ static int runwicked(int argc, char **argv){ char *path = NULL; char *rstr = NULL; bool mt = false; int opts = 0; int omode = 0; for(int i = 2; i < argc; i++){ if(!path && argv[i][0] == '-'){ if(!strcmp(argv[i], "-mt")){ mt = true; } else if(!strcmp(argv[i], "-tl")){ opts |= BDBTLARGE; } else if(!strcmp(argv[i], "-td")){ opts |= BDBTDEFLATE; } else if(!strcmp(argv[i], "-tb")){ opts |= BDBTBZIP; } else if(!strcmp(argv[i], "-tt")){ opts |= BDBTTCBS; } else if(!strcmp(argv[i], "-tx")){ opts |= BDBTEXCODEC; } else if(!strcmp(argv[i], "-nl")){ omode |= BDBONOLCK; } else if(!strcmp(argv[i], "-nb")){ omode |= BDBOLCKNB; } else { usage(); } } else if(!path){ path = argv[i]; } else if(!rstr){ rstr = argv[i]; } else { usage(); } } if(!path || !rstr) usage(); int rnum = tcatoix(rstr); if(rnum < 1) usage(); int rv = procwicked(path, rnum, mt, opts, omode); return rv; } /* perform write command */ static int procwrite(const char *path, int rnum, int lmemb, int nmemb, int bnum, int apow, int fpow, bool mt, TCCMP cmp, int opts, int lcnum, int ncnum, int xmsiz, int dfunit, int lsmax, int capnum, int omode, bool rnd){ iprintf("\n seed=%u path=%s rnum=%d lmemb=%d nmemb=%d bnum=%d apow=%d" " fpow=%d mt=%d cmp=%p opts=%d lcnum=%d ncnum=%d xmsiz=%d dfunit=%d lsmax=%d" " capnum=%d omode=%d rnd=%d\n\n", g_randseed, path, rnum, lmemb, nmemb, bnum, apow, fpow, mt, (void *)(intptr_t)cmp, opts, lcnum, ncnum, xmsiz, dfunit, lsmax, capnum, omode, rnd); bool err = false; double stime = tctime(); TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(mt && !tcbdbsetmutex(bdb)){ eprint(bdb, __LINE__, "tcbdbsetmutex"); err = true; } if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcmpfunc"); err = true; } if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcodecfunc"); err = true; } if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){ eprint(bdb, __LINE__, "tcbdbtune"); err = true; } if(!tcbdbsetcache(bdb, lcnum, ncnum)){ eprint(bdb, __LINE__, "tcbdbsetcache"); err = true; } if(xmsiz >= 0 && !tcbdbsetxmsiz(bdb, xmsiz)){ eprint(bdb, __LINE__, "tcbdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tcbdbsetdfunit(bdb, dfunit)){ eprint(bdb, __LINE__, "tcbdbsetdfunit"); err = true; } if(!tcbdbsetlsmax(bdb, lsmax)){ eprint(bdb, __LINE__, "tcbdbsetlsmax"); err = true; } if(!tcbdbsetcapnum(bdb, capnum)){ eprint(bdb, __LINE__, "tcbdbsetcapnum"); err = true; } if(!rnd) omode |= BDBOTRUNC; if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | omode)){ eprint(bdb, __LINE__, "tcbdbopen"); err = true; } for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len; if(cmp == tccmpdecimal){ len = sprintf(buf, "%d", rnd ? myrand(rnum) + 1 : i); } else if(cmp == tccmpint32){ int32_t lnum = rnd ? myrand(rnum) + 1 : i; memcpy(buf, &lnum, sizeof(lnum)); len = sizeof(lnum); } else if(cmp == tccmpint64){ int64_t llnum = rnd ? myrand(rnum) + 1 : i; memcpy(buf, &llnum, sizeof(llnum)); len = sizeof(llnum); } else { len = sprintf(buf, "%08d", rnd ? myrand(rnum) + 1 : i); } if(!tcbdbput(bdb, buf, len, buf, len)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb)); iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb)); mprint(bdb); sysprint(); if(!tcbdbclose(bdb)){ eprint(bdb, __LINE__, "tcbdbclose"); err = true; } tcbdbdel(bdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform read command */ static int procread(const char *path, bool mt, TCCMP cmp, int lcnum, int ncnum, int xmsiz, int dfunit, int omode, bool wb, bool rnd){ iprintf("\n seed=%u path=%s mt=%d cmp=%p lcnum=%d ncnum=%d" " xmsiz=%d dfunit=%d omode=%d wb=%d rnd=%d\n\n", g_randseed, path, mt, (void *)(intptr_t)cmp, lcnum, ncnum, xmsiz, dfunit, omode, wb, rnd); bool err = false; double stime = tctime(); TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(mt && !tcbdbsetmutex(bdb)){ eprint(bdb, __LINE__, "tcbdbsetmutex"); err = true; } if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcmpfunc"); err = true; } if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcodecfunc"); err = true; } if(!tcbdbsetcache(bdb, lcnum, ncnum)){ eprint(bdb, __LINE__, "tcbdbsetcache"); err = true; } if(xmsiz >= 0 && !tcbdbsetxmsiz(bdb, xmsiz)){ eprint(bdb, __LINE__, "tcbdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tcbdbsetdfunit(bdb, dfunit)){ eprint(bdb, __LINE__, "tcbdbsetdfunit"); err = true; } if(!tcbdbopen(bdb, path, BDBOREADER | omode)){ eprint(bdb, __LINE__, "tcbdbopen"); err = true; } int rnum = tcbdbrnum(bdb); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz; if(cmp == tccmpdecimal){ ksiz = sprintf(kbuf, "%d", rnd ? myrand(rnum) + 1 : i); } else if(cmp == tccmpint32){ int32_t lnum = rnd ? myrand(rnum) + 1 : i; memcpy(kbuf, &lnum, sizeof(lnum)); ksiz = sizeof(lnum); } else if(cmp == tccmpint64){ int64_t llnum = rnd ? myrand(rnum) + 1 : i; memcpy(kbuf, &llnum, sizeof(llnum)); ksiz = sizeof(llnum); } else { ksiz = sprintf(kbuf, "%08d", rnd ? myrand(rnum) + 1 : i); } int vsiz; if(wb){ int vsiz; const char *vbuf = tcbdbget3(bdb, kbuf, ksiz, &vsiz); if(!vbuf && !(rnd && tcbdbecode(bdb) == TCENOREC)){ eprint(bdb, __LINE__, "tcbdbget3"); err = true; break; } } else { char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz); if(!vbuf && !(rnd && tcbdbecode(bdb) == TCENOREC)){ eprint(bdb, __LINE__, "tcbdbget"); err = true; break; } tcfree(vbuf); } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb)); iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb)); mprint(bdb); sysprint(); if(!tcbdbclose(bdb)){ eprint(bdb, __LINE__, "tcbdbclose"); err = true; } tcbdbdel(bdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform remove command */ static int procremove(const char *path, bool mt, TCCMP cmp, int lcnum, int ncnum, int xmsiz, int dfunit, int omode, bool rnd){ iprintf("\n seed=%u path=%s mt=%d cmp=%p lcnum=%d ncnum=%d" " xmsiz=%d dfunit=%d omode=%d rnd=%d\n\n", g_randseed, path, mt, (void *)(intptr_t)cmp, lcnum, ncnum, xmsiz, dfunit, omode, rnd); bool err = false; double stime = tctime(); TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(mt && !tcbdbsetmutex(bdb)){ eprint(bdb, __LINE__, "tcbdbsetmutex"); err = true; } if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcmpfunc"); err = true; } if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcodecfunc"); err = true; } if(!tcbdbsetcache(bdb, lcnum, ncnum)){ eprint(bdb, __LINE__, "tcbdbsetcache"); err = true; } if(xmsiz >= 0 && !tcbdbsetxmsiz(bdb, xmsiz)){ eprint(bdb, __LINE__, "tcbdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tcbdbsetdfunit(bdb, dfunit)){ eprint(bdb, __LINE__, "tcbdbsetdfunit"); err = true; } if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){ eprint(bdb, __LINE__, "tcbdbopen"); err = true; } int rnum = tcbdbrnum(bdb); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz; if(cmp == tccmpdecimal){ ksiz = sprintf(kbuf, "%d", rnd ? myrand(rnum) + 1 : i); } else if(cmp == tccmpint32){ int32_t lnum = rnd ? myrand(rnum) + 1 : i; memcpy(kbuf, &lnum, sizeof(lnum)); ksiz = sizeof(lnum); } else if(cmp == tccmpint64){ int64_t llnum = rnd ? myrand(rnum) + 1 : i; memcpy(kbuf, &llnum, sizeof(llnum)); ksiz = sizeof(llnum); } else { ksiz = sprintf(kbuf, "%08d", rnd ? myrand(rnum) + 1 : i); } if(!tcbdbout(bdb, kbuf, ksiz) && !(rnd && tcbdbecode(bdb) == TCENOREC)){ eprint(bdb, __LINE__, "tcbdbout"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb)); iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb)); mprint(bdb); sysprint(); if(!tcbdbclose(bdb)){ eprint(bdb, __LINE__, "tcbdbclose"); err = true; } tcbdbdel(bdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform rcat command */ static int procrcat(const char *path, int rnum, int lmemb, int nmemb, int bnum, int apow, int fpow, bool mt, TCCMP cmp, int opts, int lcnum, int ncnum, int xmsiz, int dfunit, int lsmax, int capnum, int omode, int pnum, bool dai, bool dad, bool rl, bool ru){ iprintf("\n" " seed=%u path=%s rnum=%d lmemb=%d nmemb=%d bnum=%d apow=%d fpow=%d" " mt=%d cmp=%p opts=%d lcnum=%d ncnum=%d xmsiz=%d dfunit=%d" " lsmax=%d capnum=%d omode=%d pnum=%d dai=%d dad=%d rl=%d ru=%d\n\n", g_randseed, path, rnum, lmemb, nmemb, bnum, apow, fpow, mt, (void *)(intptr_t)cmp, opts, lcnum, ncnum, xmsiz, dfunit, lsmax, capnum, omode, pnum, dai, dad, rl, ru); if(pnum < 1) pnum = rnum; bool err = false; double stime = tctime(); TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(mt && !tcbdbsetmutex(bdb)){ eprint(bdb, __LINE__, "tcbdbsetmutex"); err = true; } if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcmpfunc"); err = true; } if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcodecfunc"); err = true; } if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){ eprint(bdb, __LINE__, "tcbdbtune"); err = true; } if(!tcbdbsetcache(bdb, lcnum, ncnum)){ eprint(bdb, __LINE__, "tcbdbsetcache"); err = true; } if(xmsiz >= 0 && !tcbdbsetxmsiz(bdb, xmsiz)){ eprint(bdb, __LINE__, "tcbdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tcbdbsetdfunit(bdb, dfunit)){ eprint(bdb, __LINE__, "tcbdbsetdfunit"); err = true; } if(!tcbdbsetlsmax(bdb, lsmax)){ eprint(bdb, __LINE__, "tcbdbsetlsmax"); err = true; } if(!tcbdbsetcapnum(bdb, capnum)){ eprint(bdb, __LINE__, "tcbdbsetcapnum"); err = true; } if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){ eprint(bdb, __LINE__, "tcbdbopen"); err = true; } for(int i = 1; i <= rnum; i++){ if(ru){ char fmt[RECBUFSIZ]; sprintf(fmt, "%%0%dd", myrand(RECBUFSIZ)); char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, fmt, myrand(pnum)); switch(myrand(10)){ case 0: if(!tcbdbput(bdb, kbuf, ksiz, kbuf, ksiz)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; } break; case 1: if(!tcbdbputkeep(bdb, kbuf, ksiz, kbuf, ksiz) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbputkeep"); err = true; } break; case 2: if(!tcbdbputdup(bdb, kbuf, ksiz, kbuf, ksiz) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbputkeep"); err = true; } break; case 3: if(!tcbdbputdupback(bdb, kbuf, ksiz, kbuf, ksiz) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbputkeep"); err = true; } break; case 4: if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout"); err = true; } break; case 5: if(tcbdbaddint(bdb, kbuf, ksiz, 1) == INT_MIN && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbaddint"); err = true; } break; case 6: if(isnan(tcbdbadddouble(bdb, kbuf, ksiz, 1.0)) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbadddouble"); err = true; } break; case 7: if(myrand(2) == 0){ if(!tcbdbputproc(bdb, kbuf, ksiz, kbuf, ksiz, pdprocfunc, NULL) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbputproc"); err = true; } } else { if(!tcbdbputproc(bdb, kbuf, ksiz, NULL, 0, pdprocfunc, NULL) && tcbdbecode(bdb) != TCEKEEP && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbputproc"); err = true; } } break; default: if(!tcbdbputcat(bdb, kbuf, ksiz, kbuf, ksiz)){ eprint(bdb, __LINE__, "tcbdbputcat"); err = true; } break; } if(err) break; } else { char kbuf[RECBUFSIZ]; int ksiz; if(cmp == tccmpdecimal){ ksiz = sprintf(kbuf, "%d", myrand(pnum)); } else if(cmp == tccmpint32){ int32_t lnum = myrand(pnum); memcpy(kbuf, &lnum, sizeof(lnum)); ksiz = sizeof(lnum); } else if(cmp == tccmpint64){ int64_t llnum = myrand(pnum); memcpy(kbuf, &llnum, sizeof(llnum)); ksiz = sizeof(llnum); } else { ksiz = sprintf(kbuf, "%d", myrand(pnum)); } if(dai){ if(tcbdbaddint(bdb, kbuf, ksiz, myrand(3)) == INT_MIN){ eprint(bdb, __LINE__, "tcbdbaddint"); err = true; break; } } else if(dad){ if(isnan(tcbdbadddouble(bdb, kbuf, ksiz, myrand(30) / 10.0))){ eprint(bdb, __LINE__, "tcbdbadddouble"); err = true; break; } } else if(rl){ char vbuf[PATH_MAX]; int vsiz = myrand(PATH_MAX); for(int j = 0; j < vsiz; j++){ vbuf[j] = myrand(0x100); } if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbputcat"); err = true; break; } } else { if(!tcbdbputcat(bdb, kbuf, ksiz, kbuf, ksiz)){ eprint(bdb, __LINE__, "tcbdbputcat"); err = true; break; } } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb)); iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb)); mprint(bdb); sysprint(); if(!tcbdbclose(bdb)){ eprint(bdb, __LINE__, "tcbdbclose"); err = true; } tcbdbdel(bdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform queue command */ static int procqueue(const char *path, int rnum, int lmemb, int nmemb, int bnum, int apow, int fpow, bool mt, TCCMP cmp, int opts, int lcnum, int ncnum, int xmsiz, int dfunit, int lsmax, int capnum, int omode){ iprintf("\n seed=%u path=%s rnum=%d lmemb=%d nmemb=%d bnum=%d apow=%d" " fpow=%d mt=%d cmp=%p opts=%d lcnum=%d ncnum=%d xmsiz=%d dfunit=%d" " lsmax=%d capnum=%d omode=%d\n\n", g_randseed, path, rnum, lmemb, nmemb, bnum, apow, fpow, mt, (void *)(intptr_t)cmp, opts, lcnum, ncnum, xmsiz, dfunit, lsmax, capnum, omode); bool err = false; double stime = tctime(); TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(mt && !tcbdbsetmutex(bdb)){ eprint(bdb, __LINE__, "tcbdbsetmutex"); err = true; } if(cmp && !tcbdbsetcmpfunc(bdb, cmp, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcmpfunc"); err = true; } if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcodecfunc"); err = true; } if(!tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts)){ eprint(bdb, __LINE__, "tcbdbtune"); err = true; } if(!tcbdbsetcache(bdb, lcnum, ncnum)){ eprint(bdb, __LINE__, "tcbdbsetcache"); err = true; } if(xmsiz >= 0 && !tcbdbsetxmsiz(bdb, xmsiz)){ eprint(bdb, __LINE__, "tcbdbsetxmsiz"); err = true; } if(dfunit >= 0 && !tcbdbsetdfunit(bdb, dfunit)){ eprint(bdb, __LINE__, "tcbdbsetdfunit"); err = true; } if(!tcbdbsetlsmax(bdb, lsmax)){ eprint(bdb, __LINE__, "tcbdbsetlsmax"); err = true; } if(!tcbdbsetcapnum(bdb, capnum)){ eprint(bdb, __LINE__, "tcbdbsetcapnum"); err = true; } if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC)){ eprint(bdb, __LINE__, "tcbdbopen"); err = true; } int deqfreq = (lmemb > 0) ? lmemb * 10 : 256; BDBCUR *cur = tcbdbcurnew(bdb); for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len; if(cmp == tccmpdecimal){ len = sprintf(buf, "%d", i); } else if(cmp == tccmpint32){ int32_t lnum = i; memcpy(buf, &lnum, sizeof(lnum)); len = sizeof(lnum); } else if(cmp == tccmpint64){ int64_t llnum = i; memcpy(buf, &llnum, sizeof(llnum)); len = sizeof(llnum); } else { len = sprintf(buf, "%08d", i); } if(!tcbdbput(bdb, buf, len, buf, len)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; break; } if(myrand(deqfreq) == 0){ if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurfirst"); err = true; break; } int num = myrand(deqfreq * 2 + 1); while(num >= 0){ if(tcbdbcurout(cur)){ num--; } else { if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurout"); err = true; } break; } } if(err) break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurfirst"); err = true; } while(true){ if(tcbdbcurout(cur)) continue; if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurout"); err = true; } break; } tcbdbcurdel(cur); iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb)); iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb)); mprint(bdb); sysprint(); if(!tcbdbclose(bdb)){ eprint(bdb, __LINE__, "tcbdbclose"); err = true; } tcbdbdel(bdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform misc command */ static int procmisc(const char *path, int rnum, bool mt, int opts, int omode){ iprintf("\n seed=%u path=%s rnum=%d mt=%d opts=%d omode=%d\n\n", g_randseed, path, rnum, mt, opts, omode); bool err = false; double stime = tctime(); TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(mt && !tcbdbsetmutex(bdb)){ eprint(bdb, __LINE__, "tcbdbsetmutex"); err = true; } if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcodecfunc"); err = true; } if(!tcbdbtune(bdb, 10, 10, rnum / 50, 100, -1, opts)){ eprint(bdb, __LINE__, "tcbdbtune"); err = true; } if(!tcbdbsetcache(bdb, 128, 256)){ eprint(bdb, __LINE__, "tcbdbsetcache"); err = true; } if(!tcbdbsetxmsiz(bdb, rnum)){ eprint(bdb, __LINE__, "tcbdbsetxmsiz"); err = true; } if(!tcbdbsetdfunit(bdb, 8)){ eprint(bdb, __LINE__, "tcbdbsetdfunit"); err = true; } if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){ eprint(bdb, __LINE__, "tcbdbopen"); err = true; } if(TCUSEPTHREAD){ TCBDB *bdbdup = tcbdbnew(); if(tcbdbopen(bdbdup, path, BDBOREADER)){ eprint(bdb, __LINE__, "(validation)"); err = true; } else if(tcbdbecode(bdbdup) != TCETHREAD){ eprint(bdb, __LINE__, "(validation)"); err = true; } tcbdbdel(bdbdup); } iprintf("writing:\n"); for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", i); if(!tcbdbputkeep(bdb, buf, len, buf, len)){ eprint(bdb, __LINE__, "tcbdbputkeep"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("reading:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", i); int vsiz; char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz); if(!vbuf){ eprint(bdb, __LINE__, "tcbdbget"); err = true; break; } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){ eprint(bdb, __LINE__, "(validation)"); err = true; tcfree(vbuf); break; } tcfree(vbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(tcbdbrnum(bdb) != rnum){ eprint(bdb, __LINE__, "(validation)"); err = true; } iprintf("random writing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); char vbuf[RECBUFSIZ]; int vsiz = myrand(RECBUFSIZ); memset(vbuf, '*', vsiz); if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; break; } int rsiz; char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz); if(!rbuf){ eprint(bdb, __LINE__, "tcbdbget"); err = true; break; } if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(bdb, __LINE__, "(validation)"); err = true; tcfree(rbuf); break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } tcfree(rbuf); } iprintf("word writing:\n"); const char *words[] = { "a", "A", "bb", "BB", "ccc", "CCC", "dddd", "DDDD", "eeeee", "EEEEEE", "mikio", "hirabayashi", "tokyo", "cabinet", "hyper", "estraier", "19780211", "birth day", "one", "first", "two", "second", "three", "third", "four", "fourth", "five", "fifth", "_[1]_", "uno", "_[2]_", "dos", "_[3]_", "tres", "_[4]_", "cuatro", "_[5]_", "cinco", "[\xe5\xb9\xb3\xe6\x9e\x97\xe5\xb9\xb9\xe9\x9b\x84]", "[\xe9\xa6\xac\xe9\xb9\xbf]", NULL }; for(int i = 0; words[i] != NULL; i += 2){ const char *kbuf = words[i]; int ksiz = strlen(kbuf); const char *vbuf = words[i+1]; int vsiz = strlen(vbuf); if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbputkeep"); err = true; break; } if(rnum > 250) iputchar('.'); } if(rnum > 250) iprintf(" (%08d)\n", (int)(sizeof(words) / sizeof(*words))); iprintf("random erasing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout"); err = true; break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("writing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "[%d]", i); char vbuf[RECBUFSIZ]; int vsiz = i % RECBUFSIZ; memset(vbuf, '*', vsiz); if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbputkeep"); err = true; break; } if(vsiz < 1){ char tbuf[PATH_MAX]; for(int j = 0; j < PATH_MAX; j++){ tbuf[j] = myrand(0x100); } if(!tcbdbput(bdb, kbuf, ksiz, tbuf, PATH_MAX)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("erasing:\n"); for(int i = 1; i <= rnum; i++){ if(i % 2 == 1){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "[%d]", i); if(!tcbdbout(bdb, kbuf, ksiz)){ eprint(bdb, __LINE__, "tcbdbout"); err = true; break; } if(tcbdbout(bdb, kbuf, ksiz) || tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("random writing and reopening:\n"); for(int i = 1; i <= rnum; i++){ if(myrand(10) == 0){ int ksiz, vsiz; char *kbuf, *vbuf; ksiz = (myrand(5) == 0) ? myrand(UINT16_MAX) : myrand(RECBUFSIZ); kbuf = tcmalloc(ksiz + 1); for(int j = 0; j < ksiz; j++){ kbuf[j] = 128 + myrand(128); } vsiz = (myrand(5) == 0) ? myrand(UINT16_MAX) : myrand(RECBUFSIZ); vbuf = tcmalloc(vsiz + 1); for(int j = 0; j < vsiz; j++){ vbuf[j] = myrand(256); } switch(myrand(5)){ case 0: if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; } break; case 1: if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbputcat"); err = true; } break; case 2: if(!tcbdbputdup(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbputdup"); err = true; } break; case 3: if(!tcbdbputdupback(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbputdupback"); err = true; } break; default: if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout"); err = true; } break; } tcfree(vbuf); tcfree(kbuf); } else { char kbuf[RECBUFSIZ]; int ksiz = myrand(RECBUFSIZ); memset(kbuf, '@', ksiz); char vbuf[RECBUFSIZ]; int vsiz = myrand(RECBUFSIZ); memset(vbuf, '@', vsiz); if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbputcat"); err = true; break; } } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tcbdbclose(bdb)){ eprint(bdb, __LINE__, "tcbdbclose"); err = true; } if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){ eprint(bdb, __LINE__, "tcbdbopen"); err = true; } iprintf("checking:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "[%d]", i); int vsiz; char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz); if(i % 2 == 0){ if(!vbuf){ eprint(bdb, __LINE__, "tcbdbget"); err = true; break; } if(vsiz != i % RECBUFSIZ && vsiz != PATH_MAX){ eprint(bdb, __LINE__, "(validation)"); err = true; tcfree(vbuf); break; } } else { if(vbuf || tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "(validation)"); err = true; tcfree(vbuf); break; } } tcfree(vbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("writing:\n"); for(int i = 1; i <= rnum; i++){ char buf[RECBUFSIZ]; int len = sprintf(buf, "%08d", i); if(!tcbdbput(bdb, buf, len, buf, len)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; break; } if(i % 10 == 0){ TCLIST *vals = tclistnew(); for(int j = myrand(5) + 1; j >= 0; j--){ tclistpush(vals, buf, len); } if(!tcbdbputdup3(bdb, buf, len, vals)){ eprint(bdb, __LINE__, "tcbdbput3"); err = true; break; } tclistdel(vals); } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("reading:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%08d", i); int vsiz; char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz); if(!vbuf){ eprint(bdb, __LINE__, "tcbdbget"); err = true; break; } else if(vsiz != ksiz || memcmp(vbuf, kbuf, vsiz)){ eprint(bdb, __LINE__, "(validation)"); err = true; tcfree(vbuf); break; } tcfree(vbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } iprintf("checking words:\n"); for(int i = 0; words[i] != NULL; i += 2){ const char *kbuf = words[i]; int ksiz = strlen(kbuf); const char *vbuf = words[i+1]; int vsiz = strlen(vbuf); int rsiz; char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz); if(!rbuf){ eprint(bdb, __LINE__, "tcbdbget"); err = true; break; } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(bdb, __LINE__, "(validation)"); err = true; tcfree(rbuf); break; } tcfree(rbuf); if(rnum > 250) iputchar('.'); } if(rnum > 250) iprintf(" (%08d)\n", (int)(sizeof(words) / sizeof(*words))); iprintf("checking cursor:\n"); BDBCUR *cur = tcbdbcurnew(bdb); int inum = 0; if(!tcbdbcurfirst(cur)){ eprint(bdb, __LINE__, "tcbdbcurfirst"); err = true; } char *kbuf; int ksiz; for(int i = 1; (kbuf = tcbdbcurkey(cur, &ksiz)) != NULL; i++, inum++){ int vsiz; char *vbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz); if(!vbuf){ eprint(bdb, __LINE__, "tcbdbget"); err = true; tcfree(kbuf); break; } tcfree(vbuf); tcfree(kbuf); tcbdbcurnext(cur); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); if(tcbdbecode(bdb) != TCENOREC || inum != tcbdbrnum(bdb)){ eprint(bdb, __LINE__, "(validation)"); err = true; } iprintf("cursor updating:\n"); if(!tcbdbcurfirst(cur)){ eprint(bdb, __LINE__, "tcbdbcurfirst"); err = true; } inum = 0; for(int i = 1; !err && (kbuf = tcbdbcurkey(cur, &ksiz)) != NULL; i++, inum++){ switch(myrand(6)){ case 0: if(!tcbdbputdup(bdb, kbuf, ksiz, "0123456789", 10)){ eprint(bdb, __LINE__, "tcbdbputcat"); err = true; } break; case 1: if(!tcbdbout(bdb, kbuf, ksiz)){ eprint(bdb, __LINE__, "tcbdbout"); err = true; } break; case 2: if(!tcbdbcurput(cur, kbuf, ksiz, BDBCPCURRENT)){ eprint(bdb, __LINE__, "tcbdbcurput"); err = true; } break; case 3: if(!tcbdbcurput(cur, kbuf, ksiz, BDBCPBEFORE)){ eprint(bdb, __LINE__, "tcbdbcurput"); err = true; } break; case 4: if(!tcbdbcurput(cur, kbuf, ksiz, BDBCPAFTER)){ eprint(bdb, __LINE__, "tcbdbcurput"); err = true; } break; default: if(!tcbdbcurout(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurout"); err = true; } break; } tcfree(kbuf); tcbdbcurnext(cur); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "(validation)"); err = true; } if(myrand(10) == 0 && !tcbdbsync(bdb)){ eprint(bdb, __LINE__, "tcbdbsync"); err = true; } iprintf("cursor updating from empty:\n"); tcbdbcurfirst(cur); inum = 0; for(int i = 1; (kbuf = tcbdbcurkey(cur, &ksiz)) != NULL; i++, inum++){ tcfree(kbuf); if(!tcbdbcurout(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurout"); err = true; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); if(tcbdbrnum(bdb) != 0){ eprint(bdb, __LINE__, "(validation)"); err = true; } if(!tcbdbput2(bdb, "one", "first")){ eprint(bdb, __LINE__, "tcbdbput"); err = true; } if(!tcbdbcurlast(cur)){ eprint(bdb, __LINE__, "tcbdbcurlast"); err = true; } if(!tcbdbcurput2(cur, "second", BDBCPCURRENT) || !tcbdbcurput2(cur, "first", BDBCPBEFORE) || !tcbdbcurput2(cur, "zero", BDBCPBEFORE) || !tcbdbcurput2(cur, "top", BDBCPBEFORE)){ eprint(bdb, __LINE__, "tcbdbcurput2"); err = true; } if(!tcbdbcurlast(cur)){ eprint(bdb, __LINE__, "tcbdbcurlast"); err = true; } if(!tcbdbcurput2(cur, "third", BDBCPAFTER) || !tcbdbcurput2(cur, "fourth", BDBCPAFTER) || !tcbdbcurput2(cur, "end", BDBCPCURRENT) || !tcbdbcurput2(cur, "bottom", BDBCPAFTER)){ eprint(bdb, __LINE__, "tcbdbcurput2"); err = true; } if(!tcbdbvanish(bdb)){ eprint(bdb, __LINE__, "tcbdbvanish"); err = true; } TCMAP *map = tcmapnew(); iprintf("random writing:\n"); for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); char vbuf[RECBUFSIZ]; int vsiz = sprintf(vbuf, "%d", myrand(rnum)); switch(myrand(4)){ case 0: if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; } tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbputkeep"); err = true; } tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 2: if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbputcat"); err = true; } tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 3: if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout"); err = true; } tcmapout(map, kbuf, ksiz); break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(myrand(4) == 0 && !tcbdbdefrag(bdb, 0)){ eprint(bdb, __LINE__, "tcbdbdefrag"); err = true; } if(myrand(4) == 0 && !tcbdbcacheclear(bdb)){ eprint(bdb, __LINE__, "tcbdbcacheclear"); err = true; } iprintf("checking transaction commit:\n"); if(!tcbdbtranbegin(bdb)){ eprint(bdb, __LINE__, "tcbdbtranbegin"); err = true; } for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); char vbuf[RECBUFSIZ]; int vsiz = sprintf(vbuf, "[%d]", myrand(rnum)); switch(myrand(7)){ case 0: if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; } tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbputkeep"); err = true; } tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 2: if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbputcat"); err = true; } tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 3: if(tcbdbaddint(bdb, kbuf, ksiz, 1) == INT_MIN && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbaddint"); err = true; } tcmapaddint(map, kbuf, ksiz, 1); break; case 4: if(isnan(tcbdbadddouble(bdb, kbuf, ksiz, 1.0)) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbadddouble"); err = true; } tcmapadddouble(map, kbuf, ksiz, 1.0); break; case 5: if(myrand(2) == 0){ void *op = (void *)(intptr_t)(myrand(3) + 1); if(!tcbdbputproc(bdb, kbuf, ksiz, vbuf, vsiz, pdprocfunc, op) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbputproc"); err = true; } tcmapputproc(map, kbuf, ksiz, vbuf, vsiz, pdprocfunc, op); } else { vsiz = myrand(10); void *op = (void *)(intptr_t)(myrand(3) + 1); if(!tcbdbputproc(bdb, kbuf, ksiz, NULL, vsiz, pdprocfunc, op) && tcbdbecode(bdb) != TCEKEEP && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbputproc"); err = true; } tcmapputproc(map, kbuf, ksiz, NULL, vsiz, pdprocfunc, op); } break; case 6: if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout"); err = true; } tcmapout(map, kbuf, ksiz); break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tcbdbtrancommit(bdb)){ eprint(bdb, __LINE__, "tcbdbtrancommit"); err = true; } iprintf("checking transaction abort:\n"); uint64_t ornum = tcbdbrnum(bdb); uint64_t ofsiz = tcbdbfsiz(bdb); if(!tcbdbtranbegin(bdb)){ eprint(bdb, __LINE__, "tcbdbtranbegin"); err = true; } for(int i = 1; i <= rnum; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); char vbuf[RECBUFSIZ]; int vsiz = sprintf(vbuf, "((%d))", myrand(rnum)); switch(myrand(8)){ case 0: if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; } break; case 1: if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbputkeep"); err = true; } break; case 2: if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbputcat"); err = true; } break; case 3: if(!tcbdbputdup(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbputdup"); err = true; } break; case 4: if(tcbdbaddint(bdb, kbuf, ksiz, 1) == INT_MIN && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbaddint"); err = true; } break; case 5: if(isnan(tcbdbadddouble(bdb, kbuf, ksiz, 1.0)) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbadddouble"); err = true; } break; case 6: if(myrand(2) == 0){ void *op = (void *)(intptr_t)(myrand(3) + 1); if(!tcbdbputproc(bdb, kbuf, ksiz, vbuf, vsiz, pdprocfunc, op) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbputproc"); err = true; } } else { vsiz = myrand(10); void *op = (void *)(intptr_t)(myrand(3) + 1); if(!tcbdbputproc(bdb, kbuf, ksiz, NULL, vsiz, pdprocfunc, op) && tcbdbecode(bdb) != TCEKEEP && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbputproc"); err = true; } } break; case 7: if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout"); err = true; } break; } if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(!tcbdbtranabort(bdb)){ eprint(bdb, __LINE__, "tcbdbtranabort"); err = true; } iprintf("checking consistency:\n"); if(tcbdbrnum(bdb) != ornum || tcbdbfsiz(bdb) != ofsiz || tcbdbrnum(bdb) != tcmaprnum(map)){ eprint(bdb, __LINE__, "(validation)"); err = true; } inum = 0; tcmapiterinit(map); const char *tkbuf; int tksiz; for(int i = 1; (tkbuf = tcmapiternext(map, &tksiz)) != NULL; i++, inum++){ int tvsiz; const char *tvbuf = tcmapiterval(tkbuf, &tvsiz); int rsiz; char *rbuf = tcbdbget(bdb, tkbuf, tksiz, &rsiz); if(!rbuf || rsiz != tvsiz || memcmp(rbuf, tvbuf, rsiz)){ eprint(bdb, __LINE__, "(validation)"); err = true; tcfree(rbuf); break; } tcfree(rbuf); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); inum = 0; if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurfirst"); err = true; } for(int i = 1; (kbuf = tcbdbcurkey(cur, &ksiz)) != NULL; i++, inum++){ int vsiz; char *vbuf = tcbdbcurval(cur, &vsiz); int rsiz; const char *rbuf = tcmapget(map, kbuf, ksiz, &rsiz); if(!rbuf || rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(bdb, __LINE__, "(validation)"); err = true; tcfree(vbuf); tcfree(kbuf); break; } tcfree(vbuf); tcfree(kbuf); tcbdbcurnext(cur); if(rnum > 250 && i % (rnum / 250) == 0){ iputchar('.'); if(i == rnum || i % (rnum / 10) == 0) iprintf(" (%08d)\n", i); } } if(rnum > 250) iprintf(" (%08d)\n", inum); tcmapdel(map); if(!tcbdbvanish(bdb)){ eprint(bdb, __LINE__, "tcbdbvanish"); err = true; } if(!tcbdbtranbegin(bdb)){ eprint(bdb, __LINE__, "tcbdbtranbegin"); err = true; } if(!tcbdbput2(bdb, "mikio", "hirabayashi")){ eprint(bdb, __LINE__, "tcbdbput2"); err = true; } for(int i = 0; i < 10; i++){ char buf[RECBUFSIZ]; int size = sprintf(buf, "%d", myrand(rnum)); if(!tcbdbput(bdb, buf, size, buf, size)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; } } for(int i = myrand(3) + 1; i < PATH_MAX; i = i * 2 + myrand(3)){ char vbuf[i]; memset(vbuf, '@', i - 1); vbuf[i-1] = '\0'; if(!tcbdbput2(bdb, "mikio", vbuf)){ eprint(bdb, __LINE__, "tcbdbput2"); err = true; } } if(!tcbdbforeach(bdb, iterfunc, NULL)){ eprint(bdb, __LINE__, "tcbdbforeach"); err = true; } tcbdbcurdel(cur); iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb)); iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb)); mprint(bdb); sysprint(); if(!tcbdbclose(bdb)){ eprint(bdb, __LINE__, "tcbdbclose"); err = true; } tcbdbdel(bdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } /* perform wicked command */ static int procwicked(const char *path, int rnum, bool mt, int opts, int omode){ iprintf("\n seed=%u path=%s rnum=%d mt=%d opts=%d omode=%d\n\n", g_randseed, path, rnum, mt, opts, omode); bool err = false; double stime = tctime(); TCBDB *bdb = tcbdbnew(); if(g_dbgfd >= 0) tcbdbsetdbgfd(bdb, g_dbgfd); if(mt && !tcbdbsetmutex(bdb)){ eprint(bdb, __LINE__, "tcbdbsetmutex"); err = true; } if(!tcbdbsetcodecfunc(bdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(bdb, __LINE__, "tcbdbsetcodecfunc"); err = true; } if(!tcbdbtune(bdb, 10, 10, rnum / 50, 100, -1, opts)){ eprint(bdb, __LINE__, "tcbdbtune"); err = true; } if(!tcbdbsetcache(bdb, 128, 256)){ eprint(bdb, __LINE__, "tcbdbsetcache"); err = true; } if(!tcbdbsetxmsiz(bdb, rnum)){ eprint(bdb, __LINE__, "tcbdbsetxmsiz"); err = true; } if(!tcbdbsetdfunit(bdb, 8)){ eprint(bdb, __LINE__, "tcbdbsetdfunit"); err = true; } if(!tcbdbopen(bdb, path, BDBOWRITER | BDBOCREAT | BDBOTRUNC | omode)){ eprint(bdb, __LINE__, "tcbdbopen"); err = true; } BDBCUR *cur = tcbdbcurnew(bdb); if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurfirst"); err = true; } TCMAP *map = tcmapnew2(rnum / 5); for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", myrand(rnum)); char vbuf[RECBUFSIZ]; int vsiz = myrand(RECBUFSIZ); memset(vbuf, '*', vsiz); vbuf[vsiz] = '\0'; char *rbuf; switch(myrand(16)){ case 0: iputchar('0'); if(!tcbdbput(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; } tcmapput(map, kbuf, ksiz, vbuf, vsiz); break; case 1: iputchar('1'); if(!tcbdbput2(bdb, kbuf, vbuf)){ eprint(bdb, __LINE__, "tcbdbput2"); err = true; } tcmapput2(map, kbuf, vbuf); break; case 2: iputchar('2'); if(!tcbdbputkeep(bdb, kbuf, ksiz, vbuf, vsiz) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbputkeep"); err = true; } tcmapputkeep(map, kbuf, ksiz, vbuf, vsiz); break; case 3: iputchar('3'); if(!tcbdbputkeep2(bdb, kbuf, vbuf) && tcbdbecode(bdb) != TCEKEEP){ eprint(bdb, __LINE__, "tcbdbputkeep2"); err = true; } tcmapputkeep2(map, kbuf, vbuf); break; case 4: iputchar('4'); if(!tcbdbputcat(bdb, kbuf, ksiz, vbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbputcat"); err = true; } tcmapputcat(map, kbuf, ksiz, vbuf, vsiz); break; case 5: iputchar('5'); if(!tcbdbputcat2(bdb, kbuf, vbuf)){ eprint(bdb, __LINE__, "tcbdbputcat2"); err = true; } tcmapputcat2(map, kbuf, vbuf); break; case 6: iputchar('6'); if(myrand(10) == 0){ if(!tcbdbout(bdb, kbuf, ksiz) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout"); err = true; } tcmapout(map, kbuf, ksiz); } break; case 7: iputchar('7'); if(myrand(10) == 0){ if(!tcbdbout2(bdb, kbuf) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout2"); err = true; } tcmapout2(map, kbuf); } break; case 8: iputchar('8'); if(!(rbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz))){ if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbget"); err = true; } rbuf = tcsprintf("[%d]", myrand(i + 1)); vsiz = strlen(rbuf); } vsiz += myrand(vsiz); if(myrand(3) == 0) vsiz += PATH_MAX; rbuf = tcrealloc(rbuf, vsiz + 1); for(int j = 0; j < vsiz; j++){ rbuf[j] = myrand(0x100); } if(!tcbdbput(bdb, kbuf, ksiz, rbuf, vsiz)){ eprint(bdb, __LINE__, "tcbdbput"); err = true; } tcmapput(map, kbuf, ksiz, rbuf, vsiz); tcfree(rbuf); break; case 9: iputchar('9'); if(!(rbuf = tcbdbget(bdb, kbuf, ksiz, &vsiz)) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbget"); err = true; } tcfree(rbuf); break; case 10: iputchar('A'); if(!(rbuf = tcbdbget2(bdb, kbuf)) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbget2"); err = true; } tcfree(rbuf); break; case 11: iputchar('B'); if(myrand(1) == 0) vsiz = 1; if(!tcbdbget3(bdb, kbuf, ksiz, &vsiz) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbget3"); err = true; } break; case 12: iputchar('C'); if(myrand(rnum / 50) == 0){ if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurfirst"); err = true; } } TCXSTR *ikey = tcxstrnew(); TCXSTR *ival = tcxstrnew(); for(int j = myrand(rnum) / 1000 + 1; j >= 0; j--){ if(j % 3 == 0){ if(tcbdbcurrec(cur, ikey, ival)){ if(tcbdbvnum(bdb, tcxstrptr(ikey), tcxstrsize(ikey)) != 1){ eprint(bdb, __LINE__, "(validation)"); err = true; } if(tcxstrsize(ival) != tcbdbvsiz(bdb, tcxstrptr(ikey), tcxstrsize(ikey))){ eprint(bdb, __LINE__, "(validation)"); err = true; } } else { int ecode = tcbdbecode(bdb); if(ecode != TCEINVALID && ecode != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurrec"); err = true; } } } else { int iksiz; char *ikbuf = tcbdbcurkey(cur, &iksiz); if(ikbuf){ tcfree(ikbuf); } else { int ecode = tcbdbecode(bdb); if(ecode != TCEINVALID && ecode != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurkey"); err = true; } } } tcbdbcurnext(cur); } tcxstrdel(ival); tcxstrdel(ikey); break; default: iputchar('@'); if(myrand(10000) == 0) srand((unsigned int)(tctime() * 1000) % UINT_MAX); if(myrand(rnum / 32 + 1) == 0){ if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurfirst"); err = true; } int cnt = myrand(30); for(int j = 0; j < rnum && !err; j++){ ksiz = sprintf(kbuf, "%d", i + j); if(myrand(4) == 0){ if(tcbdbout3(bdb, kbuf, ksiz)){ cnt--; } else if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout3"); err = true; } tcmapout(map, kbuf, ksiz); } else if(myrand(30) == 0){ int tksiz; char *tkbuf = tcbdbcurkey(cur, &tksiz); if(tkbuf){ if(tcbdbcurout(cur)){ cnt--; } else { eprint(bdb, __LINE__, "tcbdbcurout"); err = true; } tcmapout(map, tkbuf, tksiz); tcfree(tkbuf); } else if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurkey"); err = true; } } else { if(tcbdbout(bdb, kbuf, ksiz)){ cnt--; } else if(tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbout"); err = true; } tcmapout(map, kbuf, ksiz); } if(cnt < 0) break; } } break; } if(i % 50 == 0) iprintf(" (%08d)\n", i); if(i == rnum / 2){ if(!tcbdbclose(bdb)){ eprint(bdb, __LINE__, "tcbdbclose"); err = true; } if(!tcbdbopen(bdb, path, BDBOWRITER | omode)){ eprint(bdb, __LINE__, "tcbdbopen"); err = true; } } else if(i == rnum / 4){ char *npath = tcsprintf("%s-tmp", path); if(!tcbdbcopy(bdb, npath)){ eprint(bdb, __LINE__, "tcbdbcopy"); err = true; } TCBDB *nbdb = tcbdbnew(); if(!tcbdbsetcodecfunc(nbdb, _tc_recencode, NULL, _tc_recdecode, NULL)){ eprint(nbdb, __LINE__, "tcbdbsetcodecfunc"); err = true; } if(!tcbdbopen(nbdb, npath, BDBOREADER | omode)){ eprint(nbdb, __LINE__, "tcbdbopen"); err = true; } tcbdbdel(nbdb); unlink(npath); tcfree(npath); if(!tcbdboptimize(bdb, -1, -1, -1, -1, -1, -1)){ eprint(bdb, __LINE__, "tcbdboptimize"); err = true; } if(!tcbdbcurfirst(cur) && tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "tcbdbcurfirst"); err = true; } } else if(i == rnum / 8){ if(!tcbdbtranbegin(bdb)){ eprint(bdb, __LINE__, "tcbdbtranbegin"); err = true; } } else if(i == rnum / 8 + rnum / 16){ if(!tcbdbtrancommit(bdb)){ eprint(bdb, __LINE__, "tcbdbtrancommit"); err = true; } } } if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum); if(!tcbdbsync(bdb)){ eprint(bdb, __LINE__, "tcbdbsync"); err = true; } if(tcbdbrnum(bdb) != tcmaprnum(map)){ eprint(bdb, __LINE__, "(validation)"); err = true; } for(int i = 1; i <= rnum && !err; i++){ char kbuf[RECBUFSIZ]; int ksiz = sprintf(kbuf, "%d", i - 1); int vsiz; const char *vbuf = tcmapget(map, kbuf, ksiz, &vsiz); int rsiz; char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz); if(vbuf){ iputchar('.'); if(!rbuf){ eprint(bdb, __LINE__, "tcbdbget"); err = true; } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(bdb, __LINE__, "(validation)"); err = true; } } else { iputchar('*'); if(rbuf || tcbdbecode(bdb) != TCENOREC){ eprint(bdb, __LINE__, "(validation)"); err = true; } } tcfree(rbuf); if(i % 50 == 0) iprintf(" (%08d)\n", i); } if(rnum % 50 > 0) iprintf(" (%08d)\n", rnum); tcmapiterinit(map); int ksiz; const char *kbuf; for(int i = 1; (kbuf = tcmapiternext(map, &ksiz)) != NULL; i++){ iputchar('+'); int vsiz; const char *vbuf = tcmapiterval(kbuf, &vsiz); int rsiz; char *rbuf = tcbdbget(bdb, kbuf, ksiz, &rsiz); if(!rbuf){ eprint(bdb, __LINE__, "tcbdbget"); err = true; } else if(rsiz != vsiz || memcmp(rbuf, vbuf, rsiz)){ eprint(bdb, __LINE__, "(validation)"); err = true; } tcfree(rbuf); if(!tcbdbout(bdb, kbuf, ksiz)){ eprint(bdb, __LINE__, "tcbdbout"); err = true; } if(i % 50 == 0) iprintf(" (%08d)\n", i); } int mrnum = tcmaprnum(map); if(mrnum % 50 > 0) iprintf(" (%08d)\n", mrnum); if(tcbdbrnum(bdb) != 0){ eprint(bdb, __LINE__, "(validation)"); err = true; } tcbdbcurdel(cur); iprintf("record number: %llu\n", (unsigned long long)tcbdbrnum(bdb)); iprintf("size: %llu\n", (unsigned long long)tcbdbfsiz(bdb)); mprint(bdb); sysprint(); tcmapdel(map); if(!tcbdbclose(bdb)){ eprint(bdb, __LINE__, "tcbdbclose"); err = true; } tcbdbdel(bdb); iprintf("time: %.3f\n", tctime() - stime); iprintf("%s\n\n", err ? "error" : "ok"); return err ? 1 : 0; } // END OF FILE