⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 db.c

📁 unix环境高级编程第二版配套源代码 unix环境高级编程第二版配套源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
#include "apue.h"#include "apue_db.h"#include <fcntl.h>		/* open & db_open flags */#include <stdarg.h>#include <errno.h>#include <sys/uio.h>	/* struct iovec *//* * Internal index file constants. * These are used to construct records in the * index file and data file. */#define IDXLEN_SZ	   4	/* index record length (ASCII chars) */#define SEP         ':'	/* separator char in index record */#define SPACE       ' '	/* space character */#define NEWLINE     '\n'	/* newline character *//* * The following definitions are for hash chains and free * list chain in the index file. */#define PTR_SZ        6	/* size of ptr field in hash chain */#define PTR_MAX  999999	/* max file offset = 10**PTR_SZ - 1 */#define NHASH_DEF	 137	/* default hash table size */#define FREE_OFF      0	/* free list offset in index file */#define HASH_OFF PTR_SZ	/* hash table offset in index file */typedef unsigned long	DBHASH;	/* hash values */typedef unsigned long	COUNT;	/* unsigned counter *//* * Library's private representation of the database. */typedef struct {  int    idxfd;  /* fd for index file */  int    datfd;  /* fd for data file */  char  *idxbuf; /* malloc'ed buffer for index record */  char  *datbuf; /* malloc'ed buffer for data record*/  char  *name;   /* name db was opened under */  off_t  idxoff; /* offset in index file of index record */			      /* key is at (idxoff + PTR_SZ + IDXLEN_SZ) */  size_t idxlen; /* length of index record */			      /* excludes IDXLEN_SZ bytes at front of record */			      /* includes newline at end of index record */  off_t  datoff; /* offset in data file of data record */  size_t datlen; /* length of data record */			      /* includes newline at end */  off_t  ptrval; /* contents of chain ptr in index record */  off_t  ptroff; /* chain ptr offset pointing to this idx record */  off_t  chainoff; /* offset of hash chain for this index record */  off_t  hashoff;  /* offset in index file of hash table */  DBHASH nhash;    /* current hash table size */  COUNT  cnt_delok;    /* delete OK */  COUNT  cnt_delerr;   /* delete error */  COUNT  cnt_fetchok;  /* fetch OK */  COUNT  cnt_fetcherr; /* fetch error */  COUNT  cnt_nextrec;  /* nextrec */  COUNT  cnt_stor1;    /* store: DB_INSERT, no empty, appended */  COUNT  cnt_stor2;    /* store: DB_INSERT, found empty, reused */  COUNT  cnt_stor3;    /* store: DB_REPLACE, diff len, appended */  COUNT  cnt_stor4;    /* store: DB_REPLACE, same len, overwrote */  COUNT  cnt_storerr;  /* store error */} DB;/* * Internal functions. */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);/* * Open or create a database.  Same arguments as open(2). */DBHANDLEdb_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 for newline and null */	struct stat	statbuff;	/*	 * Allocate a DB structure, and the buffers it needs.	 */	len = strlen(pathname);	if ((db = _db_alloc(len)) == NULL)		err_dump("db_open: _db_alloc error for DB");	db->nhash   = NHASH_DEF;/* hash table size */	db->hashoff = HASH_OFF;	/* offset in index file of hash table */	strcpy(db->name, pathname);	strcat(db->name, ".idx");	if (oflag & O_CREAT) {		va_list ap;		va_start(ap, oflag);		mode = va_arg(ap, int);		va_end(ap);		/*		 * Open index file and data file.		 */		db->idxfd = open(db->name, oflag, mode);		strcpy(db->name + len, ".dat");		db->datfd = open(db->name, oflag, mode);	} else {		/*		 * Open index file and data file.		 */		db->idxfd = open(db->name, oflag);		strcpy(db->name + len, ".dat");		db->datfd = open(db->name, oflag);	}	if (db->idxfd < 0 || db->datfd < 0) {		_db_free(db);		return(NULL);	}	if ((oflag & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) {		/*		 * If the database was created, we have to initialize		 * it.  Write lock the entire file so that we can stat		 * it, check its size, and initialize it, atomically.		 */		if (writew_lock(db->idxfd, 0, SEEK_SET, 0) < 0)			err_dump("db_open: writew_lock error");		if (fstat(db->idxfd, &statbuff) < 0)			err_sys("db_open: fstat error");		if (statbuff.st_size == 0) {			/*			 * We have to build a list of (NHASH_DEF + 1) chain			 * ptrs with a value of 0.  The +1 is for the free			 * list pointer that precedes the hash table.			 */			sprintf(asciiptr, "%*d", PTR_SZ, 0);			hash[0] = 0;			for (i = 0; i < NHASH_DEF + 1; i++)				strcat(hash, asciiptr);			strcat(hash, "\n");			i = strlen(hash);			if (write(db->idxfd, hash, i) != i)				err_dump("db_open: index file init write error");		}		if (un_lock(db->idxfd, 0, SEEK_SET, 0) < 0)			err_dump("db_open: un_lock error");	}	db_rewind(db);	return(db);}/* * Allocate & initialize a DB structure and its buffers. */static DB *_db_alloc(int namelen){	DB		*db;	/*	 * Use calloc, to initialize the structure to zero.	 */	if ((db = calloc(1, sizeof(DB))) == NULL)		err_dump("_db_alloc: calloc error for DB");	db->idxfd = db->datfd = -1;				/* descriptors */	/*	 * Allocate room for the name.	 * +5 for ".idx" or ".dat" plus null at end.	 */	if ((db->name = malloc(namelen + 5)) == NULL)		err_dump("_db_alloc: malloc error for name");	/*	 * Allocate an index buffer and a data buffer.	 * +2 for newline and null at end.	 */	if ((db->idxbuf = malloc(IDXLEN_MAX + 2)) == NULL)		err_dump("_db_alloc: malloc error for index buffer");	if ((db->datbuf = malloc(DATLEN_MAX + 2)) == NULL)		err_dump("_db_alloc: malloc error for data buffer");	return(db);}/* * Relinquish access to the database. */voiddb_close(DBHANDLE h){	_db_free((DB *)h);	/* closes fds, free buffers & struct */}/* * Free up a DB structure, and all the malloc'ed buffers it * may point to.  Also close the file descriptors if still open. */static void_db_free(DB *db){	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);}/* * Fetch a record.  Return a pointer to the null-terminated data. */char *db_fetch(DBHANDLE h, const char *key){	DB      *db = h;	char	*ptr;	if (_db_find_and_lock(db, key, 0) < 0) {		ptr = NULL;				/* error, record not found */		db->cnt_fetcherr++;	} else {		ptr = _db_readdat(db);	/* return pointer to data */		db->cnt_fetchok++;	}	/*	 * Unlock the hash chain that _db_find_and_lock locked.	 */	if (un_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0)		err_dump("db_fetch: un_lock error");	return(ptr);}/* * Find the specified record.  Called by db_delete, db_fetch, * and db_store.  Returns with the hash chain locked. */static int_db_find_and_lock(DB *db, const char *key, int writelock){	off_t	offset, nextoffset;	/*	 * Calculate the hash value for this key, then calculate the	 * byte offset of corresponding chain ptr in hash table.	 * This is where our search starts.  First we calculate the	 * offset in the hash table for this key.	 */	db->chainoff = (_db_hash(db, key) * PTR_SZ) + db->hashoff;	db->ptroff = db->chainoff;	/*	 * We lock the hash chain here.  The caller must unlock it	 * when done.  Note we lock and unlock only the first byte.	 */	if (writelock) {		if (writew_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0)			err_dump("_db_find_and_lock: writew_lock error");	} else {		if (readw_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0)			err_dump("_db_find_and_lock: readw_lock error");	}	/*	 * Get the offset in the index file of first record	 * on the hash chain (can be 0).	 */	offset = _db_readptr(db, db->ptroff);	while (offset != 0) {		nextoffset = _db_readidx(db, offset);		if (strcmp(db->idxbuf, key) == 0)			break;       /* found a match */		db->ptroff = offset; /* offset of this (unequal) record */		offset = nextoffset; /* next one to compare */	}	/*	 * offset == 0 on error (record not found).	 */	return(offset == 0 ? -1 : 0);}/* * Calculate the hash value for a key. */static DBHASH_db_hash(DB *db, const char *key){	DBHASH		hval = 0;	char		c;	int			i;	for (i = 1; (c = *key++) != 0; i++)		hval += c * i;		/* ascii char times its 1-based index */	return(hval % db->nhash);}/* * Read a chain ptr field from anywhere in the index file: * the free list pointer, a hash table chain ptr, or an * index record chain ptr. */static off_t_db_readptr(DB *db, off_t offset){	char	asciiptr[PTR_SZ + 1];	if (lseek(db->idxfd, offset, SEEK_SET) == -1)		err_dump("_db_readptr: lseek error to ptr field");	if (read(db->idxfd, asciiptr, PTR_SZ) != PTR_SZ)		err_dump("_db_readptr: read error of ptr field");	asciiptr[PTR_SZ] = 0;		/* null terminate */	return(atol(asciiptr));}/* * Read the next index record.  We start at the specified offset * in the index file.  We read the index record into db->idxbuf * and replace the separators with null bytes.  If all is OK we * set db->datoff and db->datlen to the offset and length of the * corresponding data record in the data file. */static off_t_db_readidx(DB *db, off_t offset){	ssize_t				i;	char			*ptr1, *ptr2;	char			asciiptr[PTR_SZ + 1], asciilen[IDXLEN_SZ + 1];	struct iovec	iov[2];	/*	 * Position index file and record the offset.  db_nextrec	 * calls us with offset==0, meaning read from current offset.	 * We still need to call lseek to record the current offset.	 */	if ((db->idxoff = lseek(db->idxfd, offset,	  offset == 0 ? SEEK_CUR : SEEK_SET)) == -1)		err_dump("_db_readidx: lseek error");	/*	 * Read the ascii chain ptr and the ascii length at	 * the front of the index record.  This tells us the	 * remaining size of the index record.	 */	iov[0].iov_base = asciiptr;	iov[0].iov_len  = PTR_SZ;	iov[1].iov_base = asciilen;	iov[1].iov_len  = IDXLEN_SZ;	if ((i = readv(db->idxfd, &iov[0], 2)) != PTR_SZ + IDXLEN_SZ) {		if (i == 0 && offset == 0)			return(-1);		/* EOF for db_nextrec */		err_dump("_db_readidx: readv error of index record");	}	/*	 * This is our return value; always >= 0.	 */	asciiptr[PTR_SZ] = 0;        /* null terminate */	db->ptrval = atol(asciiptr); /* offset of next key in chain */	asciilen[IDXLEN_SZ] = 0;     /* null terminate */	if ((db->idxlen = atoi(asciilen)) < IDXLEN_MIN ||	  db->idxlen > IDXLEN_MAX)		err_dump("_db_readidx: invalid length");	/*	 * Now read the actual index record.  We read it into the key	 * buffer that we malloced when we opened the database.	 */	if ((i = read(db->idxfd, db->idxbuf, db->idxlen)) != db->idxlen)		err_dump("_db_readidx: read error of index record");	if (db->idxbuf[db->idxlen-1] != NEWLINE)	/* sanity check */		err_dump("_db_readidx: missing newline");	db->idxbuf[db->idxlen-1] = 0;	 /* replace newline with null */	/*	 * Find the separators in the index record.	 */	if ((ptr1 = strchr(db->idxbuf, SEP)) == NULL)		err_dump("_db_readidx: missing first separator");	*ptr1++ = 0;				/* replace SEP with null */	if ((ptr2 = strchr(ptr1, SEP)) == NULL)		err_dump("_db_readidx: missing second separator");	*ptr2++ = 0;				/* replace SEP with null */	if (strchr(ptr2, SEP) != NULL)		err_dump("_db_readidx: too many separators");	/*	 * Get the starting offset and length of the data record.	 */	if ((db->datoff = atol(ptr1)) < 0)		err_dump("_db_readidx: starting offset < 0");	if ((db->datlen = atol(ptr2)) <= 0 || db->datlen > DATLEN_MAX)		err_dump("_db_readidx: invalid length");	return(db->ptrval);		/* return offset of next key in chain */}/* * Read the current data record into the data buffer. * Return a pointer to the null-terminated data buffer. */static char *_db_readdat(DB *db){	if (lseek(db->datfd, db->datoff, SEEK_SET) == -1)		err_dump("_db_readdat: lseek error");	if (read(db->datfd, db->datbuf, db->datlen) != db->datlen)		err_dump("_db_readdat: read error");	if (db->datbuf[db->datlen-1] != NEWLINE)	/* sanity check */		err_dump("_db_readdat: missing newline");	db->datbuf[db->datlen-1] = 0; /* replace newline with null */	return(db->datbuf);		/* return pointer to data record */}/* * Delete the specified record. */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -