【3】字节写数据库函数库 — 打开数据库

来源:互联网 发布:数据分析技术有哪些 编辑:程序博客网 时间:2024/05/17 06:06
/* 索引记录常量 */#define IDXLEN_SZ    4/* 索引记录长度所占字节数 */#define SEP ':'/* 分隔符 */#define SPACE     ' '/* 空格符 */#define NEWLINE'\n'/* 换行符 *//* 散列表相关常量 */#define PTR_SZ 6/* 指针所占字符数 */#define PTR_MAX 999999/* 文件最大偏移量(六位数最大值) */#define NHASH_DEF137/* 散列表大小 */#define FREE_OFF0/* 空链表在索引文件中的偏移量 */#define HASH_OFFPTR_SZ/* 散列表在索引文件中的偏移量 */typedef unsigned long DBHASH;typedef unsigned long COUNT;/* 记录一个打开数据库的所有信息 */typedef struct {int   idxfd;/* 索引文件描述符 */int   datfd;/* 数据文件描述符 */char *idxbuf;/* 实际索引记录,包括键值、数据记录偏移量、数据记录长度 */char *datbuf;char *name;off_t idxoff;/* 当前索引记录的偏移量 */size_t idxlen;/* 当前索引记录长度 */off_t datoff;/* 数据文件中该记录的偏移量 */size_t datlen;/* 该数据记录长度 */off_t ptrval;/* 下一条索引记录的偏移量 */off_t ptroff;/* 前一条索引记录的偏移量 */off_t chainoff;/* 一条散列链的偏移量 */off_t hashoff;/* 散列表的起始位置 */DBHASH nhash;/* 散列表大小,这里为137 *//* 操作成功或失败的记录 */COUNT cnt_delok;COUNT cnt_delerr;COUNT cnt_fetchok;COUNT cnt_fetcherr;/* 获取数据失败记录 */COUNT cnt_nextrec;COUNT cnt_stor1;/* 插入操作,追加新纪录 */COUNT cnt_stor2;/* 插入操作,找到空记录 */COUNT cnt_stor3;COUNT cnt_stor4;COUNT cnt_storerr;} DB;/* 内部函数 */static DB     *_db_alloc(int);static void    _db_dodelete(DB *);static int     _db_find_and_lock(DB *, const char *, int);static int     _db_findfree(DB *, int, int);static void    _db_free(DB *);static DBHASH  _db_hash(DB *, const char *);static char   *_db_readdat(DB *);static off_t   _db_readidx(DB *, off_t);static off_t   _db_readptr(DB *, off_t);static void    _db_writedat(DB *, const char *, off_t, int);static void    _db_writeidx(DB *, const char *, off_t, int, off_t);static void    _db_writeptr(DB *, off_t, off_t);/* 打开或建立一个数据库 * 调用完此函数后,索引文件pathname.idx只有一个空闲链表和一个散列表 * 数据文件pathname.dat为空 */DBHANDLE db_open(const char *pathname, int oflag, ...){DB *db;int len, mode;size_t i;char asciiptr[PTR_SZ + 1], hash[(NHASH_DEF+1) * PTR_SZ + 2];/* +2表示空字符和换行符 */struct stat statbuff;len = strlen(pathname);if ((db = _db_alloc(len)) == NULL)/* 给db结构体分配空间 */{printf("%d\n", __LINE__);exit(-1);}db->nhash   = NHASH_DEF;/* 散列表大小 = 137 */db->hashoff = HASH_OFF;/* 跳过第一个空闲链表,散列表起始位置 = 6 */if (oflag & O_CREAT)/* 创建文件 */{/* 因为参数是在栈内连续存放,所以可以根据可变参数之前的那个参数地址, * 推算出所有可变参数的地址,从而获得每个可变参数 */va_list ap;/* 实质是一个指针 */va_start(ap, oflag);/* 初始化ap,使它指向可变参数列表中第一个参数 */mode = va_arg(ap, int);/* 获取可变参数,mode保存有访问权限,知道类型就能够获取值 */va_end(ap);/* 关闭指针,使它为NULL *//* 创建文件 */strcpy(db->name, pathname);strcat(db->name, ".idx");db->idxfd = open(db->name, oflag, mode);/* 创建索引文件pathname.idx */strcpy(db->name + len, ".dat");db->datfd = open(db->name, oflag, mode);/* 创建数据文件pathname.dat */}else/* 打开现有数据库 */{strcpy(db->name, pathname);strcat(db->name, ".idx");db->idxfd = open(db->name, oflag);strcpy(db->name + len, ".dat");db->datfd = open(db->name, oflag);}/* 这里一次性检查上面所有的open是否成功,节省了代码空间 */if (db->idxfd < 0 || db->datfd < 0){/* 打开失败 */printf("%d\n", __LINE__);_db_free(db);/* 清理DB结构 */return NULL;/* 打开失败,返回NULL */}/* 注意,要两个条件同时满足 */if ((oflag & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)){/* 初始化新创建的数据库 *//* 长度为0表示锁住整个文件 */if (writew_lock(db->idxfd, 0, SEEK_SET, 0) < 0){printf("%d\n", __LINE__);exit(-1);}if (fstat(db->idxfd, &statbuff) < 0)/* 这个函数一定要上锁 */{printf("%d\n", __LINE__);exit(-1);}if (statbuff.st_size == 0){/* 初始化索引文件,*表示占位符,占用PTR_SZ个字符 *//* 先构造散列表,然后写入索引文件 */sprintf(asciiptr, "%*d", PTR_SZ, 0);/* asciiptr = "_ _ _ _ _ 0" */hash[0] = 0;/* 为了让strcat函数覆盖此空字符 */for (i = 0; i < NHASH_DEF + 1; i++)strcat(hash, asciiptr);/* strcat会覆盖hash尾部的'\0' */strcat(hash, "\n");/* hash和asciiptr尾部都没有空字符 */i = strlen(hash);/* i = 829 = 138 * 6 + 1 */if (write(db->idxfd, hash, i) != i)/* 哈希表写入索引文件 */{printf("%d\n", __LINE__);exit(-1);}}if (un_lock(db->idxfd, 0, SEEK_SET, 0) < 0)/* 文件解锁 */{printf("%d\n", __LINE__);exit(-1);}}db_rewind(db);/* 跳过散列表,使db->idxoff指向第一条索引记录 */return db;}


