dyndb - a database library for multiple readers and one writer
Multiple processes may use the same database, provided that they are carefully using some kind of locking.
See limitations for a list of limitations.
dyndb_create_name or
dyndb_create_fd create a
database.
dyndb_init initializes the
management information on an existing database.
dyndb_enter,
dyndb_enterstart,
dyndb_enteraddkey,
dyndb_enteradddata and
dyndb_enterfinish enter new
information.
dyndb_lookup respectively
dyndb_lookupstart and
dyndb_lookupnext allow to
search for information by a key.
dyndb_walkstart and
dyndb_walknext allows to walk
through all records of the database.
dyndb_fwalk provides an
alternative interface.
dyndb_delete deletes the record
found by the latest call to dyndb_lookupnext.
This structure describes the internal state of the library. Please treat it as opaque data type. Exception: you may access the fd member _readonly_.
is a 32bit unsigned integer. On many systems you can use
#include <inttypes.h> #define uint32 uint32_t
to get a definition. Do not use unsigned int or unsigned long, unless you do not need portability of your source.
is a 32bit signed integer. On many systems you can use
#include <inttypes.h> #define int32 int32_t
to get a definition. Do not use int or long, unless you do not need portability of your source.
To use the library in your program you have to include dyndb.h inside your C sources (C++ is not supported, it may or may not work), and link a few functions to your binaries. The source files names of the library functions match dyndb_*.c (a wild card, not a regular expression).
dyndb.h
includes uint32.h and int32.h, which are
expected to define uint32 and int32. The two files included
in the distribution, which you might want to replace if they
don't fit into your framework, work this way:
They include typesize.h, which in turn includes
typesize2.h, a file created at compile time, if if
`HAVE_CONFIG_H' is not defined. Otherwise, if
`HAVE_CONFIG_H' is defined, you have either define a
few constants or include config.h before you include
dyndb.h.
Back when i used GNU autoconf I used the following magic:
AC_CHECK_SIZEOF(unsigned short,2) AC_CHECK_SIZEOF(short,2) AC_CHECK_SIZEOF(int,4) AC_CHECK_SIZEOF(unsigned int,4) AC_CHECK_SIZEOF(long,4) AC_CHECK_SIZEOF(unsigned long,4) AC_CHECK_SIZEOF(long long,0) AC_CHECK_SIZEOF(unsigned long long,0)
If `HAVE_CONFIG_H' is not defined then typesize2.h has to define the following C macros, possibly to other values:
#define SIZEOF_SHORT 2 #define SIZEOF_UNSIGNED_SHORT 2 #define SIZEOF_INT 4 #define SIZEOF_UNSIGNED_INT 4 #define SIZEOF_LONG 4 #define SIZEOF_UNSIGNED_LONG 4 #define SIZEOF_LONG_LONG 8 #define SIZEOF_UNSIGNED_LONG_LONG 8
You have to ensure that no other process enters data into dy during the call to dyndb_enter.
You have to ensure that no other process enters data into dy between the calling of dyndb_enterstart and the end of the dyndb_enterfinish call.
You must call dyndb_enteraddkey only after a successful call to dyndb_enterstart. You must follow dyndb_enteraddkey with either one or more calls to dyndb_enteradddata or a call to dyndb_enterfinish.
You must call dyndb_enteradddata only after the key has been entered. You may calldyndb_enteradddata multiple times. You must call dyndb_enterfinish to actually add the record to the database index.
dyndb_fwalk will return 0 if callback has been called for all elements of the database, -1 in case of an error, and any possible integer value callback returned.
dyndb_fwalk finds every record in the database at least once. It finds every record exactly once if noone else is writing to the database at that moment, and may find records more than once if a table split happens during the walk through the table.
An alternative way to read all elements is the dyndb_walk interface. The main difference between the two is that dyndb_walk is slower, has a more clean API and is likely to provide more useful results in case another process writes to the database during the walk, while dyndb_fwalk is faster.
dyndb_init returns 0 in case of success or -1 in case of error.
dyndb_init does not lock db. If this is needed then the application has to do this before the call to dyndb_init.
You may call dyndb_init on the same dy more than once. You have to close fd yourself.
The data is .intlink dyndb_datalen(dy) bytes long. It may be read with dyndb_read either directly after the dyndb_lookupnext call or later after the file descriptor has been moved to the position returned by dyndb_datapos(dy), using the dyndb_seek function.
This example retrieves all records with a given key from the DB on the standard input and prints them, separated by a space, to the standard output:
#include "dyndb.h"
void error(void) { _exit(1); }
int main(int argc, char **argv)
{
struct dyndb dy;
if (argc<2) error();
if (-1==dyndb_init(&dy,0)) error();
dyndb_lookupstart(&dy);
while (1) {
int r;
int32 l;
r=dyndb_lookupnext(&dy,argv[1],strlen(argv[1]));
if (-1==r)
error();
if (!r)
break; /* no further records */
/* read and print data */
for (l=dyndb_datalen(&dy); l ; ) {
char buf[128];
int32 got;
got=dyndb_read(dy.fd,buf, l > sizeof(buf) ? sizeof(buf) : l);
if (-1==got) error();
write(1,buf,got);
l-=got;
}
write(1,"0,1);
}
return 0;
}
The function returns -1 in case of error and zero if successful.
If an element is found then key and data may be read, in that order, from dy->fd. dyndb_keylen returns to length of the key, and dyndb_datalen returns the length of the data. In case you need to rewind the file descriptor to the old position then dyndb_keypos returns the position of the key and dyndb_datapos returns the position of the data (key position + key length).
dyndb_walknext finds every record in the database at least once. It finds every record exactly once if noone else is writing to the database at that moment, and may find records more than once if a table split happens during the walk through the table.
This example dumps all keys to the standard output:
struct dyndb dy;
if (-1==dyndb_init(&dy,0)) error();
if (-1==dyndb_walkstart(&dy)) error();
while (1) {
int r;
uint32 l;
r=dyndb_walknext(&dy);
if (-1==r)
error();
if (!r)
break; /* no further records */
/* read and print the key */
for (l=dyndb_keylen(&dy); l ; ) {
char buf[128];
int32 got;
got=dyndb_read(dy.fd,buf, l > sizeof(buf) ? sizeof(buf) : l);
if (-1==got)
error();
write(1,buf,got);
l-=got;
}
write(1,"0,1);
/* could do the same for the data, using dyndb_datalen() */
}
Uwe Ohse, uwe@ohse.de