📄 tcfdb.c
字号:
/* Get the inode number of the database file of a fixed-length database object. */uint64_t tcfdbinode(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return fdb->inode;}/* Get the modification time of the database file of a fixed-length database object. */time_t tcfdbmtime(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return fdb->mtime;}/* Get the connection mode of a fixed-length database object. */int tcfdbomode(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return fdb->omode;}/* Get the database type of a fixed-length database object. */uint8_t tcfdbtype(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return fdb->type;}/* Get the additional flags of a fixed-length database object. */uint8_t tcfdbflags(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return 0; } return fdb->flags;}/* Get the pointer to the opaque field of a fixed-length database object. */char *tcfdbopaque(TCFDB *fdb){ assert(fdb); if(fdb->fd < 0){ tcfdbsetecode(fdb, TCEINVALID, __FILE__, __LINE__, __func__); return NULL; } return fdb->map + FDBOPAQUEOFF;}/* Generate the ID number from arbitrary binary data. `kbuf' specifies the pointer to the region of the key. `ksiz' specifies the size of the region of the key. The return value is the ID number. */int64_t tcfdbkeytoid(const char *kbuf, int ksiz){ assert(kbuf && ksiz >= 0); if(ksiz == 3 && !memcmp(kbuf, "min", 3)){ return FDBIDMIN; } else if(ksiz == 4 && !memcmp(kbuf, "prev", 4)){ return FDBIDPREV; } else if(ksiz == 3 && !memcmp(kbuf, "max", 3)){ return FDBIDMAX; } else if(ksiz == 4 && !memcmp(kbuf, "next", 4)){ return FDBIDNEXT; } int64_t id = 0; const char *end = kbuf + ksiz; while(kbuf < end){ int c = *(unsigned char *)(kbuf++); if(c >= '0' && c <= '9') id = id * 10 + c - '0'; } return id;}/************************************************************************************************* * private features *************************************************************************************************//* Serialize meta data into a buffer. `fdb' specifies the fixed-length database object. `hbuf' specifies the buffer. */static void tcdumpmeta(TCFDB *fdb, char *hbuf){ memset(hbuf, 0, FDBHEADSIZ); sprintf(hbuf, "%s\n%s:%d\n", FDBMAGICDATA, _TC_FORMATVER, _TC_LIBVER); memcpy(hbuf + FDBTYPEOFF, &(fdb->type), sizeof(fdb->type)); memcpy(hbuf + FDBFLAGSOFF, &(fdb->flags), sizeof(fdb->flags)); uint64_t llnum; llnum = fdb->rnum; llnum = TCHTOILL(llnum); memcpy(hbuf + FDBRNUMOFF, &llnum, sizeof(llnum)); llnum = fdb->fsiz; llnum = TCHTOILL(llnum); memcpy(hbuf + FDBFSIZOFF, &llnum, sizeof(llnum)); llnum = fdb->width; llnum = TCHTOILL(llnum); memcpy(hbuf + FDBWIDTHOFF, &llnum, sizeof(llnum)); llnum = fdb->limsiz; llnum = TCHTOILL(llnum); memcpy(hbuf + FDBLIMSIZOFF, &llnum, sizeof(llnum)); llnum = fdb->min; llnum = TCHTOILL(llnum); memcpy(hbuf + FDBMINOFF, &llnum, sizeof(llnum)); llnum = fdb->max; llnum = TCHTOILL(llnum); memcpy(hbuf + FDBMAXOFF, &llnum, sizeof(llnum));}/* Deserialize meta data from a buffer. `fdb' specifies the fixed-length database object. `hbuf' specifies the buffer. */static void tcloadmeta(TCFDB *fdb, const char *hbuf){ memcpy(&(fdb->type), hbuf + FDBTYPEOFF, sizeof(fdb->type)); memcpy(&(fdb->flags), hbuf + FDBFLAGSOFF, sizeof(fdb->flags)); uint64_t llnum; memcpy(&llnum, hbuf + FDBRNUMOFF, sizeof(llnum)); fdb->rnum = TCITOHLL(llnum); memcpy(&llnum, hbuf + FDBFSIZOFF, sizeof(llnum)); fdb->fsiz = TCITOHLL(llnum); memcpy(&llnum, hbuf + FDBWIDTHOFF, sizeof(llnum)); fdb->width = TCITOHLL(llnum); memcpy(&llnum, hbuf + FDBLIMSIZOFF, sizeof(llnum)); fdb->limsiz = TCITOHLL(llnum); memcpy(&llnum, hbuf + FDBMINOFF, sizeof(llnum)); fdb->min = TCITOHLL(llnum); memcpy(&llnum, hbuf + FDBMAXOFF, sizeof(llnum)); fdb->max = TCITOHLL(llnum);}/* Clear all members. `fdb' specifies the fixed-length database object. */static void tcfdbclear(TCFDB *fdb){ assert(fdb); fdb->mmtx = NULL; fdb->amtx = NULL; fdb->rmtxs = NULL; fdb->eckey = NULL; fdb->type = TCDBTFIXED; fdb->flags = 0; fdb->width = FDBDEFWIDTH; fdb->limsiz = FDBDEFLIMSIZ; fdb->wsiz = 0; fdb->rsiz = 0; fdb->limid = 0; fdb->path = NULL; fdb->fd = -1; fdb->omode = 0; fdb->rnum = 0; fdb->fsiz = 0; fdb->min = 0; fdb->max = 0; fdb->iter = 0; fdb->map = NULL; fdb->array = NULL; fdb->ecode = TCESUCCESS; fdb->fatal = false; fdb->inode = 0; fdb->mtime = 0; fdb->dbgfd = -1; fdb->cnt_writerec = -1; fdb->cnt_readrec = -1; fdb->cnt_truncfile = -1; TCDODEBUG(fdb->cnt_writerec = 0); TCDODEBUG(fdb->cnt_readrec = 0); TCDODEBUG(fdb->cnt_truncfile = 0);}/* Set the open flag. `fdb' specifies the fixed-length database object. `flag' specifies the flag value. `sign' specifies the sign. */static void tcfdbsetflag(TCFDB *fdb, int flag, bool sign){ assert(fdb); char *fp = (char *)fdb->map + FDBFLAGSOFF; if(sign){ *fp |= (uint8_t)flag; } else { *fp &= ~(uint8_t)flag; } fdb->flags = *fp;}/* Lock a method of the fixed-length database object. `fdb' specifies the fixed-length database object. `wr' specifies whether the lock is writer or not. If successful, the return value is true, else, it is false. */static bool tcfdblockmethod(TCFDB *fdb, bool wr){ assert(fdb); if(wr ? pthread_rwlock_wrlock(fdb->mmtx) != 0 : pthread_rwlock_rdlock(fdb->mmtx) != 0){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true;}/* Unlock a method of the fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */static bool tcfdbunlockmethod(TCFDB *fdb){ assert(fdb); if(pthread_rwlock_unlock(fdb->mmtx) != 0){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true;}/* Lock the attributes of the fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */static bool tcfdblockattr(TCFDB *fdb){ assert(fdb); if(pthread_mutex_lock(fdb->amtx) != 0){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true;}/* Unlock the attributes of the fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */static bool tcfdbunlockattr(TCFDB *fdb){ assert(fdb); if(pthread_mutex_unlock(fdb->amtx) != 0){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true;}/* Lock a record of the fixed-length database object. `fdb' specifies the fixed-length database object. `wr' specifies whether the lock is writer or not. If successful, the return value is true, else, it is false. */static bool tcfdblockrecord(TCFDB *fdb, bool wr, uint64_t id){ assert(fdb && id > 0); if(wr ? pthread_rwlock_wrlock((pthread_rwlock_t *)fdb->rmtxs + id % FDBRMTXNUM) != 0 : pthread_rwlock_rdlock((pthread_rwlock_t *)fdb->rmtxs + id % FDBRMTXNUM) != 0){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true;}/* Unlock a record of the fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */static bool tcfdbunlockrecord(TCFDB *fdb, uint64_t id){ assert(fdb); if(pthread_rwlock_unlock((pthread_rwlock_t *)fdb->rmtxs + id % FDBRMTXNUM) != 0){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } TCTESTYIELD(); return true;}/* Lock all records of the hash database object. `fdb' specifies the hash database object. `wr' specifies whether the lock is writer or not. If successful, the return value is true, else, it is false. */static bool tcfdblockallrecords(TCFDB *fdb, bool wr){ assert(fdb); for(int i = 0; i < FDBRMTXNUM; i++){ if(wr ? pthread_rwlock_wrlock((pthread_rwlock_t *)fdb->rmtxs + i) != 0 : pthread_rwlock_rdlock((pthread_rwlock_t *)fdb->rmtxs + i) != 0){ while(--i >= 0){ pthread_rwlock_unlock((pthread_rwlock_t *)fdb->rmtxs + i); } tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } } TCTESTYIELD(); return true;}/* Unlock all records of the hash database object. `fdb' specifies the hash database object. If successful, the return value is true, else, it is false. */static bool tcfdbunlockallrecords(TCFDB *fdb){ assert(fdb); bool err = false; for(int i = FDBRMTXNUM - 1; i >= 0; i--){ if(pthread_rwlock_unlock((pthread_rwlock_t *)fdb->rmtxs + i)) err = true; } TCTESTYIELD(); if(err){ tcfdbsetecode(fdb, TCETHREAD, __FILE__, __LINE__, __func__); return false; } return true;}/* Open a database file and connect a fixed-length database object. `fdb' specifies the fixed-length database object. `path' specifies the path of the database file. `omode' specifies the connection mode. If successful, the return value is true, else, it is false. */static bool tcfdbopenimpl(TCFDB *fdb, const char *path, int omode){ assert(fdb && path); int mode = O_RDONLY; if(omode & FDBOWRITER){ mode = O_RDWR; if(omode & FDBOCREAT) mode |= O_CREAT; } int fd = open(path, mode, FDBFILEMODE); if(fd < 0){ int ecode = TCEOPEN; switch(errno){ case EACCES: ecode = TCENOPERM; break; case ENOENT: ecode = TCENOFILE; break; } tcfdbsetecode(fdb, ecode, __FILE__, __LINE__, __func__); return false; } if(!(omode & FDBONOLCK)){ if(!tclock(fd, omode & FDBOWRITER, omode & FDBOLCKNB)){ tcfdbsetecode(fdb, TCELOCK, __FILE__, __LINE__, __func__); close(fd); return false; } } if((omode & FDBOWRITER) && (omode & FDBOTRUNC)){ if(ftruncate(fd, 0) == -1){ tcfdbsetecode(fdb, TCETRUNC, __FILE__, __LINE__, __func__); close(fd); return false; } } struct stat sbuf; if(fstat(fd, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)){ tcfdbsetecode(fdb, TCESTAT, __FILE__, __LINE__, __func__); close(fd); return false; } char hbuf[FDBHEADSIZ]; if((omode & FDBOWRITER) && sbuf.st_size < 1){ fdb->flags = 0; fdb->rnum = 0; fdb->fsiz = FDBHEADSIZ; fdb->min = 0; fdb->max = 0; tcdumpmeta(fdb, hbuf); if(!tcwrite(fd, hbuf, FDBHEADSIZ)){ tcfdbsetecode(fdb, TCEWRITE, __FILE__, __LINE__, __func__); close(fd); return false; } sbuf.st_size = fdb->fsiz; } if(lseek(fd, 0, SEEK_SET) == -1){ tcfdbsetecode(fdb, TCESEEK, __FILE__, __LINE__, __func__); close(fd); return false; } if(!tcread(fd, hbuf, FDBHEADSIZ)){ tcfdbsetecode(fdb, TCEREAD, __FILE__, __LINE__, __func__); close(fd); return false; } int type = fdb->type; tcloadmeta(fdb, hbuf); if(!(omode & FDBONOLCK)){ if(memcmp(hbuf, FDBMAGICDATA, strlen(FDBMAGICDATA)) || fdb->type != type || fdb->width < 1 || sbuf.st_size < fdb->fsiz || fdb->limsiz < FDBHEADSIZ || fdb->fsiz > fdb->limsiz){ tcfdbsetecode(fdb, TCEMETA, __FILE__, __LINE__, __func__); close(fd); return false; } if(sbuf.st_size > fdb->fsiz) fdb->fsiz = sbuf.st_size; } void *map = mmap(0, fdb->limsiz, PROT_READ | ((omode & FDBOWRITER) ? PROT_WRITE : 0), MAP_SHARED, fd, 0); if(map == MAP_FAILED){ tcfdbsetecode(fdb, TCEMMAP, __FILE__, __LINE__, __func__); close(fd); return false; } if(fdb->width <= UINT8_MAX){ fdb->wsiz = sizeof(uint8_t); } else if(fdb->width <= UINT16_MAX){ fdb->wsiz = sizeof(uint16_t); } else { fdb->wsiz = sizeof(uint32_t); } fdb->rsiz = fdb->width + fdb->wsiz; fdb->limid = (fdb->limsiz - FDBHEADSIZ) / fdb->rsiz; fdb->path = tcstrdup(path); fdb->fd = fd; fdb->omode = omode; fdb->iter = 0; fdb->map = map; fdb->array = (unsigned char *)map + FDBHEADSIZ; fdb->ecode = TCESUCCESS; fdb->fatal = false; fdb->inode = (uint64_t)sbuf.st_ino; fdb->mtime = sbuf.st_mtime; if(fdb->omode & FDBOWRITER) tcfdbsetflag(fdb, FDBFOPEN, true); return true;}/* Close a fixed-length database object. `fdb' specifies the fixed-length database object. If successful, the return value is true, else, it is false. */static bool tcfdbcloseimpl(TCFDB *fdb){ assert(fdb); bool err = false; if(fdb->omode & FDBOWRITER) tcfdbsetflag(fdb, FDBFOPEN, false); TCFREE(fdb->path); if((fdb->omode & FDBOWRITER) && !tcfdbmemsync(fdb, false)) err = true; if(munmap(fdb->map, fdb->limsiz) == -1){ tcfdbsetecode(fdb, TCEMMAP, __FILE__, __LINE__, __func__); err = true; } if(close(fdb->fd) == -1){ tcfdbsetecode(fdb, TCECLOSE, __FILE__, __LINE__, __func__); err = true; } fdb->path = NULL; fdb->fd = -1; return !err;}/* Get the previous record of a record. `fdb' specifies the fixed-length database object. `id' specifies the ID number. The return value is the ID number of the previous record or 0 if no record corresponds. */static int64_t tcfdbprevid(TCFDB *fdb, int64_t id){ assert(fdb && id >= 0); id--; while(id >= fdb->min){ TCDODEBUG(fdb->cnt_readrec++); unsigned char *rec = fdb->array + (id - 1) * (fdb->rsiz); unsigned char *rp = rec; uint32_t osiz; uint16_t snum; uint32_t lnum; switch(fdb->wsiz){ case 1: osiz = *(rp++); break; case 2: memcpy(&snum, rp, sizeof(snum)); osiz = TCITOHS(snum); rp += sizeof(snum); break; default: memcpy(&lnum, rp, sizeof(lnum)); osiz = TCITOHL(lnum); rp += sizeof(lnum); break; } if(osiz > 0 || *rp != 0) return id; id--; } return 0;}/* Get the next record of a record. `fdb' specifies the fixed-length database object. `id' specifies the ID number. The return value is the ID number of the next record or 0 if no record corresponds. */static int64_t tcfdbnextid(TCFDB *fdb, int64_t id){ assert(fdb && id >= 0); id++; while(id <= fdb->max){ TCDODEBUG(fdb->cnt_readrec++); unsigned char *rec = fdb->array + (id - 1) * (fdb->rsiz); unsigned char *rp = rec; uint32_t osiz; uint16_t snum;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -