1 /*************************************************************************************************
2 * The fixed-length database API of Tokyo Cabinet
3 * Copyright (C) 2006-2012 FAL Labs
4 * This file is part of Tokyo Cabinet.
5 * Tokyo Cabinet is free software; you can redistribute it and/or modify it under the terms of
6 * the GNU Lesser General Public License as published by the Free Software Foundation; either
7 * version 2.1 of the License or any later version. Tokyo Cabinet is distributed in the hope
8 * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10 * License for more details.
11 * You should have received a copy of the GNU Lesser General Public License along with Tokyo
12 * Cabinet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
13 * Boston, MA 02111-1307 USA.
14 *************************************************************************************************/
15
16
17 #include "tcutil.h"
18 #include "tcfdb.h"
19 #include "myconf.h"
20
21 #define FDBFILEMODE 00644 // permission of created files
22 #define FDBIOBUFSIZ 8192 // size of an I/O buffer
23
24 #define FDBMAGICDATA "ToKyO CaBiNeT" // magic data for identification
25 #define FDBHEADSIZ 256 // size of the reagion of the header
26 #define FDBTYPEOFF 32 // offset of the region for the database type
27 #define FDBFLAGSOFF 33 // offset of the region for the additional flags
28 #define FDBRNUMOFF 48 // offset of the region for the record number
29 #define FDBFSIZOFF 56 // offset of the region for the file size
30 #define FDBWIDTHOFF 64 // offset of the region for the record width
31 #define FDBLIMSIZOFF 72 // offset of the region for the limit size
32 #define FDBMINOFF 80 // offset of the region for the minimum ID offset
33 #define FDBMAXOFF 88 // offset of the region for the maximum ID offset
34 #define FDBOPAQUEOFF 128 // offset of the region for the opaque field
35
36 #define FDBDEFWIDTH 255 // default value width
37 #define FDBDEFLIMSIZ (256LL<<20) // default limit size
38 #define FDBRMTXNUM 127 // number of record mutexes
39 #define FDBTRUNCALW 256 // number of record for truncate allowance
40 #define FDBIDARYUNIT 2048 // size of ID array allocation unit
41 #define FDBWALSUFFIX "wal" // suffix of write ahead logging file
42
43 enum { // enumeration for duplication behavior
44 FDBPDOVER, // overwrite an existing value
45 FDBPDKEEP, // keep the existing value
46 FDBPDCAT, // concatenate values
47 FDBPDADDINT, // add an integer
48 FDBPDADDDBL, // add a real number
49 FDBPDPROC // process by a callback function
50 };
51
52 typedef struct { // type of structure for a duplication callback
53 TCPDPROC proc; // function pointer
54 void *op; // opaque pointer
55 } FDBPDPROCOP;
56
57
58 /* private macros */
59 #define FDBLOCKMETHOD(TC_fdb, TC_wr) \
60 ((TC_fdb)->mmtx ? tcfdblockmethod((TC_fdb), (TC_wr)) : true)
61 #define FDBUNLOCKMETHOD(TC_fdb) \
62 ((TC_fdb)->mmtx ? tcfdbunlockmethod(TC_fdb) : true)
63 #define FDBLOCKATTR(TC_fdb) \
64 ((TC_fdb)->mmtx ? tcfdblockattr(TC_fdb) : true)
65 #define FDBUNLOCKATTR(TC_fdb) \
66 ((TC_fdb)->mmtx ? tcfdbunlockattr(TC_fdb) : true)
67 #define FDBLOCKRECORD(TC_fdb, TC_wr, TC_id) \
68 ((TC_fdb)->mmtx ? tcfdblockrecord((TC_fdb), (TC_wr), (TC_id)) : true)
69 #define FDBUNLOCKRECORD(TC_fdb, TC_id) \
70 ((TC_fdb)->mmtx ? tcfdbunlockrecord((TC_fdb), (TC_id)) : true)
71 #define FDBLOCKALLRECORDS(TC_fdb, TC_wr) \
72 ((TC_fdb)->mmtx ? tcfdblockallrecords((TC_fdb), (TC_wr)) : true)
73 #define FDBUNLOCKALLRECORDS(TC_fdb) \
74 ((TC_fdb)->mmtx ? tcfdbunlockallrecords(TC_fdb) : true)
75 #define FDBLOCKWAL(TC_fdb) \
76 ((TC_fdb)->mmtx ? tcfdblockwal(TC_fdb) : true)
77 #define FDBUNLOCKWAL(TC_fdb) \
78 ((TC_fdb)->mmtx ? tcfdbunlockwal(TC_fdb) : true)
79 #define FDBTHREADYIELD(TC_fdb) \
80 do { if((TC_fdb)->mmtx) sched_yield(); } while(false)
81
82
83 /* private function prototypes */
84 static void tcfdbdumpmeta(TCFDB *fdb, char *hbuf);
85 static void tcfdbloadmeta(TCFDB *fdb, const char *hbuf);
86 static void tcfdbclear(TCFDB *fdb);
87 static void tcfdbsetflag(TCFDB *fdb, int flag, bool sign);
88 static bool tcfdbwalinit(TCFDB *fdb);
89 static bool tcfdbwalwrite(TCFDB *fdb, uint64_t off, int64_t size);
90 static int tcfdbwalrestore(TCFDB *fdb, const char *path);
91 static bool tcfdbwalremove(TCFDB *fdb, const char *path);
92 static bool tcfdbopenimpl(TCFDB *fdb, const char *path, int omode);
93 static bool tcfdbcloseimpl(TCFDB *fdb);
94 static int64_t tcfdbprevid(TCFDB *fdb, int64_t id);
95 static int64_t tcfdbnextid(TCFDB *fdb, int64_t id);
96 static bool tcfdbputimpl(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz, int dmode);
97 static bool tcfdboutimpl(TCFDB *fdb, int64_t id);
98 static const void *tcfdbgetimpl(TCFDB *fdb, int64_t id, int *sp);
99 static bool tcfdbiterinitimpl(TCFDB *fdb);
100 static uint64_t tcfdbiternextimpl(TCFDB *fdb);
101 static uint64_t *tcfdbrangeimpl(TCFDB *fdb, int64_t lower, int64_t upper, int max, int *np);
102 static bool tcfdboptimizeimpl(TCFDB *fdb, int32_t width, int64_t limsiz);
103 static bool tcfdbvanishimpl(TCFDB *fdb);
104 static bool tcfdbcopyimpl(TCFDB *fdb, const char *path);
105 static bool tcfdbiterjumpimpl(TCFDB *fdb, int64_t id);
106 static bool tcfdbforeachimpl(TCFDB *fdb, TCITER iter, void *op);
107 static bool tcfdblockmethod(TCFDB *fdb, bool wr);
108 static bool tcfdbunlockmethod(TCFDB *fdb);
109 static bool tcfdblockattr(TCFDB *fdb);
110 static bool tcfdbunlockattr(TCFDB *fdb);
111 static bool tcfdblockrecord(TCFDB *fdb, bool wr, uint64_t id);
112 static bool tcfdbunlockrecord(TCFDB *fdb, uint64_t id);
113 static bool tcfdblockallrecords(TCFDB *fdb, bool wr);
114 static bool tcfdbunlockallrecords(TCFDB *fdb);
115 static bool tcfdblockwal(TCFDB *fdb);
116 static bool tcfdbunlockwal(TCFDB *fdb);
117
118
119 /* debugging function prototypes */
120 void tcfdbprintmeta(TCFDB *fdb);
121
122
123
124 /*************************************************************************************************
125 * API
126 *************************************************************************************************/
127
128
129 /* Get the message string corresponding to an error code. */
tcfdberrmsg(int ecode)130 const char *tcfdberrmsg(int ecode){
131 return tcerrmsg(ecode);
132 }
133
134
135 /* Create a fixed-length database object. */
tcfdbnew(void)136 TCFDB *tcfdbnew(void){
137 TCFDB *fdb;
138 TCMALLOC(fdb, sizeof(*fdb));
139 tcfdbclear(fdb);
140 return fdb;
141 }
142
143
144 /* Delete a fixed-length database object. */
tcfdbdel(TCFDB * fdb)145 void tcfdbdel(TCFDB *fdb){
146 assert(fdb);
147 if(fdb->fd >= 0) tcfdbclose(fdb);
148 if(fdb->mmtx){
149 pthread_key_delete(*(pthread_key_t *)fdb->eckey);
150 pthread_mutex_destroy(fdb->wmtx);
151 pthread_mutex_destroy(fdb->tmtx);
152 for(int i = FDBRMTXNUM - 1; i >= 0; i--){
153 pthread_rwlock_destroy((pthread_rwlock_t *)fdb->rmtxs + i);
154 }
155 pthread_mutex_destroy(fdb->amtx);
156 pthread_rwlock_destroy(fdb->mmtx);
157 TCFREE(fdb->eckey);
158 TCFREE(fdb->wmtx);
159 TCFREE(fdb->tmtx);
160 TCFREE(fdb->rmtxs);
161 TCFREE(fdb->amtx);
162 TCFREE(fdb->mmtx);
163 }
164 TCFREE(fdb);
165 }
166
167
168 /* Get the last happened error code of a fixed-length database object. */
tcfdbecode(TCFDB * fdb)169 int tcfdbecode(TCFDB *fdb){
170 assert(fdb);
171 return fdb->mmtx ?
172 (int)(intptr_t)pthread_getspecific(*(pthread_key_t *)fdb->eckey) : fdb->ecode;
173 }
174
175
176 /* Set mutual exclusion control of a fixed-length database object for threading. */
tcfdbsetmutex(TCFDB * fdb)177 bool tcfdbsetmutex(TCFDB *fdb){
178 assert(fdb);
179 if(!TCUSEPTHREAD) return true;
180 if(fdb->mmtx || fdb->fd >= 0){
181 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
182 return false;
183 }
184 TCMALLOC(fdb->mmtx, sizeof(pthread_rwlock_t));
185 TCMALLOC(fdb->amtx, sizeof(pthread_mutex_t));
186 TCMALLOC(fdb->rmtxs, sizeof(pthread_rwlock_t) * FDBRMTXNUM);
187 TCMALLOC(fdb->tmtx, sizeof(pthread_mutex_t));
188 TCMALLOC(fdb->wmtx, sizeof(pthread_mutex_t));
189 TCMALLOC(fdb->eckey, sizeof(pthread_key_t));
190 bool err = false;
191 if(pthread_rwlock_init(fdb->mmtx, NULL) != 0) err = true;
192 if(pthread_mutex_init(fdb->amtx, NULL) != 0) err = true;
193 for(int i = 0; i < FDBRMTXNUM; i++){
194 if(pthread_rwlock_init((pthread_rwlock_t *)fdb->rmtxs + i, NULL) != 0) err = true;
195 }
196 if(pthread_mutex_init(fdb->tmtx, NULL) != 0) err = true;
197 if(pthread_mutex_init(fdb->wmtx, NULL) != 0) err = true;
198 if(pthread_key_create(fdb->eckey, NULL) != 0) err = true;
199 if(err){
200 TCFREE(fdb->eckey);
201 TCFREE(fdb->wmtx);
202 TCFREE(fdb->tmtx);
203 TCFREE(fdb->rmtxs);
204 TCFREE(fdb->amtx);
205 TCFREE(fdb->mmtx);
206 fdb->eckey = NULL;
207 fdb->wmtx = NULL;
208 fdb->tmtx = NULL;
209 fdb->rmtxs = NULL;
210 fdb->amtx = NULL;
211 fdb->mmtx = NULL;
212 return false;
213 }
214 return true;
215 }
216
217
218 /* Set the tuning parameters of a fixed-length database object. */
tcfdbtune(TCFDB * fdb,int32_t width,int64_t limsiz)219 bool tcfdbtune(TCFDB *fdb, int32_t width, int64_t limsiz){
220 assert(fdb);
221 if(fdb->fd >= 0){
222 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
223 return false;
224 }
225 fdb->width = (width > 0) ? width : FDBDEFWIDTH;
226 fdb->limsiz = (limsiz > 0) ? limsiz : FDBDEFLIMSIZ;
227 if(fdb->limsiz < FDBHEADSIZ + fdb->width + sizeof(uint32_t))
228 fdb->limsiz = FDBHEADSIZ + fdb->width + sizeof(uint32_t);
229 fdb->limsiz = tcpagealign(fdb->limsiz);
230 return true;
231 }
232
233
234 /* Open a database file and connect a fixed-length database object. */
tcfdbopen(TCFDB * fdb,const char * path,int omode)235 bool tcfdbopen(TCFDB *fdb, const char *path, int omode){
236 assert(fdb && path);
237 if(!FDBLOCKMETHOD(fdb, true)) return false;
238 if(fdb->fd >= 0){
239 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
240 FDBUNLOCKMETHOD(fdb);
241 return false;
242 }
243 char *rpath = tcrealpath(path);
244 if(!rpath){
245 int ecode = TCEOPEN;
246 switch(errno){
247 case EACCES: ecode = TCENOPERM; break;
248 case ENOENT: ecode = TCENOFILE; break;
249 case ENOTDIR: ecode = TCENOFILE; break;
250 }
251 tcfdbsetecode(fdb, ecode, __FILE__, __LINE__, __func__);
252 FDBUNLOCKMETHOD(fdb);
253 return false;
254 }
255 if(!tcpathlock(rpath)){
256 tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__);
257 TCFREE(rpath);
258 FDBUNLOCKMETHOD(fdb);
259 return false;
260 }
261 bool rv = tcfdbopenimpl(fdb, path, omode);
262 if(rv){
263 fdb->rpath = rpath;
264 } else {
265 tcpathunlock(rpath);
266 TCFREE(rpath);
267 }
268 FDBUNLOCKMETHOD(fdb);
269 return rv;
270 }
271
272
273 /* Close a fixed-length database object. */
tcfdbclose(TCFDB * fdb)274 bool tcfdbclose(TCFDB *fdb){
275 assert(fdb);
276 if(!FDBLOCKMETHOD(fdb, true)) return false;
277 if(fdb->fd < 0){
278 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
279 FDBUNLOCKMETHOD(fdb);
280 return false;
281 }
282 bool rv = tcfdbcloseimpl(fdb);
283 tcpathunlock(fdb->rpath);
284 TCFREE(fdb->rpath);
285 fdb->rpath = NULL;
286 FDBUNLOCKMETHOD(fdb);
287 return rv;
288 }
289
290
291 /* Store a record into a fixed-length database object. */
tcfdbput(TCFDB * fdb,int64_t id,const void * vbuf,int vsiz)292 bool tcfdbput(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz){
293 assert(fdb && vbuf && vsiz >= 0);
294 if(!FDBLOCKMETHOD(fdb, id < 1)) return false;
295 if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){
296 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
297 FDBUNLOCKMETHOD(fdb);
298 return false;
299 }
300 if(id == FDBIDMIN){
301 id = fdb->min;
302 } else if(id == FDBIDPREV){
303 id = fdb->min - 1;
304 } else if(id == FDBIDMAX){
305 id = fdb->max;
306 } else if(id == FDBIDNEXT){
307 id = fdb->max + 1;
308 }
309 if(id < 1 || id > fdb->limid){
310 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
311 FDBUNLOCKMETHOD(fdb);
312 return false;
313 }
314 if(!FDBLOCKRECORD(fdb, true, id)){
315 FDBUNLOCKMETHOD(fdb);
316 return false;
317 }
318 bool rv = tcfdbputimpl(fdb, id, vbuf, vsiz, FDBPDOVER);
319 FDBUNLOCKRECORD(fdb, id);
320 FDBUNLOCKMETHOD(fdb);
321 return rv;
322 }
323
324
325 /* Store a record with a decimal key into a fixed-length database object. */
tcfdbput2(TCFDB * fdb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)326 bool tcfdbput2(TCFDB *fdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
327 assert(fdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
328 return tcfdbput(fdb, tcfdbkeytoid(kbuf, ksiz), vbuf, vsiz);
329 }
330
331
332 /* Store a string record with a decimal key into a fixed-length database object. */
tcfdbput3(TCFDB * fdb,const char * kstr,const void * vstr)333 bool tcfdbput3(TCFDB *fdb, const char *kstr, const void *vstr){
334 assert(fdb && kstr && vstr);
335 return tcfdbput(fdb, tcfdbkeytoid(kstr, strlen(kstr)), vstr, strlen(vstr));
336 }
337
338
339 /* Store a new record into a fixed-length database object. */
tcfdbputkeep(TCFDB * fdb,int64_t id,const void * vbuf,int vsiz)340 bool tcfdbputkeep(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz){
341 assert(fdb && vbuf && vsiz >= 0);
342 if(!FDBLOCKMETHOD(fdb, id < 1)) return false;
343 if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){
344 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
345 FDBUNLOCKMETHOD(fdb);
346 return false;
347 }
348 if(id == FDBIDMIN){
349 id = fdb->min;
350 } else if(id == FDBIDPREV){
351 id = fdb->min - 1;
352 } else if(id == FDBIDMAX){
353 id = fdb->max;
354 } else if(id == FDBIDNEXT){
355 id = fdb->max + 1;
356 }
357 if(id < 1 || id > fdb->limid){
358 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
359 FDBUNLOCKMETHOD(fdb);
360 return false;
361 }
362 if(!FDBLOCKRECORD(fdb, true, id)){
363 FDBUNLOCKMETHOD(fdb);
364 return false;
365 }
366 bool rv = tcfdbputimpl(fdb, id, vbuf, vsiz, FDBPDKEEP);
367 FDBUNLOCKRECORD(fdb, id);
368 FDBUNLOCKMETHOD(fdb);
369 return rv;
370 }
371
372
373 /* Store a new record with a decimal key into a fixed-length database object. */
tcfdbputkeep2(TCFDB * fdb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)374 bool tcfdbputkeep2(TCFDB *fdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
375 assert(fdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
376 return tcfdbputkeep(fdb, tcfdbkeytoid(kbuf, ksiz), vbuf, vsiz);
377 }
378
379
380 /* Store a new string record with a decimal key into a fixed-length database object. */
tcfdbputkeep3(TCFDB * fdb,const char * kstr,const void * vstr)381 bool tcfdbputkeep3(TCFDB *fdb, const char *kstr, const void *vstr){
382 assert(fdb && kstr && vstr);
383 return tcfdbputkeep(fdb, tcfdbkeytoid(kstr, strlen(kstr)), vstr, strlen(vstr));
384 }
385
386
387 /* Concatenate a value at the end of the existing record in a fixed-length database object. */
tcfdbputcat(TCFDB * fdb,int64_t id,const void * vbuf,int vsiz)388 bool tcfdbputcat(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz){
389 assert(fdb && vbuf && vsiz >= 0);
390 if(!FDBLOCKMETHOD(fdb, id < 1)) return false;
391 if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){
392 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
393 FDBUNLOCKMETHOD(fdb);
394 return false;
395 }
396 if(id == FDBIDMIN){
397 id = fdb->min;
398 } else if(id == FDBIDPREV){
399 id = fdb->min - 1;
400 } else if(id == FDBIDMAX){
401 id = fdb->max;
402 } else if(id == FDBIDNEXT){
403 id = fdb->max + 1;
404 }
405 if(id < 1 || id > fdb->limid){
406 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
407 FDBUNLOCKMETHOD(fdb);
408 return false;
409 }
410 if(!FDBLOCKRECORD(fdb, true, id)){
411 FDBUNLOCKMETHOD(fdb);
412 return false;
413 }
414 bool rv = tcfdbputimpl(fdb, id, vbuf, vsiz, FDBPDCAT);
415 FDBUNLOCKRECORD(fdb, id);
416 FDBUNLOCKMETHOD(fdb);
417 return rv;
418 }
419
420
421 /* Concatenate a value with a decimal key in a fixed-length database object. */
tcfdbputcat2(TCFDB * fdb,const void * kbuf,int ksiz,const void * vbuf,int vsiz)422 bool tcfdbputcat2(TCFDB *fdb, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
423 assert(fdb && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
424 return tcfdbputcat(fdb, tcfdbkeytoid(kbuf, ksiz), vbuf, vsiz);
425 }
426
427
428 /* Concatenate a string value with a decimal key in a fixed-length database object. */
tcfdbputcat3(TCFDB * fdb,const char * kstr,const void * vstr)429 bool tcfdbputcat3(TCFDB *fdb, const char *kstr, const void *vstr){
430 assert(fdb && kstr && vstr);
431 return tcfdbputcat(fdb, tcfdbkeytoid(kstr, strlen(kstr)), vstr, strlen(vstr));
432 }
433
434
435 /* Remove a record of a fixed-length database object. */
tcfdbout(TCFDB * fdb,int64_t id)436 bool tcfdbout(TCFDB *fdb, int64_t id){
437 assert(fdb);
438 if(!FDBLOCKMETHOD(fdb, true)) return false;
439 if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){
440 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
441 FDBUNLOCKMETHOD(fdb);
442 return false;
443 }
444 if(id == FDBIDMIN){
445 id = fdb->min;
446 } else if(id == FDBIDMAX){
447 id = fdb->max;
448 }
449 if(id < 1 || id > fdb->limid){
450 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
451 FDBUNLOCKMETHOD(fdb);
452 return false;
453 }
454 if(!FDBLOCKRECORD(fdb, true, id)){
455 FDBUNLOCKMETHOD(fdb);
456 return false;
457 }
458 bool rv = tcfdboutimpl(fdb, id);
459 FDBUNLOCKRECORD(fdb, id);
460 FDBUNLOCKMETHOD(fdb);
461 return rv;
462 }
463
464
465 /* Remove a record with a decimal key of a fixed-length database object. */
tcfdbout2(TCFDB * fdb,const void * kbuf,int ksiz)466 bool tcfdbout2(TCFDB *fdb, const void *kbuf, int ksiz){
467 assert(fdb && kbuf && ksiz >= 0);
468 return tcfdbout(fdb, tcfdbkeytoid(kbuf, ksiz));
469 }
470
471
472 /* Remove a string record with a decimal key of a fixed-length database object. */
tcfdbout3(TCFDB * fdb,const char * kstr)473 bool tcfdbout3(TCFDB *fdb, const char *kstr){
474 assert(fdb && kstr);
475 return tcfdbout(fdb, tcfdbkeytoid(kstr, strlen(kstr)));
476 }
477
478
479 /* Retrieve a record in a fixed-length database object. */
tcfdbget(TCFDB * fdb,int64_t id,int * sp)480 void *tcfdbget(TCFDB *fdb, int64_t id, int *sp){
481 assert(fdb && sp);
482 if(!FDBLOCKMETHOD(fdb, false)) return false;
483 if(fdb->fd < 0){
484 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
485 FDBUNLOCKMETHOD(fdb);
486 return false;
487 }
488 if(id == FDBIDMIN){
489 id = fdb->min;
490 } else if(id == FDBIDMAX){
491 id = fdb->max;
492 }
493 if(id < 1 || id > fdb->limid){
494 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
495 FDBUNLOCKMETHOD(fdb);
496 return false;
497 }
498 if(!FDBLOCKRECORD(fdb, false, id)){
499 FDBUNLOCKMETHOD(fdb);
500 return false;
501 }
502 const void *vbuf = tcfdbgetimpl(fdb, id, sp);
503 char *rv = vbuf ? tcmemdup(vbuf, *sp) : NULL;
504 FDBUNLOCKRECORD(fdb, id);
505 FDBUNLOCKMETHOD(fdb);
506 return rv;
507 }
508
509
510 /* Retrieve a record with a decimal key in a fixed-length database object. */
tcfdbget2(TCFDB * fdb,const void * kbuf,int ksiz,int * sp)511 void *tcfdbget2(TCFDB *fdb, const void *kbuf, int ksiz, int *sp){
512 assert(fdb && kbuf && ksiz >= 0 && sp);
513 return tcfdbget(fdb, tcfdbkeytoid(kbuf, ksiz), sp);
514 }
515
516
517 /* Retrieve a string record with a decimal key in a fixed-length database object. */
tcfdbget3(TCFDB * fdb,const char * kstr)518 char *tcfdbget3(TCFDB *fdb, const char *kstr){
519 assert(fdb && kstr);
520 int vsiz;
521 return tcfdbget(fdb, tcfdbkeytoid(kstr, strlen(kstr)), &vsiz);
522 }
523
524
525 /* Retrieve a record in a fixed-length database object and write the value into a buffer. */
tcfdbget4(TCFDB * fdb,int64_t id,void * vbuf,int max)526 int tcfdbget4(TCFDB *fdb, int64_t id, void *vbuf, int max){
527 assert(fdb && vbuf && max >= 0);
528 if(!FDBLOCKMETHOD(fdb, false)) return false;
529 if(fdb->fd < 0){
530 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
531 FDBUNLOCKMETHOD(fdb);
532 return false;
533 }
534 if(id == FDBIDMIN){
535 id = fdb->min;
536 } else if(id == FDBIDMAX){
537 id = fdb->max;
538 }
539 if(id < 1 || id > fdb->limid){
540 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
541 FDBUNLOCKMETHOD(fdb);
542 return false;
543 }
544 if(!FDBLOCKRECORD(fdb, false, id)){
545 FDBUNLOCKMETHOD(fdb);
546 return false;
547 }
548 int vsiz;
549 const void *rbuf = tcfdbgetimpl(fdb, id, &vsiz);
550 if(rbuf){
551 if(vsiz > max) vsiz = max;
552 memcpy(vbuf, rbuf, vsiz);
553 } else {
554 vsiz = -1;
555 }
556 FDBUNLOCKRECORD(fdb, id);
557 FDBUNLOCKMETHOD(fdb);
558 return vsiz;
559 }
560
561
562 /* Get the size of the value of a record in a fixed-length database object. */
tcfdbvsiz(TCFDB * fdb,int64_t id)563 int tcfdbvsiz(TCFDB *fdb, int64_t id){
564 assert(fdb);
565 if(!FDBLOCKMETHOD(fdb, false)) return false;
566 if(fdb->fd < 0){
567 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
568 FDBUNLOCKMETHOD(fdb);
569 return false;
570 }
571 if(id == FDBIDMIN){
572 id = fdb->min;
573 } else if(id == FDBIDMAX){
574 id = fdb->max;
575 }
576 if(id < 1 || id > fdb->limid){
577 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
578 FDBUNLOCKMETHOD(fdb);
579 return false;
580 }
581 if(!FDBLOCKRECORD(fdb, false, id)){
582 FDBUNLOCKMETHOD(fdb);
583 return false;
584 }
585 int vsiz;
586 const void *vbuf = tcfdbgetimpl(fdb, id, &vsiz);
587 if(!vbuf) vsiz = -1;
588 FDBUNLOCKRECORD(fdb, id);
589 FDBUNLOCKMETHOD(fdb);
590 return vsiz;
591 }
592
593
594 /* Get the size of the value with a decimal key in a fixed-length database object. */
tcfdbvsiz2(TCFDB * fdb,const void * kbuf,int ksiz)595 int tcfdbvsiz2(TCFDB *fdb, const void *kbuf, int ksiz){
596 assert(fdb && kbuf && ksiz >= 0);
597 return tcfdbvsiz(fdb, tcfdbkeytoid(kbuf, ksiz));
598 }
599
600
601 /* Get the size of the string value with a decimal key in a fixed-length database object. */
tcfdbvsiz3(TCFDB * fdb,const char * kstr)602 int tcfdbvsiz3(TCFDB *fdb, const char *kstr){
603 assert(fdb && kstr);
604 return tcfdbvsiz(fdb, tcfdbkeytoid(kstr, strlen(kstr)));
605 }
606
607
608 /* Initialize the iterator of a fixed-length database object. */
tcfdbiterinit(TCFDB * fdb)609 bool tcfdbiterinit(TCFDB *fdb){
610 assert(fdb);
611 if(!FDBLOCKMETHOD(fdb, true)) return false;
612 if(fdb->fd < 0){
613 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
614 FDBUNLOCKMETHOD(fdb);
615 return false;
616 }
617 bool rv = tcfdbiterinitimpl(fdb);
618 FDBUNLOCKMETHOD(fdb);
619 return rv;
620 }
621
622
623 /* Get the next ID number of the iterator of a fixed-length database object. */
tcfdbiternext(TCFDB * fdb)624 uint64_t tcfdbiternext(TCFDB *fdb){
625 assert(fdb);
626 if(!FDBLOCKMETHOD(fdb, true)) return false;
627 if(fdb->fd < 0){
628 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
629 FDBUNLOCKMETHOD(fdb);
630 return false;
631 }
632 uint64_t rv = tcfdbiternextimpl(fdb);
633 FDBUNLOCKMETHOD(fdb);
634 return rv;
635 }
636
637
638 /* Get the next decimay key of the iterator of a fixed-length database object. */
tcfdbiternext2(TCFDB * fdb,int * sp)639 void *tcfdbiternext2(TCFDB *fdb, int *sp){
640 assert(fdb && sp);
641 uint64_t id = tcfdbiternextimpl(fdb);
642 if(id < 1) return NULL;
643 char kbuf[TCNUMBUFSIZ];
644 int ksiz = sprintf(kbuf, "%llu", (unsigned long long)id);
645 *sp = ksiz;
646 return tcmemdup(kbuf, ksiz);
647 }
648
649
650 /* Get the next decimay key string of the iterator of a fixed-length database object. */
tcfdbiternext3(TCFDB * fdb)651 char *tcfdbiternext3(TCFDB *fdb){
652 assert(fdb);
653 int ksiz;
654 return tcfdbiternext2(fdb, &ksiz);
655 }
656
657
658 /* Get range matching decimal keys in a fixed-length database object. */
tcfdbrange(TCFDB * fdb,int64_t lower,int64_t upper,int max,int * np)659 uint64_t *tcfdbrange(TCFDB *fdb, int64_t lower, int64_t upper, int max, int *np){
660 assert(fdb && np);
661 if(!FDBLOCKMETHOD(fdb, true)) return false;
662 if(fdb->fd < 0){
663 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
664 FDBUNLOCKMETHOD(fdb);
665 *np = 0;
666 return tcmalloc(1);
667 }
668 if(lower == FDBIDMIN) lower = fdb->min;
669 if(upper == FDBIDMAX) upper = fdb->max;
670 if(lower < 1 || lower > fdb->limid || upper < 1 || upper > fdb->limid){
671 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
672 FDBUNLOCKMETHOD(fdb);
673 *np = 0;
674 return tcmalloc(1);
675 }
676 uint64_t *rv = tcfdbrangeimpl(fdb, lower, upper, max, np);
677 FDBUNLOCKMETHOD(fdb);
678 return rv;
679 }
680
681
682 /* Get range matching decimal keys in a fixed-length database object. */
tcfdbrange2(TCFDB * fdb,const void * lbuf,int lsiz,const void * ubuf,int usiz,int max)683 TCLIST *tcfdbrange2(TCFDB *fdb, const void *lbuf, int lsiz, const void *ubuf, int usiz, int max){
684 assert(fdb && lbuf && lsiz >= 0 && ubuf && usiz >= 0);
685 int num;
686 uint64_t *ids = tcfdbrange(fdb, tcfdbkeytoid(lbuf, lsiz), tcfdbkeytoid(ubuf, usiz), max, &num);
687 TCLIST *keys = tclistnew2(num);
688 for(int i = 0; i < num; i++){
689 char kbuf[TCNUMBUFSIZ];
690 int ksiz = sprintf(kbuf, "%llu", (unsigned long long)ids[i]);
691 TCLISTPUSH(keys, kbuf, ksiz);
692 }
693 TCFREE(ids);
694 return keys;
695 }
696
697
698 /* Get range matching decimal keys with strings in a fixed-length database object. */
tcfdbrange3(TCFDB * fdb,const char * lstr,const char * ustr,int max)699 TCLIST *tcfdbrange3(TCFDB *fdb, const char *lstr, const char *ustr, int max){
700 assert(fdb && lstr && ustr);
701 return tcfdbrange2(fdb, lstr, strlen(lstr), ustr, strlen(ustr), max);
702 }
703
704
705 /* Get keys with an interval notation in a fixed-length database object. */
tcfdbrange4(TCFDB * fdb,const void * ibuf,int isiz,int max)706 TCLIST *tcfdbrange4(TCFDB *fdb, const void *ibuf, int isiz, int max){
707 assert(fdb && ibuf && isiz >= 0);
708 char *expr;
709 TCMEMDUP(expr, ibuf, isiz);
710 char *pv = expr;
711 while(*pv > '\0' && *pv <= ' '){
712 pv++;
713 }
714 bool linc = false;
715 if(*pv == '['){
716 linc = true;
717 } else if(*pv != '('){
718 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
719 TCFREE(expr);
720 return tclistnew();
721 }
722 pv++;
723 char *sep = strchr(pv, ',');
724 if(!sep){
725 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
726 TCFREE(expr);
727 return tclistnew();
728 }
729 *sep = '\0';
730 tcstrtrim(pv);
731 int64_t lower = tcfdbkeytoid(pv, strlen(pv));
732 pv = sep + 1;
733 bool uinc = false;
734 if((sep = strchr(pv, ']')) != NULL){
735 uinc = true;
736 *sep = '\0';
737 } else if((sep = strchr(pv, ')')) != NULL){
738 *sep = '\0';
739 } else {
740 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
741 TCFREE(expr);
742 return tclistnew();
743 }
744 tcstrtrim(pv);
745 int64_t upper = tcfdbkeytoid(pv, strlen(pv));
746 if(lower == FDBIDMIN){
747 lower = fdb->min;
748 } else if(lower == FDBIDPREV){
749 lower = fdb->min - 1;
750 } else if(lower == FDBIDMAX){
751 lower = fdb->max;
752 } else if(lower == FDBIDNEXT){
753 lower = fdb->max + 1;
754 }
755 if(!linc) lower++;
756 if(upper == FDBIDMIN){
757 upper = fdb->min;
758 } else if(upper == FDBIDPREV){
759 upper = fdb->min - 1;
760 } else if(upper == FDBIDMAX){
761 upper = fdb->max;
762 } else if(upper == FDBIDNEXT){
763 upper = fdb->max + 1;
764 }
765 if(!uinc) upper--;
766 TCFREE(expr);
767 int num;
768 uint64_t *ids = tcfdbrange(fdb, lower, upper, max, &num);
769 TCLIST *keys = tclistnew2(num);
770 for(int i = 0; i < num; i++){
771 char kbuf[TCNUMBUFSIZ];
772 int ksiz = sprintf(kbuf, "%llu", (unsigned long long)ids[i]);
773 TCLISTPUSH(keys, kbuf, ksiz);
774 }
775 TCFREE(ids);
776 return keys;
777 }
778
779
780 /* Get keys with an interval notation string in a fixed-length database object. */
tcfdbrange5(TCFDB * fdb,const void * istr,int max)781 TCLIST *tcfdbrange5(TCFDB *fdb, const void *istr, int max){
782 assert(fdb && istr);
783 return tcfdbrange4(fdb, istr, strlen(istr), max);
784 }
785
786
787 /* Add an integer to a record in a fixed-length database object. */
tcfdbaddint(TCFDB * fdb,int64_t id,int num)788 int tcfdbaddint(TCFDB *fdb, int64_t id, int num){
789 assert(fdb);
790 if(!FDBLOCKMETHOD(fdb, id < 1)) return INT_MIN;
791 if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){
792 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
793 FDBUNLOCKMETHOD(fdb);
794 return INT_MIN;
795 }
796 if(id == FDBIDMIN){
797 id = fdb->min;
798 } else if(id == FDBIDPREV){
799 id = fdb->min - 1;
800 } else if(id == FDBIDMAX){
801 id = fdb->max;
802 } else if(id == FDBIDNEXT){
803 id = fdb->max + 1;
804 }
805 if(id < 1 || id > fdb->limid){
806 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
807 FDBUNLOCKMETHOD(fdb);
808 return INT_MIN;
809 }
810 if(!FDBLOCKRECORD(fdb, true, id)){
811 FDBUNLOCKMETHOD(fdb);
812 return INT_MIN;
813 }
814 bool rv = tcfdbputimpl(fdb, id, (char *)&num, sizeof(num), FDBPDADDINT);
815 FDBUNLOCKRECORD(fdb, id);
816 FDBUNLOCKMETHOD(fdb);
817 return rv ? num : INT_MIN;
818 }
819
820
821 /* Add a real number to a record in a fixed-length database object. */
tcfdbadddouble(TCFDB * fdb,int64_t id,double num)822 double tcfdbadddouble(TCFDB *fdb, int64_t id, double num){
823 assert(fdb);
824 if(!FDBLOCKMETHOD(fdb, id < 1)) return nan("");
825 if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){
826 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
827 FDBUNLOCKMETHOD(fdb);
828 return nan("");
829 }
830 if(id == FDBIDMIN){
831 id = fdb->min;
832 } else if(id == FDBIDPREV){
833 id = fdb->min - 1;
834 } else if(id == FDBIDMAX){
835 id = fdb->max;
836 } else if(id == FDBIDNEXT){
837 id = fdb->max + 1;
838 }
839 if(id < 1 || id > fdb->limid){
840 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
841 FDBUNLOCKMETHOD(fdb);
842 return nan("");
843 }
844 if(!FDBLOCKRECORD(fdb, true, id)){
845 FDBUNLOCKMETHOD(fdb);
846 return nan("");
847 }
848 bool rv = tcfdbputimpl(fdb, id, (char *)&num, sizeof(num), FDBPDADDDBL);
849 FDBUNLOCKRECORD(fdb, id);
850 FDBUNLOCKMETHOD(fdb);
851 return rv ? num : nan("");
852 }
853
854
855 /* Synchronize updated contents of a fixed-length database object with the file and the device. */
tcfdbsync(TCFDB * fdb)856 bool tcfdbsync(TCFDB *fdb){
857 assert(fdb);
858 if(!FDBLOCKMETHOD(fdb, true)) return false;
859 if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER) || fdb->tran){
860 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
861 FDBUNLOCKMETHOD(fdb);
862 return false;
863 }
864 bool rv = tcfdbmemsync(fdb, true);
865 FDBUNLOCKMETHOD(fdb);
866 return rv;
867 }
868
869
870 /* Optimize the file of a fixed-length database object. */
tcfdboptimize(TCFDB * fdb,int32_t width,int64_t limsiz)871 bool tcfdboptimize(TCFDB *fdb, int32_t width, int64_t limsiz){
872 assert(fdb);
873 if(!FDBLOCKMETHOD(fdb, true)) return false;
874 if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER) || fdb->tran){
875 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
876 FDBUNLOCKMETHOD(fdb);
877 return false;
878 }
879 FDBTHREADYIELD(fdb);
880 bool rv = tcfdboptimizeimpl(fdb, width, limsiz);
881 FDBUNLOCKMETHOD(fdb);
882 return rv;
883 }
884
885
886 /* Remove all records of a fixed-length database object. */
tcfdbvanish(TCFDB * fdb)887 bool tcfdbvanish(TCFDB *fdb){
888 assert(fdb);
889 if(!FDBLOCKMETHOD(fdb, true)) return false;
890 if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER) || fdb->tran){
891 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
892 FDBUNLOCKMETHOD(fdb);
893 return false;
894 }
895 FDBTHREADYIELD(fdb);
896 bool rv = tcfdbvanishimpl(fdb);
897 FDBUNLOCKMETHOD(fdb);
898 return rv;
899 }
900
901
902 /* Copy the database file of a fixed-length database object. */
tcfdbcopy(TCFDB * fdb,const char * path)903 bool tcfdbcopy(TCFDB *fdb, const char *path){
904 assert(fdb && path);
905 if(!FDBLOCKMETHOD(fdb, false)) return false;
906 if(fdb->fd < 0){
907 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
908 FDBUNLOCKMETHOD(fdb);
909 return false;
910 }
911 if(!FDBLOCKALLRECORDS(fdb, false)){
912 FDBUNLOCKMETHOD(fdb);
913 return false;
914 }
915 FDBTHREADYIELD(fdb);
916 bool rv = tcfdbcopyimpl(fdb, path);
917 FDBUNLOCKALLRECORDS(fdb);
918 FDBUNLOCKMETHOD(fdb);
919 return rv;
920 }
921
922
923 /* Begin the transaction of a fixed-length database object. */
tcfdbtranbegin(TCFDB * fdb)924 bool tcfdbtranbegin(TCFDB *fdb){
925 assert(fdb);
926 for(double wsec = 1.0 / sysconf(_SC_CLK_TCK); true; wsec *= 2){
927 if(!FDBLOCKMETHOD(fdb, true)) return false;
928 if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER) || fdb->fatal){
929 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
930 FDBUNLOCKMETHOD(fdb);
931 return false;
932 }
933 if(!fdb->tran) break;
934 FDBUNLOCKMETHOD(fdb);
935 if(wsec > 1.0) wsec = 1.0;
936 tcsleep(wsec);
937 }
938 if(!tcfdbmemsync(fdb, false)){
939 FDBUNLOCKMETHOD(fdb);
940 return false;
941 }
942 if((fdb->omode & FDBOTSYNC) && fsync(fdb->fd) == -1){
943 tcfdbsetecode(fdb, TCESYNC, __FILE__, __LINE__, __func__);
944 return false;
945 }
946 if(fdb->walfd < 0){
947 char *tpath = tcsprintf("%s%c%s", fdb->path, MYEXTCHR, FDBWALSUFFIX);
948 int walfd = open(tpath, O_RDWR | O_CREAT | O_TRUNC, FDBFILEMODE);
949 TCFREE(tpath);
950 if(walfd < 0){
951 int ecode = TCEOPEN;
952 switch(errno){
953 case EACCES: ecode = TCENOPERM; break;
954 case ENOENT: ecode = TCENOFILE; break;
955 case ENOTDIR: ecode = TCENOFILE; break;
956 }
957 tcfdbsetecode(fdb, ecode, __FILE__, __LINE__, __func__);
958 FDBUNLOCKMETHOD(fdb);
959 return false;
960 }
961 fdb->walfd = walfd;
962 }
963 tcfdbsetflag(fdb, FDBFOPEN, false);
964 if(!tcfdbwalinit(fdb)){
965 tcfdbsetflag(fdb, FDBFOPEN, true);
966 FDBUNLOCKMETHOD(fdb);
967 return false;
968 }
969 tcfdbsetflag(fdb, FDBFOPEN, true);
970 fdb->tran = true;
971 FDBUNLOCKMETHOD(fdb);
972 return true;
973 }
974
975
976 /* Commit the transaction of a fixed-length database object. */
tcfdbtrancommit(TCFDB * fdb)977 bool tcfdbtrancommit(TCFDB *fdb){
978 assert(fdb);
979 if(!FDBLOCKMETHOD(fdb, true)) return false;
980 if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER) || fdb->fatal || !fdb->tran){
981 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
982 FDBUNLOCKMETHOD(fdb);
983 return false;
984 }
985 bool err = false;
986 if(!tcfdbmemsync(fdb, fdb->omode & FDBOTSYNC)) err = true;
987 if(!err && ftruncate(fdb->walfd, 0) == -1){
988 tcfdbsetecode(fdb, TCETRUNC, __FILE__, __LINE__, __func__);
989 err = true;
990 }
991 fdb->tran = false;
992 FDBUNLOCKMETHOD(fdb);
993 return !err;
994 }
995
996
997 /* Abort the transaction of a fixed-length database object. */
tcfdbtranabort(TCFDB * fdb)998 bool tcfdbtranabort(TCFDB *fdb){
999 assert(fdb);
1000 if(!FDBLOCKMETHOD(fdb, true)) return false;
1001 if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER) || !fdb->tran){
1002 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1003 FDBUNLOCKMETHOD(fdb);
1004 return false;
1005 }
1006 bool err = false;
1007 if(!tcfdbmemsync(fdb, false)) err = true;
1008 if(!tcfdbwalrestore(fdb, fdb->path)) err = true;
1009 char hbuf[FDBHEADSIZ];
1010 if(lseek(fdb->fd, 0, SEEK_SET) == -1){
1011 tcfdbsetecode(fdb, TCESEEK, __FILE__, __LINE__, __func__);
1012 err = false;
1013 } else if(!tcread(fdb->fd, hbuf, FDBHEADSIZ)){
1014 tcfdbsetecode(fdb, TCEREAD, __FILE__, __LINE__, __func__);
1015 err = false;
1016 } else {
1017 tcfdbloadmeta(fdb, hbuf);
1018 }
1019 fdb->tran = false;
1020 FDBUNLOCKMETHOD(fdb);
1021 return !err;
1022 }
1023
1024
1025 /* Get the file path of a fixed-length database object. */
tcfdbpath(TCFDB * fdb)1026 const char *tcfdbpath(TCFDB *fdb){
1027 assert(fdb);
1028 if(!FDBLOCKMETHOD(fdb, false)) return NULL;
1029 if(fdb->fd < 0){
1030 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1031 FDBUNLOCKMETHOD(fdb);
1032 return NULL;
1033 }
1034 const char *rv = fdb->path;
1035 FDBUNLOCKMETHOD(fdb);
1036 return rv;
1037 }
1038
1039
1040 /* Get the number of records of a fixed-length database object. */
tcfdbrnum(TCFDB * fdb)1041 uint64_t tcfdbrnum(TCFDB *fdb){
1042 assert(fdb);
1043 if(!FDBLOCKMETHOD(fdb, false)) return 0;
1044 if(fdb->fd < 0){
1045 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1046 FDBUNLOCKMETHOD(fdb);
1047 return 0;
1048 }
1049 uint64_t rv = fdb->rnum;
1050 FDBUNLOCKMETHOD(fdb);
1051 return rv;
1052 }
1053
1054
1055 /* Get the size of the database file of a fixed-length database object. */
tcfdbfsiz(TCFDB * fdb)1056 uint64_t tcfdbfsiz(TCFDB *fdb){
1057 assert(fdb);
1058 if(!FDBLOCKMETHOD(fdb, false)) return 0;
1059 if(fdb->fd < 0){
1060 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1061 FDBUNLOCKMETHOD(fdb);
1062 return 0;
1063 }
1064 uint64_t rv = fdb->fsiz;
1065 FDBUNLOCKMETHOD(fdb);
1066 return rv;
1067 }
1068
1069
1070
1071 /*************************************************************************************************
1072 * features for experts
1073 *************************************************************************************************/
1074
1075
1076 /* Set the error code of a fixed-length database object. */
tcfdbsetecode(TCFDB * fdb,int ecode,const char * filename,int line,const char * func)1077 void tcfdbsetecode(TCFDB *fdb, int ecode, const char *filename, int line, const char *func){
1078 assert(fdb && filename && line >= 1 && func);
1079 int myerrno = errno;
1080 if(!fdb->fatal){
1081 fdb->ecode = ecode;
1082 if(fdb->mmtx) pthread_setspecific(*(pthread_key_t *)fdb->eckey, (void *)(intptr_t)ecode);
1083 }
1084 if(ecode != TCEINVALID && ecode != TCEKEEP && ecode != TCENOREC){
1085 fdb->fatal = true;
1086 if(fdb->fd >= 0 && (fdb->omode & FDBOWRITER)) tcfdbsetflag(fdb, FDBFFATAL, true);
1087 }
1088 if(fdb->dbgfd >= 0 && (fdb->dbgfd != UINT16_MAX || fdb->fatal)){
1089 int dbgfd = (fdb->dbgfd == UINT16_MAX) ? 1 : fdb->dbgfd;
1090 char obuf[FDBIOBUFSIZ];
1091 int osiz = sprintf(obuf, "ERROR:%s:%d:%s:%s:%d:%s:%d:%s\n", filename, line, func,
1092 fdb->path ? fdb->path : "-", ecode, tcfdberrmsg(ecode),
1093 myerrno, strerror(myerrno));
1094 tcwrite(dbgfd, obuf, osiz);
1095 }
1096 }
1097
1098
1099 /* Set the file descriptor for debugging output. */
tcfdbsetdbgfd(TCFDB * fdb,int fd)1100 void tcfdbsetdbgfd(TCFDB *fdb, int fd){
1101 assert(fdb && fd >= 0);
1102 fdb->dbgfd = fd;
1103 }
1104
1105
1106 /* Get the file descriptor for debugging output. */
tcfdbdbgfd(TCFDB * fdb)1107 int tcfdbdbgfd(TCFDB *fdb){
1108 assert(fdb);
1109 return fdb->dbgfd;
1110 }
1111
1112
1113 /* Check whether mutual exclusion control is set to a fixed-length database object. */
tcfdbhasmutex(TCFDB * fdb)1114 bool tcfdbhasmutex(TCFDB *fdb){
1115 assert(fdb);
1116 return fdb->mmtx != NULL;
1117 }
1118
1119
1120 /* Synchronize updating contents on memory of a fixed-length database object. */
tcfdbmemsync(TCFDB * fdb,bool phys)1121 bool tcfdbmemsync(TCFDB *fdb, bool phys){
1122 assert(fdb);
1123 if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){
1124 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1125 return false;
1126 }
1127 bool err = false;
1128 char hbuf[FDBHEADSIZ];
1129 tcfdbdumpmeta(fdb, hbuf);
1130 memcpy(fdb->map, hbuf, FDBOPAQUEOFF);
1131 if(phys){
1132 if(msync(fdb->map, fdb->limsiz, MS_SYNC) == -1){
1133 tcfdbsetecode(fdb, TCEMMAP, __FILE__, __LINE__, __func__);
1134 err = true;
1135 }
1136 if(fsync(fdb->fd) == -1){
1137 tcfdbsetecode(fdb, TCESYNC, __FILE__, __LINE__, __func__);
1138 err = true;
1139 }
1140 }
1141 return !err;
1142 }
1143
1144
1145 /* Get the minimum ID number of records of a fixed-length database object. */
tcfdbmin(TCFDB * fdb)1146 uint64_t tcfdbmin(TCFDB *fdb){
1147 assert(fdb);
1148 if(fdb->fd < 0){
1149 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1150 return 0;
1151 }
1152 return fdb->min;
1153 }
1154
1155
1156 /* Get the maximum ID number of records of a fixed-length database object. */
tcfdbmax(TCFDB * fdb)1157 uint64_t tcfdbmax(TCFDB *fdb){
1158 assert(fdb);
1159 if(fdb->fd < 0){
1160 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1161 return 0;
1162 }
1163 return fdb->max;
1164 }
1165
1166
1167 /* Get the width of the value of each record of a fixed-length database object. */
tcfdbwidth(TCFDB * fdb)1168 uint32_t tcfdbwidth(TCFDB *fdb){
1169 assert(fdb);
1170 if(fdb->fd < 0){
1171 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1172 return 0;
1173 }
1174 return fdb->width;
1175 }
1176
1177
1178 /* Get the limit file size of a fixed-length database object. */
tcfdblimsiz(TCFDB * fdb)1179 uint64_t tcfdblimsiz(TCFDB *fdb){
1180 assert(fdb);
1181 if(fdb->fd < 0){
1182 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1183 return 0;
1184 }
1185 return fdb->limsiz;
1186 }
1187
1188
1189 /* Get the limit ID number of a fixed-length database object. */
tcfdblimid(TCFDB * fdb)1190 uint64_t tcfdblimid(TCFDB *fdb){
1191 assert(fdb);
1192 if(fdb->fd < 0){
1193 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1194 return 0;
1195 }
1196 return fdb->limid;
1197 }
1198
1199
1200 /* Get the inode number of the database file of a fixed-length database object. */
tcfdbinode(TCFDB * fdb)1201 uint64_t tcfdbinode(TCFDB *fdb){
1202 assert(fdb);
1203 if(fdb->fd < 0){
1204 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1205 return 0;
1206 }
1207 return fdb->inode;
1208 }
1209
1210
1211 /* Get the modification time of the database file of a fixed-length database object. */
tcfdbmtime(TCFDB * fdb)1212 time_t tcfdbmtime(TCFDB *fdb){
1213 assert(fdb);
1214 if(fdb->fd < 0){
1215 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1216 return 0;
1217 }
1218 return fdb->mtime;
1219 }
1220
1221
1222 /* Get the connection mode of a fixed-length database object. */
tcfdbomode(TCFDB * fdb)1223 int tcfdbomode(TCFDB *fdb){
1224 assert(fdb);
1225 if(fdb->fd < 0){
1226 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1227 return 0;
1228 }
1229 return fdb->omode;
1230 }
1231
1232
1233 /* Get the database type of a fixed-length database object. */
tcfdbtype(TCFDB * fdb)1234 uint8_t tcfdbtype(TCFDB *fdb){
1235 assert(fdb);
1236 if(fdb->fd < 0){
1237 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1238 return 0;
1239 }
1240 return fdb->type;
1241 }
1242
1243
1244 /* Get the additional flags of a fixed-length database object. */
tcfdbflags(TCFDB * fdb)1245 uint8_t tcfdbflags(TCFDB *fdb){
1246 assert(fdb);
1247 if(fdb->fd < 0){
1248 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1249 return 0;
1250 }
1251 return fdb->flags;
1252 }
1253
1254
1255 /* Get the pointer to the opaque field of a fixed-length database object. */
tcfdbopaque(TCFDB * fdb)1256 char *tcfdbopaque(TCFDB *fdb){
1257 assert(fdb);
1258 if(fdb->fd < 0){
1259 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1260 return NULL;
1261 }
1262 return fdb->map + FDBOPAQUEOFF;
1263 }
1264
1265
1266 /* Store a record into a fixed-length database object with a duplication handler. */
tcfdbputproc(TCFDB * fdb,int64_t id,const void * vbuf,int vsiz,TCPDPROC proc,void * op)1267 bool tcfdbputproc(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz, TCPDPROC proc, void *op){
1268 assert(fdb && proc);
1269 if(!FDBLOCKMETHOD(fdb, id < 1)) return false;
1270 if(fdb->fd < 0 || !(fdb->omode & FDBOWRITER)){
1271 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1272 FDBUNLOCKMETHOD(fdb);
1273 return false;
1274 }
1275 if(id == FDBIDMIN){
1276 id = fdb->min;
1277 } else if(id == FDBIDPREV){
1278 id = fdb->min - 1;
1279 } else if(id == FDBIDMAX){
1280 id = fdb->max;
1281 } else if(id == FDBIDNEXT){
1282 id = fdb->max + 1;
1283 }
1284 if(id < 1 || id > fdb->limid){
1285 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1286 FDBUNLOCKMETHOD(fdb);
1287 return false;
1288 }
1289 if(!FDBLOCKRECORD(fdb, true, id)){
1290 FDBUNLOCKMETHOD(fdb);
1291 return false;
1292 }
1293 FDBPDPROCOP procop;
1294 procop.proc = proc;
1295 procop.op = op;
1296 FDBPDPROCOP *procptr = &procop;
1297 tcgeneric_t stack[(FDBDEFWIDTH+TCNUMBUFSIZ)/sizeof(tcgeneric_t)+1];
1298 char *rbuf;
1299 if(vbuf){
1300 if(vsiz <= sizeof(stack) - sizeof(procptr)){
1301 rbuf = (char *)stack;
1302 } else {
1303 TCMALLOC(rbuf, vsiz + sizeof(procptr));
1304 }
1305 char *wp = rbuf;
1306 memcpy(wp, &procptr, sizeof(procptr));
1307 wp += sizeof(procptr);
1308 memcpy(wp, vbuf, vsiz);
1309 vbuf = rbuf + sizeof(procptr);
1310 } else {
1311 rbuf = (char *)stack;
1312 memcpy(rbuf, &procptr, sizeof(procptr));
1313 vbuf = rbuf + sizeof(procptr);
1314 vsiz = -1;
1315 }
1316 bool rv = tcfdbputimpl(fdb, id, vbuf, vsiz, FDBPDPROC);
1317 if(rbuf != (char *)stack) TCFREE(rbuf);
1318 FDBUNLOCKRECORD(fdb, id);
1319 FDBUNLOCKMETHOD(fdb);
1320 return rv;
1321 }
1322
1323
1324 /* Move the iterator to the record corresponding a key of a fixed-length database object. */
tcfdbiterinit2(TCFDB * fdb,int64_t id)1325 bool tcfdbiterinit2(TCFDB *fdb, int64_t id){
1326 assert(fdb);
1327 if(!FDBLOCKMETHOD(fdb, true)) return false;
1328 if(fdb->fd < 0){
1329 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1330 FDBUNLOCKMETHOD(fdb);
1331 return false;
1332 }
1333 if(id == FDBIDMIN){
1334 id = fdb->min;
1335 } else if(id == FDBIDMAX){
1336 id = fdb->max;
1337 }
1338 if(id < 1 || id > fdb->limid){
1339 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1340 FDBUNLOCKMETHOD(fdb);
1341 return false;
1342 }
1343 bool rv = tcfdbiterjumpimpl(fdb, id);
1344 FDBUNLOCKMETHOD(fdb);
1345 return rv;
1346 }
1347
1348
1349 /* Move the iterator to the decimal record of a fixed-length database object. */
tcfdbiterinit3(TCFDB * fdb,const void * kbuf,int ksiz)1350 bool tcfdbiterinit3(TCFDB *fdb, const void *kbuf, int ksiz){
1351 assert(fdb && kbuf && ksiz >= 0);
1352 return tcfdbiterinit2(fdb, tcfdbkeytoid(kbuf, ksiz));
1353 }
1354
1355
1356 /* Move the iterator to the decimal string record of a fixed-length database object. */
tcfdbiterinit4(TCFDB * fdb,const char * kstr)1357 bool tcfdbiterinit4(TCFDB *fdb, const char *kstr){
1358 assert(fdb && kstr);
1359 return tcfdbiterinit2(fdb, tcfdbkeytoid(kstr, strlen(kstr)));
1360 }
1361
1362
1363 /* Process each record atomically of a fixed-length database object. */
tcfdbforeach(TCFDB * fdb,TCITER iter,void * op)1364 bool tcfdbforeach(TCFDB *fdb, TCITER iter, void *op){
1365 assert(fdb && iter);
1366 if(!FDBLOCKMETHOD(fdb, false)) return false;
1367 if(fdb->fd < 0){
1368 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1369 FDBUNLOCKMETHOD(fdb);
1370 return false;
1371 }
1372 if(!FDBLOCKALLRECORDS(fdb, false)){
1373 FDBUNLOCKMETHOD(fdb);
1374 return false;
1375 }
1376 FDBTHREADYIELD(fdb);
1377 bool rv = tcfdbforeachimpl(fdb, iter, op);
1378 FDBUNLOCKALLRECORDS(fdb);
1379 FDBUNLOCKMETHOD(fdb);
1380 return rv;
1381 }
1382
1383
1384 /* Generate the ID number from arbitrary binary data. */
tcfdbkeytoid(const char * kbuf,int ksiz)1385 int64_t tcfdbkeytoid(const char *kbuf, int ksiz){
1386 assert(kbuf && ksiz >= 0);
1387 if(ksiz == 3 && !memcmp(kbuf, "min", 3)){
1388 return FDBIDMIN;
1389 } else if(ksiz == 4 && !memcmp(kbuf, "prev", 4)){
1390 return FDBIDPREV;
1391 } else if(ksiz == 3 && !memcmp(kbuf, "max", 3)){
1392 return FDBIDMAX;
1393 } else if(ksiz == 4 && !memcmp(kbuf, "next", 4)){
1394 return FDBIDNEXT;
1395 }
1396 int64_t id = 0;
1397 const char *end = kbuf + ksiz;
1398 while(kbuf < end){
1399 int c = *(unsigned char *)(kbuf++);
1400 if(c >= '0' && c <= '9') id = id * 10 + c - '0';
1401 }
1402 return id;
1403 }
1404
1405
1406
1407 /*************************************************************************************************
1408 * private features
1409 *************************************************************************************************/
1410
1411
1412 /* Serialize meta data into a buffer.
1413 `fdb' specifies the fixed-length database object.
1414 `hbuf' specifies the buffer. */
tcfdbdumpmeta(TCFDB * fdb,char * hbuf)1415 static void tcfdbdumpmeta(TCFDB *fdb, char *hbuf){
1416 memset(hbuf, 0, FDBHEADSIZ);
1417 sprintf(hbuf, "%s\n%s:%d\n", FDBMAGICDATA, _TC_FORMATVER, _TC_LIBVER);
1418 memcpy(hbuf + FDBTYPEOFF, &(fdb->type), sizeof(fdb->type));
1419 memcpy(hbuf + FDBFLAGSOFF, &(fdb->flags), sizeof(fdb->flags));
1420 uint64_t llnum;
1421 llnum = fdb->rnum;
1422 llnum = TCHTOILL(llnum);
1423 memcpy(hbuf + FDBRNUMOFF, &llnum, sizeof(llnum));
1424 llnum = fdb->fsiz;
1425 llnum = TCHTOILL(llnum);
1426 memcpy(hbuf + FDBFSIZOFF, &llnum, sizeof(llnum));
1427 llnum = fdb->width;
1428 llnum = TCHTOILL(llnum);
1429 memcpy(hbuf + FDBWIDTHOFF, &llnum, sizeof(llnum));
1430 llnum = fdb->limsiz;
1431 llnum = TCHTOILL(llnum);
1432 memcpy(hbuf + FDBLIMSIZOFF, &llnum, sizeof(llnum));
1433 llnum = fdb->min;
1434 llnum = TCHTOILL(llnum);
1435 memcpy(hbuf + FDBMINOFF, &llnum, sizeof(llnum));
1436 llnum = fdb->max;
1437 llnum = TCHTOILL(llnum);
1438 memcpy(hbuf + FDBMAXOFF, &llnum, sizeof(llnum));
1439 }
1440
1441
1442 /* Deserialize meta data from a buffer.
1443 `fdb' specifies the fixed-length database object.
1444 `hbuf' specifies the buffer. */
tcfdbloadmeta(TCFDB * fdb,const char * hbuf)1445 static void tcfdbloadmeta(TCFDB *fdb, const char *hbuf){
1446 memcpy(&(fdb->type), hbuf + FDBTYPEOFF, sizeof(fdb->type));
1447 memcpy(&(fdb->flags), hbuf + FDBFLAGSOFF, sizeof(fdb->flags));
1448 uint64_t llnum;
1449 memcpy(&llnum, hbuf + FDBRNUMOFF, sizeof(llnum));
1450 fdb->rnum = TCITOHLL(llnum);
1451 memcpy(&llnum, hbuf + FDBFSIZOFF, sizeof(llnum));
1452 fdb->fsiz = TCITOHLL(llnum);
1453 memcpy(&llnum, hbuf + FDBWIDTHOFF, sizeof(llnum));
1454 fdb->width = TCITOHLL(llnum);
1455 memcpy(&llnum, hbuf + FDBLIMSIZOFF, sizeof(llnum));
1456 fdb->limsiz = TCITOHLL(llnum);
1457 memcpy(&llnum, hbuf + FDBMINOFF, sizeof(llnum));
1458 fdb->min = TCITOHLL(llnum);
1459 memcpy(&llnum, hbuf + FDBMAXOFF, sizeof(llnum));
1460 fdb->max = TCITOHLL(llnum);
1461 }
1462
1463
1464 /* Clear all members.
1465 `fdb' specifies the fixed-length database object. */
tcfdbclear(TCFDB * fdb)1466 static void tcfdbclear(TCFDB *fdb){
1467 assert(fdb);
1468 fdb->mmtx = NULL;
1469 fdb->amtx = NULL;
1470 fdb->rmtxs = NULL;
1471 fdb->tmtx = NULL;
1472 fdb->wmtx = NULL;
1473 fdb->eckey = NULL;
1474 fdb->rpath = NULL;
1475 fdb->type = TCDBTFIXED;
1476 fdb->flags = 0;
1477 fdb->width = FDBDEFWIDTH;
1478 fdb->limsiz = FDBDEFLIMSIZ;
1479 fdb->wsiz = 0;
1480 fdb->rsiz = 0;
1481 fdb->limid = 0;
1482 fdb->path = NULL;
1483 fdb->fd = -1;
1484 fdb->omode = 0;
1485 fdb->rnum = 0;
1486 fdb->fsiz = 0;
1487 fdb->min = 0;
1488 fdb->max = 0;
1489 fdb->iter = 0;
1490 fdb->map = NULL;
1491 fdb->array = NULL;
1492 fdb->ecode = TCESUCCESS;
1493 fdb->fatal = false;
1494 fdb->inode = 0;
1495 fdb->mtime = 0;
1496 fdb->tran = false;
1497 fdb->walfd = -1;
1498 fdb->walend = 0;
1499 fdb->dbgfd = -1;
1500 fdb->cnt_writerec = -1;
1501 fdb->cnt_readrec = -1;
1502 fdb->cnt_truncfile = -1;
1503 TCDODEBUG(fdb->cnt_writerec = 0);
1504 TCDODEBUG(fdb->cnt_readrec = 0);
1505 TCDODEBUG(fdb->cnt_truncfile = 0);
1506 }
1507
1508
1509 /* Set the open flag.
1510 `fdb' specifies the fixed-length database object.
1511 `flag' specifies the flag value.
1512 `sign' specifies the sign. */
tcfdbsetflag(TCFDB * fdb,int flag,bool sign)1513 static void tcfdbsetflag(TCFDB *fdb, int flag, bool sign){
1514 assert(fdb);
1515 char *fp = (char *)fdb->map + FDBFLAGSOFF;
1516 if(sign){
1517 *fp |= (uint8_t)flag;
1518 } else {
1519 *fp &= ~(uint8_t)flag;
1520 }
1521 fdb->flags = *fp;
1522 }
1523
1524
1525 /* Initialize the write ahead logging file.
1526 `fdb' specifies the fixed-length database object.
1527 If successful, the return value is true, else, it is false. */
tcfdbwalinit(TCFDB * fdb)1528 static bool tcfdbwalinit(TCFDB *fdb){
1529 assert(fdb);
1530 if(lseek(fdb->walfd, 0, SEEK_SET) == -1){
1531 tcfdbsetecode(fdb, TCESEEK, __FILE__, __LINE__, __func__);
1532 return false;
1533 }
1534 if(ftruncate(fdb->walfd, 0) == -1){
1535 tcfdbsetecode(fdb, TCETRUNC, __FILE__, __LINE__, __func__);
1536 return false;
1537 }
1538 uint64_t llnum = fdb->fsiz;
1539 llnum = TCHTOILL(llnum);
1540 if(!tcwrite(fdb->walfd, &llnum, sizeof(llnum))){
1541 tcfdbsetecode(fdb, TCEWRITE, __FILE__, __LINE__, __func__);
1542 return false;
1543 }
1544 fdb->walend = fdb->fsiz;
1545 if(!tcfdbwalwrite(fdb, 0, FDBHEADSIZ)) return false;
1546 return true;
1547 }
1548
1549
1550 /* Write an event into the write ahead logging file.
1551 `fdb' specifies the fixed-length database object.
1552 `off' specifies the offset of the region to be updated.
1553 `size' specifies the size of the region.
1554 If successful, the return value is true, else, it is false. */
tcfdbwalwrite(TCFDB * fdb,uint64_t off,int64_t size)1555 static bool tcfdbwalwrite(TCFDB *fdb, uint64_t off, int64_t size){
1556 assert(fdb && off >= 0 && size >= 0);
1557 if(off + size > fdb->walend) size = fdb->walend - off;
1558 if(size < 1) return true;
1559 char stack[FDBIOBUFSIZ];
1560 char *buf;
1561 if(size + sizeof(off) + sizeof(size) <= FDBIOBUFSIZ){
1562 buf = stack;
1563 } else {
1564 TCMALLOC(buf, size + sizeof(off) + sizeof(size));
1565 }
1566 char *wp = buf;
1567 uint64_t llnum = TCHTOILL(off);
1568 memcpy(wp, &llnum, sizeof(llnum));
1569 wp += sizeof(llnum);
1570 uint32_t lnum = TCHTOIL(size);
1571 memcpy(wp, &lnum, sizeof(lnum));
1572 wp += sizeof(lnum);
1573 memcpy(wp, fdb->map + off, size);
1574 wp += size;
1575 if(!FDBLOCKWAL(fdb)) return false;
1576 if(!tcwrite(fdb->walfd, buf, wp - buf)){
1577 tcfdbsetecode(fdb, TCEWRITE, __FILE__, __LINE__, __func__);
1578 if(buf != stack) TCFREE(buf);
1579 FDBUNLOCKWAL(fdb);
1580 return false;
1581 }
1582 if(buf != stack) TCFREE(buf);
1583 if((fdb->omode & FDBOTSYNC) && fsync(fdb->walfd) == -1){
1584 tcfdbsetecode(fdb, TCESYNC, __FILE__, __LINE__, __func__);
1585 FDBUNLOCKWAL(fdb);
1586 return false;
1587 }
1588 FDBUNLOCKWAL(fdb);
1589 return true;
1590 }
1591
1592
1593 /* Restore the database from the write ahead logging file.
1594 `fdb' specifies the fixed-length database object.
1595 `path' specifies the path of the database file.
1596 If successful, the return value is true, else, it is false. */
tcfdbwalrestore(TCFDB * fdb,const char * path)1597 static int tcfdbwalrestore(TCFDB *fdb, const char *path){
1598 assert(fdb && path);
1599 char *tpath = tcsprintf("%s%c%s", path, MYEXTCHR, FDBWALSUFFIX);
1600 int walfd = open(tpath, O_RDONLY, FDBFILEMODE);
1601 TCFREE(tpath);
1602 if(walfd < 0) return false;
1603 bool err = false;
1604 uint64_t walsiz = 0;
1605 struct stat sbuf;
1606 if(fstat(walfd, &sbuf) == 0){
1607 walsiz = sbuf.st_size;
1608 } else {
1609 tcfdbsetecode(fdb, TCESTAT, __FILE__, __LINE__, __func__);
1610 err = true;
1611 }
1612 if(walsiz >= sizeof(walsiz) + FDBHEADSIZ){
1613 int dbfd = fdb->fd;
1614 int tfd = -1;
1615 if(!(fdb->omode & FDBOWRITER)){
1616 tfd = open(path, O_WRONLY, FDBFILEMODE);
1617 if(tfd >= 0){
1618 dbfd = tfd;
1619 } else {
1620 int ecode = TCEOPEN;
1621 switch(errno){
1622 case EACCES: ecode = TCENOPERM; break;
1623 case ENOENT: ecode = TCENOFILE; break;
1624 case ENOTDIR: ecode = TCENOFILE; break;
1625 }
1626 tcfdbsetecode(fdb, ecode, __FILE__, __LINE__, __func__);
1627 err = true;
1628 }
1629 }
1630 uint64_t fsiz = 0;
1631 if(tcread(walfd, &fsiz, sizeof(fsiz))){
1632 fsiz = TCITOHLL(fsiz);
1633 } else {
1634 tcfdbsetecode(fdb, TCEREAD, __FILE__, __LINE__, __func__);
1635 err = true;
1636 }
1637 TCLIST *list = tclistnew();
1638 uint64_t waloff = sizeof(fsiz);
1639 char stack[FDBIOBUFSIZ];
1640 while(waloff < walsiz){
1641 uint64_t off;
1642 uint32_t size;
1643 if(!tcread(walfd, stack, sizeof(off) + sizeof(size))){
1644 tcfdbsetecode(fdb, TCEREAD, __FILE__, __LINE__, __func__);
1645 err = true;
1646 break;
1647 }
1648 memcpy(&off, stack, sizeof(off));
1649 off = TCITOHLL(off);
1650 memcpy(&size, stack + sizeof(off), sizeof(size));
1651 size = TCITOHL(size);
1652 char *buf;
1653 if(sizeof(off) + size <= FDBIOBUFSIZ){
1654 buf = stack;
1655 } else {
1656 TCMALLOC(buf, sizeof(off) + size);
1657 }
1658 *(uint64_t *)buf = off;
1659 if(!tcread(walfd, buf + sizeof(off), size)){
1660 tcfdbsetecode(fdb, TCEREAD, __FILE__, __LINE__, __func__);
1661 err = true;
1662 if(buf != stack) TCFREE(buf);
1663 break;
1664 }
1665 TCLISTPUSH(list, buf, sizeof(off) + size);
1666 if(buf != stack) TCFREE(buf);
1667 waloff += sizeof(off) + sizeof(size) + size;
1668 }
1669 for(int i = TCLISTNUM(list) - 1; i >= 0; i--){
1670 const char *rec;
1671 int size;
1672 TCLISTVAL(rec, list, i, size);
1673 uint64_t off = *(uint64_t *)rec;
1674 rec += sizeof(off);
1675 size -= sizeof(off);
1676 if(lseek(dbfd, off, SEEK_SET) == -1){
1677 tcfdbsetecode(fdb, TCESEEK, __FILE__, __LINE__, __func__);
1678 err = true;
1679 break;
1680 }
1681 if(!tcwrite(dbfd, rec, size)){
1682 tcfdbsetecode(fdb, TCEWRITE, __FILE__, __LINE__, __func__);
1683 err = true;
1684 break;
1685 }
1686 }
1687 tclistdel(list);
1688 if(ftruncate(dbfd, fsiz) == -1){
1689 tcfdbsetecode(fdb, TCETRUNC, __FILE__, __LINE__, __func__);
1690 err = true;
1691 }
1692 if((fdb->omode & FDBOTSYNC) && fsync(dbfd) == -1){
1693 tcfdbsetecode(fdb, TCESYNC, __FILE__, __LINE__, __func__);
1694 err = true;
1695 }
1696 if(tfd >= 0 && close(tfd) == -1){
1697 tcfdbsetecode(fdb, TCECLOSE, __FILE__, __LINE__, __func__);
1698 err = true;
1699 }
1700 } else {
1701 err = true;
1702 }
1703 if(close(walfd) == -1){
1704 tcfdbsetecode(fdb, TCECLOSE, __FILE__, __LINE__, __func__);
1705 err = true;
1706 }
1707 return !err;
1708 }
1709
1710
1711 /* Remove the write ahead logging file.
1712 `fdb' specifies the fixed-length database object.
1713 `path' specifies the path of the database file.
1714 If successful, the return value is true, else, it is false. */
tcfdbwalremove(TCFDB * fdb,const char * path)1715 static bool tcfdbwalremove(TCFDB *fdb, const char *path){
1716 assert(fdb && path);
1717 char *tpath = tcsprintf("%s%c%s", path, MYEXTCHR, FDBWALSUFFIX);
1718 bool err = false;
1719 if(unlink(tpath) == -1 && errno != ENOENT){
1720 tcfdbsetecode(fdb, TCEUNLINK, __FILE__, __LINE__, __func__);
1721 err = true;
1722 }
1723 TCFREE(tpath);
1724 return !err;
1725 }
1726
1727
1728 /* Open a database file and connect a fixed-length database object.
1729 `fdb' specifies the fixed-length database object.
1730 `path' specifies the path of the database file.
1731 `omode' specifies the connection mode.
1732 If successful, the return value is true, else, it is false. */
tcfdbopenimpl(TCFDB * fdb,const char * path,int omode)1733 static bool tcfdbopenimpl(TCFDB *fdb, const char *path, int omode){
1734 assert(fdb && path);
1735 int mode = O_RDONLY;
1736 if(omode & FDBOWRITER){
1737 mode = O_RDWR;
1738 if(omode & FDBOCREAT) mode |= O_CREAT;
1739 }
1740 int fd = open(path, mode, FDBFILEMODE);
1741 if(fd < 0){
1742 int ecode = TCEOPEN;
1743 switch(errno){
1744 case EACCES: ecode = TCENOPERM; break;
1745 case ENOENT: ecode = TCENOFILE; break;
1746 case ENOTDIR: ecode = TCENOFILE; break;
1747 }
1748 tcfdbsetecode(fdb, ecode, __FILE__, __LINE__, __func__);
1749 return false;
1750 }
1751 if(!(omode & FDBONOLCK)){
1752 if(!tclock(fd, omode & FDBOWRITER, omode & FDBOLCKNB)){
1753 tcfdbsetecode(fdb, TCELOCK, __FILE__, __LINE__, __func__);
1754 close(fd);
1755 return false;
1756 }
1757 }
1758 if((omode & FDBOWRITER) && (omode & FDBOTRUNC)){
1759 if(ftruncate(fd, 0) == -1){
1760 tcfdbsetecode(fdb, TCETRUNC, __FILE__, __LINE__, __func__);
1761 close(fd);
1762 return false;
1763 }
1764 if(!tcfdbwalremove(fdb, path)){
1765 close(fd);
1766 return false;
1767 }
1768 }
1769 struct stat sbuf;
1770 if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){
1771 tcfdbsetecode(fdb, TCESTAT, __FILE__, __LINE__, __func__);
1772 close(fd);
1773 return false;
1774 }
1775 char hbuf[FDBHEADSIZ];
1776 if((omode & FDBOWRITER) && sbuf.st_size < 1){
1777 fdb->flags = 0;
1778 fdb->rnum = 0;
1779 fdb->fsiz = FDBHEADSIZ;
1780 fdb->min = 0;
1781 fdb->max = 0;
1782 tcfdbdumpmeta(fdb, hbuf);
1783 if(!tcwrite(fd, hbuf, FDBHEADSIZ)){
1784 tcfdbsetecode(fdb, TCEWRITE, __FILE__, __LINE__, __func__);
1785 close(fd);
1786 return false;
1787 }
1788 sbuf.st_size = fdb->fsiz;
1789 }
1790 if(lseek(fd, 0, SEEK_SET) == -1){
1791 tcfdbsetecode(fdb, TCESEEK, __FILE__, __LINE__, __func__);
1792 close(fd);
1793 return false;
1794 }
1795 if(!tcread(fd, hbuf, FDBHEADSIZ)){
1796 tcfdbsetecode(fdb, TCEREAD, __FILE__, __LINE__, __func__);
1797 close(fd);
1798 return false;
1799 }
1800 int type = fdb->type;
1801 tcfdbloadmeta(fdb, hbuf);
1802 if((fdb->flags & FDBFOPEN) && tcfdbwalrestore(fdb, path)){
1803 if(lseek(fd, 0, SEEK_SET) == -1){
1804 tcfdbsetecode(fdb, TCESEEK, __FILE__, __LINE__, __func__);
1805 close(fd);
1806 return false;
1807 }
1808 if(!tcread(fd, hbuf, FDBHEADSIZ)){
1809 tcfdbsetecode(fdb, TCEREAD, __FILE__, __LINE__, __func__);
1810 close(fd);
1811 return false;
1812 }
1813 tcfdbloadmeta(fdb, hbuf);
1814 if(!tcfdbwalremove(fdb, path)){
1815 close(fd);
1816 return false;
1817 }
1818 }
1819 if(!(omode & FDBONOLCK)){
1820 if(memcmp(hbuf, FDBMAGICDATA, strlen(FDBMAGICDATA)) || fdb->type != type ||
1821 fdb->width < 1 || sbuf.st_size < fdb->fsiz || fdb->limsiz < FDBHEADSIZ ||
1822 fdb->fsiz > fdb->limsiz){
1823 tcfdbsetecode(fdb, TCEMETA, __FILE__, __LINE__, __func__);
1824 close(fd);
1825 return false;
1826 }
1827 if(sbuf.st_size > fdb->fsiz) fdb->fsiz = sbuf.st_size;
1828 }
1829 void *map = mmap(0, fdb->limsiz, PROT_READ | ((omode & FDBOWRITER) ? PROT_WRITE : 0),
1830 MAP_SHARED, fd, 0);
1831 if(map == MAP_FAILED){
1832 tcfdbsetecode(fdb, TCEMMAP, __FILE__, __LINE__, __func__);
1833 close(fd);
1834 return false;
1835 }
1836 if(fdb->width <= UINT8_MAX){
1837 fdb->wsiz = sizeof(uint8_t);
1838 } else if(fdb->width <= UINT16_MAX){
1839 fdb->wsiz = sizeof(uint16_t);
1840 } else {
1841 fdb->wsiz = sizeof(uint32_t);
1842 }
1843 fdb->rsiz = fdb->width + fdb->wsiz;
1844 fdb->limid = (fdb->limsiz - FDBHEADSIZ) / fdb->rsiz;
1845 fdb->path = tcstrdup(path);
1846 fdb->fd = fd;
1847 fdb->omode = omode;
1848 fdb->iter = 0;
1849 fdb->map = map;
1850 fdb->array = (unsigned char *)map + FDBHEADSIZ;
1851 fdb->ecode = TCESUCCESS;
1852 fdb->fatal = false;
1853 fdb->inode = (uint64_t)sbuf.st_ino;
1854 fdb->mtime = sbuf.st_mtime;
1855 fdb->tran = false;
1856 fdb->walfd = -1;
1857 fdb->walend = 0;
1858 if(fdb->omode & FDBOWRITER) tcfdbsetflag(fdb, FDBFOPEN, true);
1859 return true;
1860 }
1861
1862
1863 /* Close a fixed-length database object.
1864 `fdb' specifies the fixed-length database object.
1865 If successful, the return value is true, else, it is false. */
tcfdbcloseimpl(TCFDB * fdb)1866 static bool tcfdbcloseimpl(TCFDB *fdb){
1867 assert(fdb);
1868 bool err = false;
1869 if(fdb->omode & FDBOWRITER) tcfdbsetflag(fdb, FDBFOPEN, false);
1870 if((fdb->omode & FDBOWRITER) && !tcfdbmemsync(fdb, false)) err = true;
1871 if(munmap(fdb->map, fdb->limsiz) == -1){
1872 tcfdbsetecode(fdb, TCEMMAP, __FILE__, __LINE__, __func__);
1873 err = true;
1874 }
1875 if(fdb->tran){
1876 if(!tcfdbwalrestore(fdb, fdb->path)) err = true;
1877 fdb->tran = false;
1878 }
1879 if(fdb->walfd >= 0){
1880 if(close(fdb->walfd) == -1){
1881 tcfdbsetecode(fdb, TCECLOSE, __FILE__, __LINE__, __func__);
1882 err = true;
1883 }
1884 if(!fdb->fatal && !tcfdbwalremove(fdb, fdb->path)) err = true;
1885 }
1886 if(close(fdb->fd) == -1){
1887 tcfdbsetecode(fdb, TCECLOSE, __FILE__, __LINE__, __func__);
1888 err = true;
1889 }
1890 TCFREE(fdb->path);
1891 fdb->path = NULL;
1892 fdb->fd = -1;
1893 return !err;
1894 }
1895
1896
1897 /* Get the previous record of a record.
1898 `fdb' specifies the fixed-length database object.
1899 `id' specifies the ID number.
1900 The return value is the ID number of the previous record or 0 if no record corresponds. */
tcfdbprevid(TCFDB * fdb,int64_t id)1901 static int64_t tcfdbprevid(TCFDB *fdb, int64_t id){
1902 assert(fdb && id >= 0);
1903 id--;
1904 while(id >= fdb->min){
1905 TCDODEBUG(fdb->cnt_readrec++);
1906 unsigned char *rec = fdb->array + (id - 1) * (fdb->rsiz);
1907 unsigned char *rp = rec;
1908 uint32_t osiz;
1909 uint16_t snum;
1910 uint32_t lnum;
1911 switch(fdb->wsiz){
1912 case 1:
1913 osiz = *(rp++);
1914 break;
1915 case 2:
1916 memcpy(&snum, rp, sizeof(snum));
1917 osiz = TCITOHS(snum);
1918 rp += sizeof(snum);
1919 break;
1920 default:
1921 memcpy(&lnum, rp, sizeof(lnum));
1922 osiz = TCITOHL(lnum);
1923 rp += sizeof(lnum);
1924 break;
1925 }
1926 if(osiz > 0 || *rp != 0) return id;
1927 id--;
1928 }
1929 return 0;
1930 }
1931
1932
1933 /* Get the next record of a record.
1934 `fdb' specifies the fixed-length database object.
1935 `id' specifies the ID number.
1936 The return value is the ID number of the next record or 0 if no record corresponds. */
tcfdbnextid(TCFDB * fdb,int64_t id)1937 static int64_t tcfdbnextid(TCFDB *fdb, int64_t id){
1938 assert(fdb && id >= 0);
1939 id++;
1940 while(id <= fdb->max){
1941 TCDODEBUG(fdb->cnt_readrec++);
1942 unsigned char *rec = fdb->array + (id - 1) * (fdb->rsiz);
1943 unsigned char *rp = rec;
1944 uint32_t osiz;
1945 uint16_t snum;
1946 uint32_t lnum;
1947 switch(fdb->wsiz){
1948 case 1:
1949 osiz = *(rp++);
1950 break;
1951 case 2:
1952 memcpy(&snum, rp, sizeof(snum));
1953 osiz = TCITOHS(snum);
1954 rp += sizeof(snum);
1955 break;
1956 default:
1957 memcpy(&lnum, rp, sizeof(lnum));
1958 osiz = TCITOHL(lnum);
1959 rp += sizeof(lnum);
1960 break;
1961 }
1962 if(osiz > 0 || *rp != 0) return id;
1963 id++;
1964 }
1965 return 0;
1966 }
1967
1968
1969 /* Store a record.
1970 `fdb' specifies the fixed-length database object.
1971 `id' specifies the ID number.
1972 `vbuf' specifies the pointer to the region of the value.
1973 `vsiz' specifies the size of the region of the value.
1974 `dmode' specifies behavior when the key overlaps.
1975 If successful, the return value is true, else, it is false. */
tcfdbputimpl(TCFDB * fdb,int64_t id,const void * vbuf,int vsiz,int dmode)1976 static bool tcfdbputimpl(TCFDB *fdb, int64_t id, const void *vbuf, int vsiz, int dmode){
1977 assert(fdb && id > 0);
1978 if(vsiz > (int64_t)fdb->width) vsiz = fdb->width;
1979 TCDODEBUG(fdb->cnt_readrec++);
1980 unsigned char *rec = fdb->array + (id - 1) * (fdb->rsiz);
1981 uint64_t nsiz = FDBHEADSIZ + id * fdb->rsiz;
1982 if(nsiz > fdb->fsiz){
1983 if(nsiz > fdb->limsiz){
1984 tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__);
1985 return false;
1986 }
1987 if(!FDBLOCKATTR(fdb)) return false;
1988 if(nsiz > fdb->fsiz){
1989 if(vsiz < 0){
1990 tcfdbsetecode(fdb, TCENOREC, __FILE__, __LINE__, __func__);
1991 FDBUNLOCKATTR(fdb);
1992 return false;
1993 }
1994 if(nsiz + fdb->rsiz * FDBTRUNCALW < fdb->limsiz) nsiz += fdb->rsiz * FDBTRUNCALW;
1995 if(ftruncate(fdb->fd, nsiz) == -1){
1996 tcfdbsetecode(fdb, TCETRUNC, __FILE__, __LINE__, __func__);
1997 FDBUNLOCKATTR(fdb);
1998 return false;
1999 }
2000 TCDODEBUG(fdb->cnt_truncfile++);
2001 fdb->fsiz = nsiz;
2002 unsigned char *wp = rec;
2003 uint16_t snum;
2004 uint32_t lnum;
2005 switch(fdb->wsiz){
2006 case 1:
2007 *(wp++) = vsiz;
2008 break;
2009 case 2:
2010 snum = TCHTOIS(vsiz);
2011 memcpy(wp, &snum, sizeof(snum));
2012 wp += sizeof(snum);
2013 break;
2014 default:
2015 lnum = TCHTOIL(vsiz);
2016 memcpy(wp, &lnum, sizeof(lnum));
2017 wp += sizeof(lnum);
2018 break;
2019 }
2020 if(vsiz > 0){
2021 memcpy(wp, vbuf, vsiz);
2022 } else {
2023 *wp = 1;
2024 }
2025 TCDODEBUG(fdb->cnt_writerec++);
2026 fdb->rnum++;
2027 if(fdb->min < 1 || id < fdb->min) fdb->min = id;
2028 if(fdb->max < 1 || id > fdb->max) fdb->max = id;
2029 FDBUNLOCKATTR(fdb);
2030 return true;
2031 }
2032 FDBUNLOCKATTR(fdb);
2033 }
2034 unsigned char *rp = rec;
2035 uint32_t osiz;
2036 uint16_t snum;
2037 uint32_t lnum;
2038 switch(fdb->wsiz){
2039 case 1:
2040 osiz = *(rp++);
2041 break;
2042 case 2:
2043 memcpy(&snum, rp, sizeof(snum));
2044 osiz = TCITOHS(snum);
2045 rp += sizeof(snum);
2046 break;
2047 default:
2048 memcpy(&lnum, rp, sizeof(lnum));
2049 osiz = TCITOHL(lnum);
2050 rp += sizeof(lnum);
2051 break;
2052 }
2053 bool miss = osiz == 0 && *rp == 0;
2054 if(dmode != FDBPDOVER && !miss){
2055 if(dmode == FDBPDKEEP){
2056 tcfdbsetecode(fdb, TCEKEEP, __FILE__, __LINE__, __func__);
2057 return false;
2058 }
2059 if(dmode == FDBPDCAT){
2060 if(fdb->tran && !tcfdbwalwrite(fdb, (char *)rec - fdb->map, fdb->width)) return false;
2061 vsiz = tclmin(vsiz, fdb->width - osiz);
2062 unsigned char *wp = rec;
2063 int usiz = osiz + vsiz;
2064 switch(fdb->wsiz){
2065 case 1:
2066 *(wp++) = usiz;
2067 break;
2068 case 2:
2069 snum = TCHTOIS(usiz);
2070 memcpy(wp, &snum, sizeof(snum));
2071 wp += sizeof(snum);
2072 break;
2073 default:
2074 lnum = TCHTOIL(usiz);
2075 memcpy(wp, &lnum, sizeof(lnum));
2076 wp += sizeof(lnum);
2077 break;
2078 }
2079 if(usiz > 0){
2080 memcpy(wp + osiz, vbuf, vsiz);
2081 } else {
2082 *wp = 1;
2083 }
2084 TCDODEBUG(fdb->cnt_writerec++);
2085 return true;
2086 }
2087 if(dmode == FDBPDADDINT){
2088 if(osiz != sizeof(int)){
2089 tcfdbsetecode(fdb, TCEKEEP, __FILE__, __LINE__, __func__);
2090 return false;
2091 }
2092 int lnum;
2093 memcpy(&lnum, rp, sizeof(lnum));
2094 if(*(int *)vbuf == 0){
2095 *(int *)vbuf = lnum;
2096 return true;
2097 }
2098 if(fdb->tran && !tcfdbwalwrite(fdb, (char *)rec - fdb->map, fdb->width)) return false;
2099 lnum += *(int *)vbuf;
2100 *(int *)vbuf = lnum;
2101 memcpy(rp, &lnum, sizeof(lnum));
2102 TCDODEBUG(fdb->cnt_writerec++);
2103 return true;
2104 }
2105 if(dmode == FDBPDADDDBL){
2106 if(osiz != sizeof(double)){
2107 tcfdbsetecode(fdb, TCEKEEP, __FILE__, __LINE__, __func__);
2108 return false;
2109 }
2110 double dnum;
2111 memcpy(&dnum, rp, sizeof(dnum));
2112 if(*(double *)vbuf == 0.0){
2113 *(double *)vbuf = dnum;
2114 return true;
2115 }
2116 if(fdb->tran && !tcfdbwalwrite(fdb, (char *)rec - fdb->map, fdb->width)) return false;
2117 dnum += *(double *)vbuf;
2118 *(double *)vbuf = dnum;
2119 memcpy(rp, &dnum, sizeof(dnum));
2120 TCDODEBUG(fdb->cnt_writerec++);
2121 return true;
2122 }
2123 if(dmode == FDBPDPROC){
2124 FDBPDPROCOP *procptr = *(FDBPDPROCOP **)((char *)vbuf - sizeof(procptr));
2125 int nvsiz;
2126 char *nvbuf = procptr->proc(rp, osiz, &nvsiz, procptr->op);
2127 if(nvbuf == (void *)-1){
2128 if(fdb->tran && !tcfdbwalwrite(fdb, (char *)rec - fdb->map, fdb->width)) return false;
2129 memset(rec, 0, fdb->wsiz + 1);
2130 TCDODEBUG(fdb->cnt_writerec++);
2131 if(!FDBLOCKATTR(fdb)) return false;
2132 fdb->rnum--;
2133 if(fdb->rnum < 1){
2134 fdb->min = 0;
2135 fdb->max = 0;
2136 } else if(fdb->rnum < 2){
2137 if(fdb->min == id){
2138 fdb->min = fdb->max;
2139 } else if(fdb->max == id){
2140 fdb->max = fdb->min;
2141 }
2142 } else {
2143 if(id == fdb->min) fdb->min = tcfdbnextid(fdb, id);
2144 if(id == fdb->max) fdb->max = tcfdbprevid(fdb, id);
2145 }
2146 FDBUNLOCKATTR(fdb);
2147 return true;
2148 }
2149 if(!nvbuf){
2150 tcfdbsetecode(fdb, TCEKEEP, __FILE__, __LINE__, __func__);
2151 return false;
2152 }
2153 if(fdb->tran && !tcfdbwalwrite(fdb, (char *)rec - fdb->map, fdb->width)) return false;
2154 if(nvsiz > fdb->width) nvsiz = fdb->width;
2155 unsigned char *wp = rec;
2156 switch(fdb->wsiz){
2157 case 1:
2158 *(wp++) = nvsiz;
2159 break;
2160 case 2:
2161 snum = TCHTOIS(nvsiz);
2162 memcpy(wp, &snum, sizeof(snum));
2163 wp += sizeof(snum);
2164 break;
2165 default:
2166 lnum = TCHTOIL(nvsiz);
2167 memcpy(wp, &lnum, sizeof(lnum));
2168 wp += sizeof(lnum);
2169 break;
2170 }
2171 if(nvsiz > 0){
2172 memcpy(wp, nvbuf, nvsiz);
2173 } else {
2174 *wp = 1;
2175 }
2176 TCFREE(nvbuf);
2177 TCDODEBUG(fdb->cnt_writerec++);
2178 return true;
2179 }
2180 }
2181 if(vsiz < 0){
2182 tcfdbsetecode(fdb, TCENOREC, __FILE__, __LINE__, __func__);
2183 return false;
2184 }
2185 if(fdb->tran && !tcfdbwalwrite(fdb, (char *)rec - fdb->map, fdb->width)) return false;
2186 unsigned char *wp = rec;
2187 switch(fdb->wsiz){
2188 case 1:
2189 *(wp++) = vsiz;
2190 break;
2191 case 2:
2192 snum = TCHTOIS(vsiz);
2193 memcpy(wp, &snum, sizeof(snum));
2194 wp += sizeof(snum);
2195 break;
2196 default:
2197 lnum = TCHTOIL(vsiz);
2198 memcpy(wp, &lnum, sizeof(lnum));
2199 wp += sizeof(lnum);
2200 break;
2201 }
2202 if(vsiz > 0){
2203 memcpy(wp, vbuf, vsiz);
2204 } else {
2205 *wp = 1;
2206 }
2207 TCDODEBUG(fdb->cnt_writerec++);
2208 if(miss){
2209 if(!FDBLOCKATTR(fdb)) return false;
2210 fdb->rnum++;
2211 if(fdb->min < 1 || id < fdb->min) fdb->min = id;
2212 if(fdb->max < 1 || id > fdb->max) fdb->max = id;
2213 FDBUNLOCKATTR(fdb);
2214 }
2215 return true;
2216 }
2217
2218
2219 /* Remove a record of a fixed-length database object.
2220 `fdb' specifies the fixed-length database object.
2221 `id' specifies the ID number.
2222 If successful, the return value is true, else, it is false. */
tcfdboutimpl(TCFDB * fdb,int64_t id)2223 static bool tcfdboutimpl(TCFDB *fdb, int64_t id){
2224 assert(fdb && id >= 0);
2225 TCDODEBUG(fdb->cnt_readrec++);
2226 unsigned char *rec = fdb->array + (id - 1) * (fdb->rsiz);
2227 uint64_t nsiz = FDBHEADSIZ + id * fdb->rsiz;
2228 if(nsiz > fdb->fsiz){
2229 tcfdbsetecode(fdb, TCENOREC, __FILE__, __LINE__, __func__);
2230 return false;
2231 }
2232 unsigned char *rp = rec;
2233 uint32_t osiz;
2234 uint16_t snum;
2235 uint32_t lnum;
2236 switch(fdb->wsiz){
2237 case 1:
2238 osiz = *(rp++);
2239 break;
2240 case 2:
2241 memcpy(&snum, rp, sizeof(snum));
2242 osiz = TCITOHS(snum);
2243 rp += sizeof(snum);
2244 break;
2245 default:
2246 memcpy(&lnum, rp, sizeof(lnum));
2247 osiz = TCITOHL(lnum);
2248 rp += sizeof(lnum);
2249 break;
2250 }
2251 if(osiz == 0 && *rp == 0){
2252 tcfdbsetecode(fdb, TCENOREC, __FILE__, __LINE__, __func__);
2253 return false;
2254 }
2255 if(fdb->tran && !tcfdbwalwrite(fdb, (char *)rec - fdb->map, fdb->width)) return false;
2256 memset(rec, 0, fdb->wsiz + 1);
2257 TCDODEBUG(fdb->cnt_writerec++);
2258 if(!FDBLOCKATTR(fdb)) return false;
2259 fdb->rnum--;
2260 if(fdb->rnum < 1){
2261 fdb->min = 0;
2262 fdb->max = 0;
2263 } else if(fdb->rnum < 2){
2264 if(fdb->min == id){
2265 fdb->min = fdb->max;
2266 } else if(fdb->max == id){
2267 fdb->max = fdb->min;
2268 }
2269 } else {
2270 if(id == fdb->min) fdb->min = tcfdbnextid(fdb, id);
2271 if(id == fdb->max) fdb->max = tcfdbprevid(fdb, id);
2272 }
2273 FDBUNLOCKATTR(fdb);
2274 return true;
2275 }
2276
2277
2278 /* Retrieve a record.
2279 `fdb' specifies the fixed-length database object.
2280 `id' specifies the ID number.
2281 `sp' specifies the pointer to the variable into which the size of the region of the return
2282 value is assigned.
2283 If successful, the return value is the pointer to the region of the value of the corresponding
2284 record. */
tcfdbgetimpl(TCFDB * fdb,int64_t id,int * sp)2285 static const void *tcfdbgetimpl(TCFDB *fdb, int64_t id, int *sp){
2286 assert(fdb && id >= 0 && sp);
2287 TCDODEBUG(fdb->cnt_readrec++);
2288 unsigned char *rec = fdb->array + (id - 1) * (fdb->rsiz);
2289 uint64_t nsiz = FDBHEADSIZ + id * fdb->rsiz;
2290 if(nsiz > fdb->fsiz){
2291 tcfdbsetecode(fdb, TCENOREC, __FILE__, __LINE__, __func__);
2292 return false;
2293 }
2294 unsigned char *rp = rec;
2295 uint32_t osiz;
2296 uint16_t snum;
2297 uint32_t lnum;
2298 switch(fdb->wsiz){
2299 case 1:
2300 osiz = *(rp++);
2301 break;
2302 case 2:
2303 memcpy(&snum, rp, sizeof(snum));
2304 osiz = TCITOHS(snum);
2305 rp += sizeof(snum);
2306 break;
2307 default:
2308 memcpy(&lnum, rp, sizeof(lnum));
2309 osiz = TCITOHL(lnum);
2310 rp += sizeof(lnum);
2311 break;
2312 }
2313 if(osiz == 0 && *rp == 0){
2314 tcfdbsetecode(fdb, TCENOREC, __FILE__, __LINE__, __func__);
2315 return false;
2316 }
2317 *sp = osiz;
2318 return rp;
2319 }
2320
2321
2322 /* Initialize the iterator of a fixed-length database object.
2323 `fdb' specifies the fixed-length database object.
2324 If successful, the return value is true, else, it is false. */
tcfdbiterinitimpl(TCFDB * fdb)2325 static bool tcfdbiterinitimpl(TCFDB *fdb){
2326 assert(fdb);
2327 fdb->iter = fdb->min;
2328 return true;
2329 }
2330
2331
2332 /* Get the next key of the iterator of a fixed-length database object.
2333 `fdb' specifies the fixed-length database object.
2334 If successful, the return value is the next ID number of the iterator, else, it is 0. */
tcfdbiternextimpl(TCFDB * fdb)2335 static uint64_t tcfdbiternextimpl(TCFDB *fdb){
2336 assert(fdb);
2337 if(fdb->iter < 1){
2338 tcfdbsetecode(fdb, TCENOREC, __FILE__, __LINE__, __func__);
2339 return 0;
2340 }
2341 uint64_t cur = fdb->iter;
2342 fdb->iter = tcfdbnextid(fdb, fdb->iter);
2343 return cur;
2344 }
2345
2346
2347 /* Get range matching ID numbers in a fixed-length database object.
2348 `fdb' specifies the fixed-length database object.
2349 `lower' specifies the lower limit of the range.
2350 `upper' specifies the upper limit of the range.
2351 `max' specifies the maximum number of keys to be fetched.
2352 `np' specifies the pointer to the variable into which the number of elements of the return
2353 value is assigned.
2354 If successful, the return value is the pointer to an array of ID numbers of the corresponding
2355 records. */
tcfdbrangeimpl(TCFDB * fdb,int64_t lower,int64_t upper,int max,int * np)2356 static uint64_t *tcfdbrangeimpl(TCFDB *fdb, int64_t lower, int64_t upper, int max, int *np){
2357 assert(fdb && lower > 0 && upper > 0 && np);
2358 if(lower < fdb->min) lower = fdb->min;
2359 if(upper > fdb->max) upper = fdb->max;
2360 if(max < 0) max = INT_MAX;
2361 int anum = FDBIDARYUNIT;
2362 uint64_t *ids;
2363 TCMALLOC(ids, anum * sizeof(*ids));
2364 int num = 0;
2365 for(int64_t i = lower; i <= upper && num < max; i++){
2366 int vsiz;
2367 const void *vbuf = tcfdbgetimpl(fdb, i, &vsiz);
2368 if(vbuf){
2369 if(num >= anum){
2370 anum *= 2;
2371 TCREALLOC(ids, ids, anum * sizeof(*ids));
2372 }
2373 ids[num++] = i;
2374 }
2375 }
2376 *np = num;
2377 return ids;
2378 }
2379
2380
2381 /* Optimize the file of a fixed-length database object.
2382 `fdb' specifies the fixed-length database object.
2383 `width' specifies the width of the value of each record.
2384 `limsiz' specifies the limit size of the database file.
2385 If successful, the return value is true, else, it is false. */
tcfdboptimizeimpl(TCFDB * fdb,int32_t width,int64_t limsiz)2386 static bool tcfdboptimizeimpl(TCFDB *fdb, int32_t width, int64_t limsiz){
2387 assert(fdb);
2388 char *tpath = tcsprintf("%s%ctmp%c%llu", fdb->path, MYEXTCHR, MYEXTCHR, fdb->inode);
2389 TCFDB *tfdb = tcfdbnew();
2390 tfdb->dbgfd = fdb->dbgfd;
2391 if(width < 1) width = fdb->width;
2392 if(limsiz < 1) limsiz = fdb->limsiz;
2393 tcfdbtune(tfdb, width, limsiz);
2394 if(!tcfdbopen(tfdb, tpath, FDBOWRITER | FDBOCREAT | FDBOTRUNC)){
2395 tcfdbsetecode(fdb, tfdb->ecode, __FILE__, __LINE__, __func__);
2396 tcfdbdel(tfdb);
2397 TCFREE(tpath);
2398 return false;
2399 }
2400 bool err = false;
2401 int64_t max = fdb->max;
2402 for(int i = fdb->min; !err && i <= max; i++){
2403 int vsiz;
2404 const void *vbuf = tcfdbgetimpl(fdb, i, &vsiz);
2405 if(vbuf && !tcfdbput(tfdb, i, vbuf, vsiz)){
2406 tcfdbsetecode(fdb, tfdb->ecode, __FILE__, __LINE__, __func__);
2407 err = true;
2408 }
2409 }
2410 if(!tcfdbclose(tfdb)){
2411 tcfdbsetecode(fdb, tfdb->ecode, __FILE__, __LINE__, __func__);
2412 err = true;
2413 }
2414 tcfdbdel(tfdb);
2415 if(unlink(fdb->path) == -1){
2416 tcfdbsetecode(fdb, TCEUNLINK, __FILE__, __LINE__, __func__);
2417 err = true;
2418 }
2419 if(rename(tpath, fdb->path) == -1){
2420 tcfdbsetecode(fdb, TCERENAME, __FILE__, __LINE__, __func__);
2421 err = true;
2422 }
2423 TCFREE(tpath);
2424 if(err) return false;
2425 tpath = tcstrdup(fdb->path);
2426 int omode = (fdb->omode & ~FDBOCREAT) & ~FDBOTRUNC;
2427 if(!tcfdbcloseimpl(fdb)){
2428 TCFREE(tpath);
2429 return false;
2430 }
2431 bool rv = tcfdbopenimpl(fdb, tpath, omode);
2432 TCFREE(tpath);
2433 return rv;
2434 }
2435
2436
2437 /* Remove all records of a fixed-length database object.
2438 `fdb' specifies the fixed-length database object.
2439 If successful, the return value is true, else, it is false. */
tcfdbvanishimpl(TCFDB * fdb)2440 static bool tcfdbvanishimpl(TCFDB *fdb){
2441 assert(fdb);
2442 char *path = tcstrdup(fdb->path);
2443 int omode = fdb->omode;
2444 bool err = false;
2445 if(!tcfdbcloseimpl(fdb)) err = true;
2446 if(!tcfdbopenimpl(fdb, path, FDBOTRUNC | omode)){
2447 tcpathunlock(fdb->rpath);
2448 TCFREE(fdb->rpath);
2449 err = true;
2450 }
2451 TCFREE(path);
2452 return !err;
2453 }
2454
2455
2456 /* Copy the database file of a fixed-length database object.
2457 `fdb' specifies the fixed-length database object.
2458 `path' specifies the path of the destination file.
2459 If successful, the return value is true, else, it is false. */
tcfdbcopyimpl(TCFDB * fdb,const char * path)2460 static bool tcfdbcopyimpl(TCFDB *fdb, const char *path){
2461 assert(fdb && path);
2462 bool err = false;
2463 if(fdb->omode & FDBOWRITER){
2464 if(!tcfdbmemsync(fdb, false)) err = true;
2465 tcfdbsetflag(fdb, FDBFOPEN, false);
2466 }
2467 if(*path == '@'){
2468 char tsbuf[TCNUMBUFSIZ];
2469 sprintf(tsbuf, "%llu", (unsigned long long)(tctime() * 1000000));
2470 const char *args[3];
2471 args[0] = path + 1;
2472 args[1] = fdb->path;
2473 args[2] = tsbuf;
2474 if(tcsystem(args, sizeof(args) / sizeof(*args)) != 0) err = true;
2475 } else {
2476 if(!tccopyfile(fdb->path, path)){
2477 tcfdbsetecode(fdb, TCEMISC, __FILE__, __LINE__, __func__);
2478 err = true;
2479 }
2480 }
2481 if(fdb->omode & FDBOWRITER) tcfdbsetflag(fdb, FDBFOPEN, true);
2482 return !err;
2483 }
2484
2485
2486 /* Move the iterator to the record corresponding a key of a fixed-length database object.
2487 `fdb' specifies the fixed-length database object.
2488 `id' specifies the ID number.
2489 If successful, the return value is true, else, it is false. */
tcfdbiterjumpimpl(TCFDB * fdb,int64_t id)2490 static bool tcfdbiterjumpimpl(TCFDB *fdb, int64_t id){
2491 assert(fdb && id >= 0);
2492 if(id <= fdb->min){
2493 fdb->iter = fdb->min;
2494 } else {
2495 int vsiz;
2496 if(tcfdbgetimpl(fdb, id, &vsiz)){
2497 fdb->iter = id;
2498 } else {
2499 uint64_t iter = tcfdbnextid(fdb, id);
2500 if(iter > 0){
2501 fdb->iter = iter;
2502 } else {
2503 return false;
2504 }
2505 }
2506 }
2507 return true;
2508 }
2509
2510
2511 /* Process each record atomically of a fixed-length database object.
2512 `fdb' specifies the fixed-length database object.
2513 `iter' specifies the pointer to the iterator function called for each record.
2514 `op' specifies an arbitrary pointer to be given as a parameter of the iterator function.
2515 If successful, the return value is true, else, it is false. */
tcfdbforeachimpl(TCFDB * fdb,TCITER iter,void * op)2516 static bool tcfdbforeachimpl(TCFDB *fdb, TCITER iter, void *op){
2517 bool err = false;
2518 uint64_t id = fdb->min;
2519 while(id > 0){
2520 int vsiz;
2521 const void *vbuf = tcfdbgetimpl(fdb, id, &vsiz);
2522 if(vbuf){
2523 char kbuf[TCNUMBUFSIZ];
2524 int ksiz = sprintf(kbuf, "%llu", (unsigned long long)id);
2525 if(!iter(kbuf, ksiz, vbuf, vsiz, op)) break;
2526 } else {
2527 tcfdbsetecode(fdb, TCEMISC, __FILE__, __LINE__, __func__);
2528 err = true;
2529 }
2530 id = tcfdbnextid(fdb, id);
2531 }
2532 return !err;
2533 }
2534
2535
2536 /* Lock a method of the fixed-length database object.
2537 `fdb' specifies the fixed-length database object.
2538 `wr' specifies whether the lock is writer or not.
2539 If successful, the return value is true, else, it is false. */
tcfdblockmethod(TCFDB * fdb,bool wr)2540 static bool tcfdblockmethod(TCFDB *fdb, bool wr){
2541 assert(fdb);
2542 if(wr ? pthread_rwlock_wrlock(fdb->mmtx) != 0 : pthread_rwlock_rdlock(fdb->mmtx) != 0){
2543 tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__);
2544 return false;
2545 }
2546 TCTESTYIELD();
2547 return true;
2548 }
2549
2550
2551 /* Unlock a method of the fixed-length database object.
2552 `fdb' specifies the fixed-length database object.
2553 If successful, the return value is true, else, it is false. */
tcfdbunlockmethod(TCFDB * fdb)2554 static bool tcfdbunlockmethod(TCFDB *fdb){
2555 assert(fdb);
2556 if(pthread_rwlock_unlock(fdb->mmtx) != 0){
2557 tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__);
2558 return false;
2559 }
2560 TCTESTYIELD();
2561 return true;
2562 }
2563
2564
2565 /* Lock the attributes of the fixed-length database object.
2566 `fdb' specifies the fixed-length database object.
2567 If successful, the return value is true, else, it is false. */
tcfdblockattr(TCFDB * fdb)2568 static bool tcfdblockattr(TCFDB *fdb){
2569 assert(fdb);
2570 if(pthread_mutex_lock(fdb->amtx) != 0){
2571 tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__);
2572 return false;
2573 }
2574 TCTESTYIELD();
2575 return true;
2576 }
2577
2578
2579 /* Unlock the attributes of the fixed-length database object.
2580 `fdb' specifies the fixed-length database object.
2581 If successful, the return value is true, else, it is false. */
tcfdbunlockattr(TCFDB * fdb)2582 static bool tcfdbunlockattr(TCFDB *fdb){
2583 assert(fdb);
2584 if(pthread_mutex_unlock(fdb->amtx) != 0){
2585 tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__);
2586 return false;
2587 }
2588 TCTESTYIELD();
2589 return true;
2590 }
2591
2592
2593 /* Lock a record of the fixed-length database object.
2594 `fdb' specifies the fixed-length database object.
2595 `wr' specifies whether the lock is writer or not.
2596 If successful, the return value is true, else, it is false. */
tcfdblockrecord(TCFDB * fdb,bool wr,uint64_t id)2597 static bool tcfdblockrecord(TCFDB *fdb, bool wr, uint64_t id){
2598 assert(fdb && id > 0);
2599 if(wr ? pthread_rwlock_wrlock((pthread_rwlock_t *)fdb->rmtxs + id % FDBRMTXNUM) != 0 :
2600 pthread_rwlock_rdlock((pthread_rwlock_t *)fdb->rmtxs + id % FDBRMTXNUM) != 0){
2601 tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__);
2602 return false;
2603 }
2604 TCTESTYIELD();
2605 return true;
2606 }
2607
2608
2609 /* Unlock a record of the fixed-length database object.
2610 `fdb' specifies the fixed-length database object.
2611 If successful, the return value is true, else, it is false. */
tcfdbunlockrecord(TCFDB * fdb,uint64_t id)2612 static bool tcfdbunlockrecord(TCFDB *fdb, uint64_t id){
2613 assert(fdb);
2614 if(pthread_rwlock_unlock((pthread_rwlock_t *)fdb->rmtxs + id % FDBRMTXNUM) != 0){
2615 tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__);
2616 return false;
2617 }
2618 TCTESTYIELD();
2619 return true;
2620 }
2621
2622
2623 /* Lock all records of the fixed-length database object.
2624 `fdb' specifies the fixed-length database object.
2625 `wr' specifies whether the lock is writer or not.
2626 If successful, the return value is true, else, it is false. */
tcfdblockallrecords(TCFDB * fdb,bool wr)2627 static bool tcfdblockallrecords(TCFDB *fdb, bool wr){
2628 assert(fdb);
2629 for(int i = 0; i < FDBRMTXNUM; i++){
2630 if(wr ? pthread_rwlock_wrlock((pthread_rwlock_t *)fdb->rmtxs + i) != 0 :
2631 pthread_rwlock_rdlock((pthread_rwlock_t *)fdb->rmtxs + i) != 0){
2632 tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__);
2633 while(--i >= 0){
2634 pthread_rwlock_unlock((pthread_rwlock_t *)fdb->rmtxs + i);
2635 }
2636 return false;
2637 }
2638 }
2639 TCTESTYIELD();
2640 return true;
2641 }
2642
2643
2644 /* Unlock all records of the fixed-length database object.
2645 `fdb' specifies the fixed-length database object.
2646 If successful, the return value is true, else, it is false. */
tcfdbunlockallrecords(TCFDB * fdb)2647 static bool tcfdbunlockallrecords(TCFDB *fdb){
2648 assert(fdb);
2649 bool err = false;
2650 for(int i = FDBRMTXNUM - 1; i >= 0; i--){
2651 if(pthread_rwlock_unlock((pthread_rwlock_t *)fdb->rmtxs + i)) err = true;
2652 }
2653 TCTESTYIELD();
2654 if(err){
2655 tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__);
2656 return false;
2657 }
2658 return true;
2659 }
2660
2661
2662 /* Lock the write ahead logging file of the fixed-length database object.
2663 `fdb' specifies the fixed-length database object.
2664 If successful, the return value is true, else, it is false. */
tcfdblockwal(TCFDB * fdb)2665 static bool tcfdblockwal(TCFDB *fdb){
2666 assert(fdb);
2667 if(pthread_mutex_lock(fdb->wmtx) != 0){
2668 tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__);
2669 return false;
2670 }
2671 TCTESTYIELD();
2672 return true;
2673 }
2674
2675
2676 /* Unlock the write ahead logging file of the fixed-length database object.
2677 `fdb' specifies the fixed-length database object.
2678 If successful, the return value is true, else, it is false. */
tcfdbunlockwal(TCFDB * fdb)2679 static bool tcfdbunlockwal(TCFDB *fdb){
2680 assert(fdb);
2681 if(pthread_mutex_unlock(fdb->wmtx) != 0){
2682 tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__);
2683 return false;
2684 }
2685 TCTESTYIELD();
2686 return true;
2687 }
2688
2689
2690
2691 /*************************************************************************************************
2692 * debugging functions
2693 *************************************************************************************************/
2694
2695
2696 /* Print meta data of the header into the debugging output.
2697 `fdb' specifies the fixed-length database object. */
tcfdbprintmeta(TCFDB * fdb)2698 void tcfdbprintmeta(TCFDB *fdb){
2699 assert(fdb);
2700 if(fdb->dbgfd < 0) return;
2701 int dbgfd = (fdb->dbgfd == UINT16_MAX) ? 1 : fdb->dbgfd;
2702 char buf[FDBIOBUFSIZ];
2703 char *wp = buf;
2704 wp += sprintf(wp, "META:");
2705 wp += sprintf(wp, " mmtx=%p", (void *)fdb->mmtx);
2706 wp += sprintf(wp, " amtx=%p", (void *)fdb->amtx);
2707 wp += sprintf(wp, " rmtxs=%p", (void *)fdb->rmtxs);
2708 wp += sprintf(wp, " tmtx=%p", (void *)fdb->tmtx);
2709 wp += sprintf(wp, " wmtx=%p", (void *)fdb->wmtx);
2710 wp += sprintf(wp, " eckey=%p", (void *)fdb->eckey);
2711 wp += sprintf(wp, " rpath=%s", fdb->rpath ? fdb->rpath : "-");
2712 wp += sprintf(wp, " type=%02X", fdb->type);
2713 wp += sprintf(wp, " flags=%02X", fdb->flags);
2714 wp += sprintf(wp, " width=%u", fdb->width);
2715 wp += sprintf(wp, " limsiz=%llu", (unsigned long long)fdb->limsiz);
2716 wp += sprintf(wp, " wsiz=%u", fdb->wsiz);
2717 wp += sprintf(wp, " rsiz=%u", fdb->rsiz);
2718 wp += sprintf(wp, " limid=%llu", (unsigned long long)fdb->limid);
2719 wp += sprintf(wp, " path=%s", fdb->path ? fdb->path : "-");
2720 wp += sprintf(wp, " fd=%d", fdb->fd);
2721 wp += sprintf(wp, " omode=%u", fdb->omode);
2722 wp += sprintf(wp, " rnum=%llu", (unsigned long long)fdb->rnum);
2723 wp += sprintf(wp, " fsiz=%llu", (unsigned long long)fdb->fsiz);
2724 wp += sprintf(wp, " min=%llu", (unsigned long long)fdb->min);
2725 wp += sprintf(wp, " max=%llu", (unsigned long long)fdb->max);
2726 wp += sprintf(wp, " iter=%llu", (unsigned long long)fdb->iter);
2727 wp += sprintf(wp, " map=%p", (void *)fdb->map);
2728 wp += sprintf(wp, " array=%p", (void *)fdb->array);
2729 wp += sprintf(wp, " ecode=%d", fdb->ecode);
2730 wp += sprintf(wp, " fatal=%u", fdb->fatal);
2731 wp += sprintf(wp, " inode=%llu", (unsigned long long)fdb->inode);
2732 wp += sprintf(wp, " mtime=%llu", (unsigned long long)fdb->mtime);
2733 wp += sprintf(wp, " tran=%d", fdb->tran);
2734 wp += sprintf(wp, " walfd=%d", fdb->walfd);
2735 wp += sprintf(wp, " walend=%llu", (unsigned long long)fdb->walend);
2736 wp += sprintf(wp, " dbgfd=%d", fdb->dbgfd);
2737 wp += sprintf(wp, " cnt_writerec=%lld", (long long)fdb->cnt_writerec);
2738 wp += sprintf(wp, " cnt_readrec=%lld", (long long)fdb->cnt_readrec);
2739 wp += sprintf(wp, " cnt_truncfile=%lld", (long long)fdb->cnt_truncfile);
2740 *(wp++) = '\n';
2741 tcwrite(dbgfd, buf, wp - buf);
2742 }
2743
2744
2745
2746 // END OF FILE
2747