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