删除一条记录的接口函数为db_delete,代码如下:
/* 根据键值删除一条记录 */ int db_delete(DBHANDLE h, const char *key) { DB *db = h; int rc = 0; /* 因为可能要修改记录,所以加写锁 */ if (_db_find_and_lock(db, key, 1) == 0) { /* 查找成功,执行删除操作 */ _db_dodelete(db); db->cnt_delok++; } else { /* 查找失败 */ rc = -1; db->cnt_delerr++; } /* _db_find_and_lock中加了锁,这里要解锁 */ if (un_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0) { printf("%d\n", __LINE__); exit(-1); } return rc; }
/* 实际的删除记录的函数 * 可由db_delete或db_store函数调用 */ static void _db_dodelete(DB *db) { int i; char *ptr; off_t freeptr, saveptr; /* 将数据缓冲填充空格符,后面在向文件中写的时候还会做调整 */ for (ptr = db->datbuf, i = 0; i < db->datlen - 1; i++) *ptr++ = SPACE; *ptr = 0; /* 将索引记录填充空格符,后面在向文件中写的时候还会做调整 * _db_find_and_lock函数调用了_db_readidx函数 * db->idxbuf = "key \0 datoff \0 datlen \0" */ ptr = db->idxbuf; while (*ptr) *ptr++ = SPACE; /* db->idxbuf = "_ _ _ _ \0 datoff \0 datlen \0" */ /* 锁住空闲链表,防止多个进程同时删除从而影响空闲链表 */ if (writew_lock(db->idxfd, FREE_OFF, SEEK_SET, 1) < 0) { printf("%d\n", __LINE__); exit(-1); } /* 向数据文件中写入空白行,_db_readidx中已经设置好了db->datoff * 这里无需加锁,因为db_delete对这条散列链加了写锁 * db->datbuf = "_ _ _ _ _ _ '\0'" */ _db_writedat(db, db->datbuf, db->datoff, SEEK_SET); freeptr = _db_readptr(db, FREE_OFF); /* freeptr = 0 */ saveptr = db->ptrval; /* 下一条索引记录的偏移量 */ /* 修改要删除条目的索引记录,使它的指向下一条记录的指针指向空闲链表的头节点freeptr * 相当于在空闲链表头插入要删除的条目 * db->idxbuf = "_ _ _ _ \0 datoff \0 datlen \0" */ _db_writeidx(db, db->idxbuf, db->idxoff, SEEK_SET, freeptr); /* 修改空闲链表,使之指向被删除条目 */ _db_writeptr(db, FREE_OFF, db->idxoff); /* 使上一条记录跳过要删除记录,指向下一条记录 */ _db_writeptr(db, db->ptroff, saveptr); if (un_lock(db->idxfd, FREE_OFF, SEEK_SET, 1) < 0) { printf("%d\n", __LINE__); exit(-1); } }
/* 向数据文件中写入数据,以换行符结尾 */ static void _db_writedat(DB *db, const char *data, off_t offset, int whence) { struct iovec iov[2]; static char newline = NEWLINE; if (whence == SEEK_END) /* 追加,则锁住整个数据文件 */ if (writew_lock(db->datfd, 0, SEEK_SET, 0) < 0) { printf("%d\n", __LINE__); exit(-1); } if ((db->datoff = lseek(db->datfd, offset, whence)) == -1) /* 在数据文件中定位 */ { printf("%d\n", __LINE__); exit(-1); } db->datlen = strlen(data) + 1; /* +1容纳换行符 */ /* 聚集写 * 写入到文件中的数据,要以换行符结尾 */ iov[0].iov_base = (char *)data; iov[0].iov_len = db->datlen - 1; iov[1].iov_base = &newline; iov[1].iov_len = 1; if (writev(db->datfd, &iov[0], 2) != db->datlen) { printf("%d\n", __LINE__); exit(-1); } if (whence == SEEK_END) if (un_lock(db->datfd, 0, SEEK_SET, 0) < 0) { printf("%d\n", __LINE__); exit(-1); } }
/* 向索引文件写一条索引记录 */ static void _db_writeidx(DB *db, const char *key, off_t offset, int whence, off_t ptrval) { struct iovec iov[2]; char asciiptrlen[PTR_SZ + IDXLEN_SZ + 1]; int len; char *fmt; /* ptrval是下一条记录的偏移量 */ if ((db->ptrval = ptrval) < 0 || ptrval > PTR_MAX) { printf("%d\n", __LINE__); exit(-1); } if (sizeof(off_t) == sizeof(long long)) fmt = "%s%c%lld%c%d\n"; else fmt = "%s%c%ld%c%d\n"; /* datoff和datlen已经在_db_find_and_lock函数中被设置好了 * 实际上,被删除的索引记录依然指向原来的数据,但key部分已经变为空了 */ sprintf(db->idxbuf, fmt, key, SEP, db->datoff, SEP, db->datlen); /* 索引记录后半部分 */ if ((len = strlen(db->idxbuf)) < IDXLEN_MIN || len > IDXLEN_MAX) { printf("%d\n", __LINE__); exit(-1); } /* ptrval为下一条记录的偏移量 */ sprintf(asciiptrlen, "%*ld%*d", PTR_SZ, ptrval, IDXLEN_SZ, len); /* 索引记录前半部分 */ if (whence == SEEK_END) /* 追加,则需要加写锁 */ /* 跳过空闲链表和散列表,锁定下一行至结尾 */ if (writew_lock(db->idxfd, ((db->nhash + 1) * PTR_SZ) + 1, SEEK_SET, 0) < 0) { printf("%d\n", __LINE__); exit(-1); } if ((db->idxoff = lseek(db->idxfd, offset, whence)) == -1) { printf("%d\n", __LINE__); exit(-1); } /* 聚集写 */ iov[0].iov_base = asciiptrlen; iov[0].iov_len = PTR_SZ + IDXLEN_SZ; iov[1].iov_base = db->idxbuf; iov[1].iov_len = len; if (writev(db->idxfd, &iov[0], 2) != PTR_SZ + IDXLEN_SZ + len) { printf("%d\n", __LINE__); exit(-1); } if (whence == SEEK_END) if (un_lock(db->idxfd, ((db->nhash + 1) * PTR_SZ) + 1, SEEK_SET, 0) < 0) { printf("%d\n", __LINE__); exit(-1); } }
/* 将一个链表指针写入索引文件 */ static void _db_writeptr(DB *db, off_t offset, off_t ptrval) { char asciiptr[PTR_SZ + 1]; if (ptrval < 0 || ptrval > PTR_MAX) { printf("%d\n", __LINE__); exit(-1); } /* 数值型转换成字符型写入缓冲区 */ sprintf(asciiptr, "%*ld", PTR_SZ, ptrval); /* 写之前的定位 */ if (lseek(db->idxfd, offset, SEEK_SET) == -1) { printf("%d\n", __LINE__); exit(-1); } if (write(db->idxfd, asciiptr, PTR_SZ) != PTR_SZ) { printf("%d\n", __LINE__); exit(-1); } }