java下手撸数据库demo
APUE有一章用c实现了数据库demo。这里尝试使用Java实现类似的功能。
APUE数据库demo
参考: APUE
使用C语言编写, 主要为以下4个文件:apue.h, db.c, Makefile, t4.c

向外部暴露的接口(头文件)
#ifndef _APUE_DB_H
#define _APUE_DB_H
typedef void * DBHANDLE;
const char *, int, ...);
DBHANDLE db_open(void db_close(DBHANDLE);
char *db_fetch(DBHANDLE, const char *);
int db_store(DBHANDLE, const char *, const char *, int);
int db_delete(DBHANDLE, const char *);
void db_rewind(DBHANDLE);
char *db_nextrec(DBHANDLE, char *);
/*
* Flags for db_store().
*/
#define DB_INSERT 1 /* insert new record only */
#define DB_REPLACE 2 /* replace existing record */
#define DB_STORE 3 /* replace or insert */
/*
* Implementation limits.
*/
#define IDXLEN_MIN 6 /* key, sep, start, sep, length, \n */
#define IDXLEN_MAX 1024 /* arbitrary */
#define DATLEN_MIN 2 /* data byte, newline */
#define DATLEN_MAX 1024 /* arbitrary */
#endif /* _APUE_DB_H */
库函数实现
#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 7 /* size of ptr field in hash chain */
#define PTR_MAX 9999999 /* 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 */
/* offset in index file of index record */
off_t idxoff; /* 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 */
/* offset in data file of data record */
off_t datoff; size_t datlen; /* length of data record */
/* includes newline at end */
/* contents of chain ptr in index record */
off_t ptrval; /* chain ptr offset pointing to this idx record */
off_t ptroff; /* offset of hash chain for this index record */
off_t chainoff; /* offset in index file of hash table */
off_t hashoff; /* current hash table size */
DBHASH nhash; /* delete OK */
COUNT cnt_delok; /* delete error */
COUNT cnt_delerr; /* fetch OK */
COUNT cnt_fetchok; /* fetch error */
COUNT cnt_fetcherr; /* nextrec */
COUNT cnt_nextrec; /* store: DB_INSERT, no empty, appended */
COUNT cnt_stor1; /* store: DB_INSERT, found empty, reused */
COUNT cnt_stor2; /* store: DB_REPLACE, diff len, appended */
COUNT cnt_stor3; /* store: DB_REPLACE, same len, overwrote */
COUNT cnt_stor4; /* store error */
COUNT cnt_storerr;
} 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).
*/
DBHANDLEconst char *pathname, int oflag, ...)
db_open(
{
DB *db;int len, mode;
size_t i;
char asciiptr[PTR_SZ + 1],
1) * PTR_SZ + 2];
hash[(NHASH_DEF + /* +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)
"db_open: _db_alloc error for DB");
err_dump(
/* hash table size */
db->nhash = NHASH_DEF;/* offset in index file of hash table */
db->hashoff = HASH_OFF;
strcpy(db->name, pathname);".idx");
strcat(db->name,
if (oflag & O_CREAT) {
va_list ap;
va_start(ap, oflag);int);
mode = va_arg(ap,
va_end(ap);
/*
* Open index file and data file.
*/
db->idxfd = open(db->name, oflag, mode);".dat");
strcpy(db->name + len,
db->datfd = open(db->name, oflag, mode);else {
} /*
* Open index file and data file.
*/
db->idxfd = open(db->name, oflag);".dat");
strcpy(db->name + len,
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)
"db_open: writew_lock error");
err_dump(
if (fstat(db->idxfd, &statbuff) < 0)
"db_open: fstat error");
err_sys(
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.
*/
"%*d", PTR_SZ, 0);
sprintf(asciiptr, 0] = 0;
hash[for (i = 0; i < NHASH_DEF + 1; i++)
strcat(hash, asciiptr);"\n");
strcat(hash,
i = strlen(hash);if (write(db->idxfd, hash, i) != i)
"db_open: index file init write error");
err_dump(
}if (un_lock(db->idxfd, 0, SEEK_SET, 0) < 0)
"db_open: un_lock error");
err_dump(
}
db_rewind(db);return(db);
}
/*
* Allocate & initialize a DB structure and its buffers.
*/
static DB *
int namelen)
_db_alloc(
{
DB *db;
/*
* Use calloc, to initialize the structure to zero.
*/
if ((db = calloc(1, sizeof(DB))) == NULL)
"_db_alloc: calloc error for DB");
err_dump(1; /* descriptors */
db->idxfd = db->datfd = -
/*
* Allocate room for the name.
* +5 for ".idx" or ".dat" plus null at end.
*/
if ((db->name = malloc(namelen + 5)) == NULL)
"_db_alloc: malloc error for name");
err_dump(
/*
* Allocate an index buffer and a data buffer.
* +2 for newline and null at end.
*/
if ((db->idxbuf = malloc(IDXLEN_MAX + 2)) == NULL)
"_db_alloc: malloc error for index buffer");
err_dump(if ((db->datbuf = malloc(DATLEN_MAX + 2)) == NULL)
"_db_alloc: malloc error for data buffer");
err_dump(return(db);
}
/*
* Relinquish access to the database.
*/
void
db_close(DBHANDLE h)
{/* closes fds, free buffers & struct */
_db_free((DB *)h);
}
/*
* 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 *
const char *key)
db_fetch(DBHANDLE h,
{
DB *db = h;char *ptr;
if (_db_find_and_lock(db, key, 0) < 0) {
/* error, record not found */
ptr = NULL;
db->cnt_fetcherr++;else {
} /* return pointer to data */
ptr = _db_readdat(db);
db->cnt_fetchok++;
}
/*
* Unlock the hash chain that _db_find_and_lock locked.
*/
if (un_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0)
"db_fetch: un_lock error");
err_dump(return(ptr);
}
/*
* Find the specified record. Called by db_delete, db_fetch,
* and db_store. Returns with the hash chain locked.
*/
static int
const char *key, int writelock)
_db_find_and_lock(DB *db,
{
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)
"_db_find_and_lock: writew_lock error");
err_dump(else {
} if (readw_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0)
"_db_find_and_lock: readw_lock error");
err_dump(
}
/*
* 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 */
/* offset of this (unequal) record */
db->ptroff = offset; /* next one to compare */
offset = nextoffset;
}/*
* offset == 0 on error (record not found).
*/
return(offset == 0 ? -1 : 0);
}
/*
* Calculate the hash value for a key.
*/
static DBHASH
const char *key)
_db_hash(DB *db,
{0;
DBHASH hval = char c;
int i;
for (i = 1; (c = *key++) != 0; i++)
/* ascii char times its 1-based index */
hval += c * i; 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)
"_db_readptr: lseek error to ptr field");
err_dump(if (read(db->idxfd, asciiptr, PTR_SZ) != PTR_SZ)
"_db_readptr: read error of ptr field");
err_dump(0; /* null terminate */
asciiptr[PTR_SZ] = 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,
0 ? SEEK_CUR : SEEK_SET)) == -1)
offset == "_db_readidx: lseek error");
err_dump(
/*
* 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.
*/
0].iov_base = asciiptr;
iov[0].iov_len = PTR_SZ;
iov[1].iov_base = asciilen;
iov[1].iov_len = IDXLEN_SZ;
iov[if ((i = readv(db->idxfd, &iov[0], 2)) != PTR_SZ + IDXLEN_SZ) {
if (i == 0 && offset == 0)
return(-1); /* EOF for db_nextrec */
"_db_readidx: readv error of index record");
err_dump(
}
/*
* This is our return value; always >= 0.
*/
0; /* null terminate */
asciiptr[PTR_SZ] = /* offset of next key in chain */
db->ptrval = atol(asciiptr);
0; /* null terminate */
asciilen[IDXLEN_SZ] = if ((db->idxlen = atoi(asciilen)) < IDXLEN_MIN ||
db->idxlen > IDXLEN_MAX)"_db_readidx: invalid length");
err_dump(
/*
* 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)
"_db_readidx: read error of index record");
err_dump(if (db->idxbuf[db->idxlen-1] != NEWLINE) /* sanity check */
"_db_readidx: missing newline");
err_dump(1] = 0; /* replace newline with null */
db->idxbuf[db->idxlen-
/*
* Find the separators in the index record.
*/
if ((ptr1 = strchr(db->idxbuf, SEP)) == NULL)
"_db_readidx: missing first separator");
err_dump(0; /* replace SEP with null */
*ptr1++ =
if ((ptr2 = strchr(ptr1, SEP)) == NULL)
"_db_readidx: missing second separator");
err_dump(0; /* replace SEP with null */
*ptr2++ =
if (strchr(ptr2, SEP) != NULL)
"_db_readidx: too many separators");
err_dump(
/*
* Get the starting offset and length of the data record.
*/
if ((db->datoff = atol(ptr1)) < 0)
"_db_readidx: starting offset < 0");
err_dump(if ((db->datlen = atol(ptr2)) <= 0 || db->datlen > DATLEN_MAX)
"_db_readidx: invalid length");
err_dump(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)
"_db_readdat: lseek error");
err_dump(if (read(db->datfd, db->datbuf, db->datlen) != db->datlen)
"_db_readdat: read error");
err_dump(if (db->datbuf[db->datlen-1] != NEWLINE) /* sanity check */
"_db_readdat: missing newline");
err_dump(1] = 0; /* replace newline with null */
db->datbuf[db->datlen-return(db->datbuf); /* return pointer to data record */
}
/*
* Delete the specified record.
*/
int
const char *key)
db_delete(DBHANDLE h,
{
DB *db = h;int rc = 0; /* assume record will be found */
if (_db_find_and_lock(db, key, 1) == 0) {
_db_dodelete(db);
db->cnt_delok++;else {
} 1; /* not found */
rc = -
db->cnt_delerr++;
}if (un_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0)
"db_delete: un_lock error");
err_dump(return(rc);
}
/*
* Delete the current record specified by the DB structure.
* This function is called by db_delete and db_store, after
* the record has been located by _db_find_and_lock.
*/
static void
_db_dodelete(DB *db)
{int i;
char *ptr;
off_t freeptr, saveptr;
/*
* Set data buffer and key to all blanks.
*/
for (ptr = db->datbuf, i = 0; i < db->datlen - 1; i++)
*ptr++ = SPACE;0; /* null terminate for _db_writedat */
*ptr =
ptr = db->idxbuf;while (*ptr)
*ptr++ = SPACE;
/*
* We have to lock the free list.
*/
if (writew_lock(db->idxfd, FREE_OFF, SEEK_SET, 1) < 0)
"_db_dodelete: writew_lock error");
err_dump(
/*
* Write the data record with all blanks.
*/
_db_writedat(db, db->datbuf, db->datoff, SEEK_SET);
/*
* Read the free list pointer. Its value becomes the
* chain ptr field of the deleted index record. This means
* the deleted record becomes the head of the free list.
*/
freeptr = _db_readptr(db, FREE_OFF);
/*
* Save the contents of index record chain ptr,
* before it's rewritten by _db_writeidx.
*/
saveptr = db->ptrval;
/*
* Rewrite the index record. This also rewrites the length
* of the index record, the data offset, and the data length,
* none of which has changed, but that's OK.
*/
_db_writeidx(db, db->idxbuf, db->idxoff, SEEK_SET, freeptr);
/*
* Write the new free list pointer.
*/
_db_writeptr(db, FREE_OFF, db->idxoff);
/*
* Rewrite the chain ptr that pointed to this record being
* deleted. Recall that _db_find_and_lock sets db->ptroff to
* point to this chain ptr. We set this chain ptr to the
* contents of the deleted record's chain ptr, saveptr.
*/
_db_writeptr(db, db->ptroff, saveptr);if (un_lock(db->idxfd, FREE_OFF, SEEK_SET, 1) < 0)
"_db_dodelete: un_lock error");
err_dump(
}
/*
* Write a data record. Called by _db_dodelete (to write
* the record with blanks) and db_store.
*/
static void
const char *data, off_t offset, int whence)
_db_writedat(DB *db,
{struct iovec iov[2];
static char newline = NEWLINE;
/*
* If we're appending, we have to lock before doing the lseek
* and write to make the two an atomic operation. If we're
* overwriting an existing record, we don't have to lock.
*/
if (whence == SEEK_END) /* we're appending, lock entire file */
if (writew_lock(db->datfd, 0, SEEK_SET, 0) < 0)
"_db_writedat: writew_lock error");
err_dump(
if ((db->datoff = lseek(db->datfd, offset, whence)) == -1)
"_db_writedat: lseek error");
err_dump(1; /* datlen includes newline */
db->datlen = strlen(data) +
0].iov_base = (char *) data;
iov[0].iov_len = db->datlen - 1;
iov[1].iov_base = &newline;
iov[1].iov_len = 1;
iov[if (writev(db->datfd, &iov[0], 2) != db->datlen)
"_db_writedat: writev error of data record");
err_dump(
if (whence == SEEK_END)
if (un_lock(db->datfd, 0, SEEK_SET, 0) < 0)
"_db_writedat: un_lock error");
err_dump(
}
/*
* Write an index record. _db_writedat is called before
* this function to set the datoff and datlen fields in the
* DB structure, which we need to write the index record.
*/
static void
const char *key,
_db_writeidx(DB *db, int whence, off_t ptrval)
off_t offset,
{struct iovec iov[2];
char asciiptrlen[PTR_SZ + IDXLEN_SZ + 1];
int len;
if ((db->ptrval = ptrval) < 0 || ptrval > PTR_MAX)
"_db_writeidx: invalid ptr: %d", ptrval);
err_quit("%s%c%lld%c%ld\n", key, SEP,
sprintf(db->idxbuf, long long)db->datoff, SEP, (long)db->datlen);
(
len = strlen(db->idxbuf);if (len < IDXLEN_MIN || len > IDXLEN_MAX)
"_db_writeidx: invalid length");
err_dump("%*lld%*d", PTR_SZ, (long long)ptrval,
sprintf(asciiptrlen,
IDXLEN_SZ, len);
/*
* If we're appending, we have to lock before doing the lseek
* and write to make the two an atomic operation. If we're
* overwriting an existing record, we don't have to lock.
*/
if (whence == SEEK_END) /* we're appending */
if (writew_lock(db->idxfd, ((db->nhash+1)*PTR_SZ)+1,
0) < 0)
SEEK_SET, "_db_writeidx: writew_lock error");
err_dump(
/*
* Position the index file and record the offset.
*/
if ((db->idxoff = lseek(db->idxfd, offset, whence)) == -1)
"_db_writeidx: lseek error");
err_dump(
0].iov_base = asciiptrlen;
iov[0].iov_len = PTR_SZ + IDXLEN_SZ;
iov[1].iov_base = db->idxbuf;
iov[1].iov_len = len;
iov[if (writev(db->idxfd, &iov[0], 2) != PTR_SZ + IDXLEN_SZ + len)
"_db_writeidx: writev error of index record");
err_dump(
if (whence == SEEK_END)
if (un_lock(db->idxfd, ((db->nhash+1)*PTR_SZ)+1,
0) < 0)
SEEK_SET, "_db_writeidx: un_lock error");
err_dump(
}
/*
* Write a chain ptr field somewhere in the index file:
* the free list, the hash table, or in an index record.
*/
static void
_db_writeptr(DB *db, off_t offset, off_t ptrval)
{char asciiptr[PTR_SZ + 1];
if (ptrval < 0 || ptrval > PTR_MAX)
"_db_writeptr: invalid ptr: %d", ptrval);
err_quit("%*lld", PTR_SZ, (long long)ptrval);
sprintf(asciiptr,
if (lseek(db->idxfd, offset, SEEK_SET) == -1)
"_db_writeptr: lseek error to ptr field");
err_dump(if (write(db->idxfd, asciiptr, PTR_SZ) != PTR_SZ)
"_db_writeptr: write error of ptr field");
err_dump(
}
/*
* Store a record in the database. Return 0 if OK, 1 if record
* exists and DB_INSERT specified, -1 on error.
*/
int
const char *key, const char *data, int flag)
db_store(DBHANDLE h,
{
DB *db = h;int rc, keylen, datlen;
off_t ptrval;
if (flag != DB_INSERT && flag != DB_REPLACE &&
flag != DB_STORE) {
errno = EINVAL;return(-1);
}
keylen = strlen(key);1; /* +1 for newline at end */
datlen = strlen(data) + if (datlen < DATLEN_MIN || datlen > DATLEN_MAX)
"db_store: invalid data length");
err_dump(
/*
* _db_find_and_lock calculates which hash table this new record
* goes into (db->chainoff), regardless of whether it already
* exists or not. The following calls to _db_writeptr change the
* hash table entry for this chain to point to the new record.
* The new record is added to the front of the hash chain.
*/
if (_db_find_and_lock(db, key, 1) < 0) { /* record not found */
if (flag == DB_REPLACE) {
1;
rc = -
db->cnt_storerr++;/* error, record does not exist */
errno = ENOENT; goto doreturn;
}
/*
* _db_find_and_lock locked the hash chain for us; read
* the chain ptr to the first index record on hash chain.
*/
ptrval = _db_readptr(db, db->chainoff);
if (_db_findfree(db, keylen, datlen) < 0) {
/*
* Can't find an empty record big enough. Append the
* new record to the ends of the index and data files.
*/
0, SEEK_END);
_db_writedat(db, data, 0, SEEK_END, ptrval);
_db_writeidx(db, key,
/*
* db->idxoff was set by _db_writeidx. The new
* record goes to the front of the hash chain.
*/
_db_writeptr(db, db->chainoff, db->idxoff);
db->cnt_stor1++;else {
} /*
* Reuse an empty record. _db_findfree removed it from
* the free list and set both db->datoff and db->idxoff.
* Reused record goes to the front of the hash chain.
*/
_db_writedat(db, data, db->datoff, SEEK_SET);
_db_writeidx(db, key, db->idxoff, SEEK_SET, ptrval);
_db_writeptr(db, db->chainoff, db->idxoff);
db->cnt_stor2++;
}else { /* record found */
} if (flag == DB_INSERT) {
1; /* error, record already in db */
rc =
db->cnt_storerr++;goto doreturn;
}
/*
* We are replacing an existing record. We know the new
* key equals the existing key, but we need to check if
* the data records are the same size.
*/
if (datlen != db->datlen) {
/* delete the existing record */
_db_dodelete(db);
/*
* Reread the chain ptr in the hash table
* (it may change with the deletion).
*/
ptrval = _db_readptr(db, db->chainoff);
/*
* Append new index and data records to end of files.
*/
0, SEEK_END);
_db_writedat(db, data, 0, SEEK_END, ptrval);
_db_writeidx(db, key,
/*
* New record goes to the front of the hash chain.
*/
_db_writeptr(db, db->chainoff, db->idxoff);
db->cnt_stor3++;else {
} /*
* Same size data, just replace data record.
*/
_db_writedat(db, data, db->datoff, SEEK_SET);
db->cnt_stor4++;
}
}0; /* OK */
rc =
/* unlock hash chain locked by _db_find_and_lock */
doreturn: if (un_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0)
"db_store: un_lock error");
err_dump(return(rc);
}
/*
* Try to find a free index record and accompanying data record
* of the correct sizes. We're only called by db_store.
*/
static int
int keylen, int datlen)
_db_findfree(DB *db,
{int rc;
off_t offset, nextoffset, saveoffset;
/*
* Lock the free list.
*/
if (writew_lock(db->idxfd, FREE_OFF, SEEK_SET, 1) < 0)
"_db_findfree: writew_lock error");
err_dump(
/*
* Read the free list pointer.
*/
saveoffset = FREE_OFF;
offset = _db_readptr(db, saveoffset);
while (offset != 0) {
nextoffset = _db_readidx(db, offset);if (strlen(db->idxbuf) == keylen && db->datlen == datlen)
break; /* found a match */
saveoffset = offset;
offset = nextoffset;
}
if (offset == 0) {
1; /* no match found */
rc = -else {
} /*
* Found a free record with matching sizes.
* The index record was read in by _db_readidx above,
* which sets db->ptrval. Also, saveoffset points to
* the chain ptr that pointed to this empty record on
* the free list. We set this chain ptr to db->ptrval,
* which removes the empty record from the free list.
*/
_db_writeptr(db, saveoffset, db->ptrval);0;
rc =
/*
* Notice also that _db_readidx set both db->idxoff
* and db->datoff. This is used by the caller, db_store,
* to write the new index record and data record.
*/
}
/*
* Unlock the free list.
*/
if (un_lock(db->idxfd, FREE_OFF, SEEK_SET, 1) < 0)
"_db_findfree: un_lock error");
err_dump(return(rc);
}
/*
* Rewind the index file for db_nextrec.
* Automatically called by db_open.
* Must be called before first db_nextrec.
*/
void
db_rewind(DBHANDLE h)
{
DB *db = h;
off_t offset;
1) * PTR_SZ; /* +1 for free list ptr */
offset = (db->nhash +
/*
* We're just setting the file offset for this process
* to the start of the index records; no need to lock.
* +1 below for newline at end of hash table.
*/
if ((db->idxoff = lseek(db->idxfd, offset+1, SEEK_SET)) == -1)
"db_rewind: lseek error");
err_dump(
}
/*
* Return the next sequential record.
* We just step our way through the index file, ignoring deleted
* records. db_rewind must be called before this function is
* called the first time.
*/
char *
char *key)
db_nextrec(DBHANDLE h,
{
DB *db = h;char c;
char *ptr;
/*
* We read lock the free list so that we don't read
* a record in the middle of its being deleted.
*/
if (readw_lock(db->idxfd, FREE_OFF, SEEK_SET, 1) < 0)
"db_nextrec: readw_lock error");
err_dump(
do {
/*
* Read next sequential index record.
*/
if (_db_readidx(db, 0) < 0) {
/* end of index file, EOF */
ptr = NULL; goto doreturn;
}
/*
* Check if key is all blank (empty record).
*/
ptr = db->idxbuf;while ((c = *ptr++) != 0 && c == SPACE)
/* skip until null byte or nonblank */
; while (c == 0); /* loop until a nonblank key is found */
}
if (key != NULL)
/* return key */
strcpy(key, db->idxbuf); /* return pointer to data buffer */
ptr = _db_readdat(db);
db->cnt_nextrec++;
doreturn:if (un_lock(db->idxfd, FREE_OFF, SEEK_SET, 1) < 0)
"db_nextrec: un_lock error");
err_dump(return(ptr);
}
测试代码
#include "apue.h"
#include "apue_db.h"
#include <fcntl.h>
int
void)
main(
{
DBHANDLE db;
if ((db = db_open("db4", O_RDWR | O_CREAT | O_TRUNC,
FILE_MODE)) == NULL)"db_open error");
err_sys(
if (db_store(db, "Alpha", "data1", DB_INSERT) != 0)
"db_store error for alpha");
err_quit(if (db_store(db, "beta", "Data for beta", DB_INSERT) != 0)
"db_store error for beta");
err_quit(if (db_store(db, "gamma", "record3", DB_INSERT) != 0)
"db_store error for gamma");
err_quit(
db_close(db);0);
exit( }
最后是makefile
ROOT=..
PLATFORM=$(shell $(ROOT)/systype.sh)
include $(ROOT)/Make.defines.$(PLATFORM)
LIBMISC = libapue_db.a
COMM_OBJ = db.o
ifeq "$(PLATFORM)" "solaris"
LDCMD=$(LD) -64 -G -Bdynamic -R/lib/64:/usr/ucblib/sparcv9 -o libapue_db.so.1 -L/lib/64 -L/usr/ucblib/sparcv9 -L$(ROOT)/lib -lapue db.o
EXTRALD=-m64 -R.
else
LDCMD=$(CC) -shared -Wl,-dylib -o libapue_db.so.1 -L$(ROOT)/lib -lapue -lc db.o
endif
ifeq "$(PLATFORM)" "linux"
EXTRALD=-Wl,-rpath=.
endif
ifeq "$(PLATFORM)" "freebsd"
EXTRALD=-R.
endif
ifeq "$(PLATFORM)" "macos"
EXTRALD=-R.
endif
all: libapue_db.so.1 t4 $(LIBMISC)
libapue_db.a: $(COMM_OBJ) $(LIBAPUE)
$(AR) rsv $(LIBMISC) $(COMM_OBJ)
$(RANLIB) $(LIBMISC)
libapue_db.so.1: db.c $(LIBAPUE)
$(CC) -fPIC $(CFLAGS) -c db.c
$(LDCMD)
ln -s libapue_db.so.1 libapue_db.so
t4: $(LIBAPUE)
$(CC) $(CFLAGS) -c -I. t4.c
$(CC) $(EXTRALD) -o t4 t4.o -L$(ROOT)/lib -L. -lapue_db -lapue
clean:
$(LIBMISC) t4 libapue_db.so.* *.dat *.idx libapue_db.so
rm -f *.o a.out core temp.*
include $(ROOT)/Make.libapue.inc
发表回复