db_open就是用来打开数据库的,它的函数原型和open系统调用很像,实际上就是根据传入db_open的参数来进行open操作的。当数据库存在,则直接打开,否则创建数据库,包括创建索引文件my_db.idx和数据文件my_db.dat两个文件。整个db_open函数围绕核心结构体DB进行初始化。DB就代表了一个数据库,内部保存有数据库当前的各种数据。用户调用的所有数据库接口都要传入一个响应的DB结构体。而db_open就是返回一个DB结构体,供后续函数接口使用。

以下是分配DB结构体所需的空间:

/* 分配、初始化DB结构体 */static DB *_db_alloc(int namelen){DB *db;if ((db = calloc(1, sizeof(DB))) == NULL){printf("%d\n", __LINE__);exit(-1);}/* calloc将全部成员变成了0,这里重新设置为-1表示此时文件描述符无效 */db->idxfd = db->datfd = -1;/* _db_free 为了检测是否需要关闭 */if ((db->name = malloc(namelen + 5)) == NULL)/* +5保存".idx"或".dat"和空字符 */{printf("%d\n", __LINE__);exit(-1);}if ((db->idxbuf = malloc(IDXLEN_MAX + 2)) == NULL)/* +2保存空字符和换行符 */{printf("%d\n", __LINE__);exit(-1);}if ((db->datbuf = malloc(DATLEN_MAX + 2)) == NULL)/* +2保存空字符和换行符 */{printf("%d\n", __LINE__);exit(-1);}return db;}

db->name负责保存文件名,db->idxbuf负责保存索引记录的内容,db->datbuf负责存储实际的数据内容。

和open操作相反,close操作则执行相反的操作:free掉所有分配出来的空间:

void db_close(DBHANDLE h){_db_free((DB *)h);}/* 释放资源和DB结构 */static void _db_free(DB *db){/* _db_alloc 函数把描述符都设成了-1 * 只要打开了就一定非负 */if (db->idxfd >= 0)close(db->idxfd);if (db->datfd >= 0)close(db->datfd);/* 销毁缓冲区 */if (db->idxbuf != NULL)free(db->idxbuf);if (db->datbuf != NULL)free(db->datbuf);if (db->name != NULL)free(db->name);free(db);/* 销毁整个DB结构体 */}


0 0