pdb Documentation
The P Database |
v0.1 |
Contents
|
pdb Structures
A database structure struct pdb { struct pdb_node_t* data; /* the root node */ char* file; /* the file name of the loaded database */ int altered; /* was the database altered since last write? */ long last_write; /* the time of the last disk write */ int write_interval; /* in seconds */ int settings; /* the enabled settings */ pthread_mutex_t* mutex; /* local mutex lock for the thread safe feature */ }; A database node struct pdb_node_t { int type; /* the type of node it is */ char* id; /* the node id (or key) */ void* data; /* the data stored at this node */ struct pdb_node_t* parent; /* the parent of this node */ custom_free_cb_t custom_free_cb; /* custom free callback */ }; |
pdb File Format
pdb stores its structure to disk in a tree-like format. Each string literal is encapsulated within two double quotes, such that the string literal foo is represented in the file as "foo". A token is a string of characters which defines a new node that exists within the database. For example, code section 1.0 is a token.
|
pdb_load()
|
|
Prototype |
struct pdb* pdb_load(char* file); |
Description |
Create and load a database into memory. |
Arguments |
file - The database file to be loaded. If NULL, the database structure will be initialized as if a file was specified and was empty. |
Return |
A pointer to the created database structure. |
Example |
struct pdb* dbptr = pdb_load("weapons.pdb"); int* glock_clip_size = pdb_query(dbptr, "handheld/glock/clip_size"); printf("The glock clip size is: %i", (glock_clip_size ? *glock_clip_size : 0)); |
pdb_unload()
|
|
Prototype |
int pdb_unload(struct pdb* dbptr); |
Description |
Unload the specified database structure from memory. All children will be recursively unloaded. |
Arguments |
dbptr - The database structure to be unloaded from memory. |
Return |
1 on success 0 on failure. |
Example |
struct pdb* dbptr = pdb_load("weapons.pdb"); /* * If this database does not have the key "weap_set", unload it. * Otherwise, output the value (it is stored as an integer in the db). */ int* set = pdb_query(dbptr, "weap_set"); if (set) printf("Weap_set is: %i\n", *set); else printf("Unload database... %s\n", (pdb_unload(dbptr) ? "success" : "failed")); |
pdb_enable()
|
|
Prototype |
void pdb_enable(struct pdb* dbptr, int settings); |
Description |
Enable a pdb setting. Valid settings are: PDB_CASE_INSENSITIVE Allow case insensitive key searching. Ex: pdb_query(dbptr, "FoO/bar"); and db_query(dbptr, "fOO/BAR"); are equivalent. PDB_WRITE_SHUFFLE Attempt to shuffle the children of supported data containers before writing to disk. PDB_WRITE_NODE_FIRST Attempt to write PDB_WRITE_NODE_FIRST_ID (defined in pdb.h) at the top any parent block if it exists. Generally this should not be set as the key id is hardcoded. PDB_THREAD_SAFE Enable thread safe mode. pdb will use a local mutex in attempt to keep the structre safe if shared memory may be in use. This should be enabled if the application uses threading. |
Arguments |
dbptr - A database pointer. settings - The setting(s) to be enabled. Settings may be OR'ed togther. |
Return |
No return. |
Example |
struct pdb* dbptr = pdb_load("users.pdb"); void* john = pdb_query(dbptr, "carmack/john"); void* carmack = pdb_query(dbptr, "caRMacK/JoHN"); if (john == carmack) { /* * This will always be true if PDB_CASE_INSENSITIVE is set. */ printf("PDB_CASE_INSENSITIVE must be set!\n"); } else { /* * PDB_CASE_INSENSITIVE is not set, lets set it and try again. */ pdb_enable(dbptr, PDB_CASE_INSENSITIVE); john = pdb_query(dbptr, "carmack/john"); carmack = pdb_query(dbptr, "caRMacK/JoHN"); if (john == carmack) { /* * This will always be true. */ printf("PDB_CASE_INSENSITIVE is now set!\n"); } else { /* * This will never be true. */ printf("You will never see this line.\n"); } } |
pdb_disable()
|
|
Prototype |
void pdb_disable(struct pdb* dbptr, int settings); |
Description |
Disable a pdb setting. See pdb_enable() for valid settings. |
Arguments |
dbptr - A database pointer. settings - The setting(s) to be disabled. Settings may be OR'ed togther. |
Return |
No return. |
Example |
struct pdb* dbptr = pdb_load("users.pdb"); /* * We are using pdb in an application which does not need shared memory * management (ie not using threads), so lets disable that option, but only * if it is already set. */ if (pdb_is_set(dbptr, PDB_THREAD_SAFE)) pdb_disable(dbptr, PDB_THREAD_SAFE); |
pdb_is_set()
|
|
Prototype |
int pdb_is_set(struct pdb* dbptr, int setting); |
Description |
Determine if the specified flag(s) are set for the specified database. |
Arguments |
dbptr - A database pointer. settings - The setting(s) to be checked. Settings may be OR'ed togther. |
Return |
1 if all specified settings are enabled. 0 if one or more specified settings are disabled. |
Example |
struct pdb* dbptr = pdb_load("users.pdb"); /* * We need to query the database for foobar, and we do not know the exact * case in which it is stored, but we still need it (ie, it may be FOObar). */ pdb_enable(dbptr, PDB_CASE_INSENSITIVE); void* f = pdb_query(dbptr, "foobar"); /* * For future queries we want the case to be sensitive, so lets disable * case insensitive searching. */ pdb_disable(dbptr, PDB_CASE_INSENSITIVE); |
pdb_create_link()
|
||
Prototype |
int pdb_create_link(struct pdb* dbptr, char* path, char* key, struct pdb_node_t* tnptr); |
|
Description |
Similar to a soft link on a Unix machine, this function will create a node which points to the specified (tnptr) node. The target node (tnptr) does not have to be in the specified database; it may reside in another loaded database. This can be used to link an entire database into some other database, or just reference one node to another.
|
|
Arguments |
dbptr - A database pointer. path - The path (excluding the key) to where the link should be made. key - The link name (this will be stored at path/key in the database). tnptr - The node in which the created link will point to. |
|
Return |
1 if the link was successfully created. 0 if the link could not be created. |
|
Example |
struct pdb* dbptr = pdb_load("users.pdb"); struct pdb_node_t* richard = pdb_query_node(dbptr, "names/army/richard"); /* * While Richard is his legal name, we like to call him Dick. * We make a link from names/army/richard to names/army/dick so * we can query either and get the same node. */ if (pdb_create_link(dbptr, "names/army", "dick", richard)) { /* * The link was successfully added. * To make sure lets query names/army/dick and compare it with * the richard pointer, which was names/army/richard. */ struct pdb_node_t* dick = pdb_query_node(dbptr, "names/army/dick"); if (dick == richard) { printf("names/army/dick points to names/army/richard\n"); } else { /* * This will never be true. */ printf("You will never see this line.\n"); } } else { /* * We were unable to create the link. */ printf("Link failed.\n"); } |
pdb_query_node()
|
||
Prototype |
struct pdb_node_t* pdb_query_node(struct pdb* dbptr, char* path); |
|
Description |
Search the specified database structure for the specified node. Each key in the path is delimited by a slash (/). Therefore, if there is a node in the root called A, a node in A called B, and a node in B called C, the path to C is: A/B/C
|
|
Arguments |
dbptr - A database pointer. path - The complete path to the node. |
|
Return |
A pointer to the specified node. If the node was not found, NULL will be returned. |
|
Example |
struct pdb* dbptr = pdb_load("users.pdb"); /* * Is there a John Doe in this database? * * Assume the database file looks like: * * "names" { * "foo" { * "bar1" "a"; * "bar2" "b"; * } * "john" { * "doe" "a"; * "dime" "b"; * } * } * */ struct pdb_node_t* john = pdb_query_node(dbptr, "names/john/doe"); if (john) { printf("John Doe's node exists at memory location %x.\n", john); } else { printf("John Doe is not in the database at that location."); } |
pdb_query()
|
||
Prototype |
void* pdb_query(struct pdb* dbptr, char* path); |
|
Description |
Similar to pdb_query_node(), but return a pointer to the data at that node rather than a pointer to the node itself.
|
|
Arguments |
dbptr - A database pointer. path - The complete path to the node. |
|
Return |
A pointer to the specified node's data. If the node was not found, NULL will be returned. |
|
Example |
struct pdb* dbptr = pdb_load("users.pdb"); /* * What is John Doe's age? * * Assume the database file looks like: * * "names" { * "john" { * "doe" '35'; * "dime" '14'; * } * } * */ int* age = (int*)pdb_query(dbptr, "names/john/doe"); if (age) printf("John Doe is %i years old.\n", *age); else printf("John Doe is not in the database at that location.\n"); |
pdb_set()
|
|
Prototype |
struct pdb_node_t* pdb_set(struct pdb* dbptr, char* path, char* key, void* data); |
Description |
Create a node at the location path/key in the specified database. The type of node created will be of the string container type. The void pointer should be a pointer to the string which you want to set the database entry to. This pointer does not have to be allocated, but if it is it can be subsequently free'ed after pdb_set() completes, as pdb_set() will create a copy of it in memory for its own use. If you wish to create a container of a type other than string, use pdb_set_node(). There are also undocumented wrappers for various types defined in pdb_types.c. These may include: struct pdb_node_t* pdb_create_tree(struct pdb* dbptr, char* path, char* key); struct pdb_node_t* pdb_create_list(struct pdb* dbptr, char* path, char* key); struct pdb_node_t* pdb_create_hash(struct pdb* dbptr, char* path, char* key, int size); struct pdb_node_t* pdb_create_abstract(struct pdb* dbptr, char* path, char* key, void* data, void* free_cb); struct pdb_node_t* pdb_set_int(struct pdb* dbptr, char* path, char* key, int data); |
Arguments |
dbptr - A database pointer. path - The path (excluding the key) to the node that should be set. key - The node name (this will be stored at path/key in the database). data - A void pointer to the data that should be stored at that node. |
Return |
A pointer to the node which was created. If an error occured, NULL will be returned. |
Example |
struct pdb* dbptr = pdb_load("websites.pdb"); /* * We want to create a node in the database's root called "slashdot" * which has the data "http://www.slashdot.org/". */ struct pdb_node_t* sd = pdb_set(dbptr, "/", "slashdot", "http://www.slashdot.org/"); if (sd) { /* * For confirmation it was added properly (even though we know it is as the * return was non-NULL, query it and compare the pointers. */ struct pdb_node_t* sd2 = pdb_query_node(dbptr, "slashdot"); if (sd == sd2) printf("Indeed, it was added corrently, and the data is \"%s\"\n", (char*)sd2->data); else printf("You will never see this line.\n"); } else { /* * The node could not be set. */ printf("An error occured and the node could not be set.\n"); } |
pdb_set_node()
|
||
Prototype |
struct pdb_node_t* pdb_set_node(struct pdb* dbptr, char* path, char* key, void* data, int type); |
|
Description |
Create a node at the location path/key in the specified database. The type of node created will be of the type container type. Valid types are: TREE_NODE_TYPE A binary tree. LIST_NODE_TYPE A linked list. HASH_NODE_TYPE A hash table. STRING_NODE_TYPE A string. INT_NODE_TYPE An integer. ABSTRACT_NODE_TYPE An abstract type. This can be any type of data you desire. See the note below for important information about this container type. LINK_NODE_TYPE A node which points to another node. For additional information on this container type, see pdb_create_link(). It is important to note that the following container types data should not be manually unallocated after it has been set -- pdb will handle the deallocation of this memory when the database is unloaded, or the node is free'd: ABSTRACT_NODE_TYPE LINK_NODE_TYPE If you are creating one of the following container types, the void pointer data should be NULL: TREE_NODE_TYPE LIST_NODE_TYPE HASH_NODE_TYPE
|
|
Arguments |
dbptr - A database pointer. path - The path (excluding the key) to the node that should be set. key - The node name (this will be stored at path/key in the database). data - A void pointer to the data that should be stored at that node. type - The type of container to create. |
|
Return |
A pointer to the node which was created. If an error occured, NULL will be returned. |
|
Example |
struct pdb* dbptr = pdb_load("websites.pdb"); /* * Create a binary tree in the database root called "foo_tree". */ struct pdb_node_t* bt = pdb_set_node(dbptr, "/", "foo_tree", NULL, TREE_NODE_TYPE); /* * Create a hash table within the binary tree we just created called "hash". */ struct pdb_node_t* ht = pdb_set_node(dbptr, "foo_tree", "hash", NULL, HASH_NODE_TYPE); /* * Finally create a string node within the hash table we just created * called "foo" with the data "bar". */ pdb_set(dbptr, "foo_tree/hash", "foo", "bar"); /* * Oops, we forgot to store the location of the "foo" node we just created. * Now we need the data at that node, use pdb_query() to obtain it. */ char* d = pdb_query(dbptr, "foo_tree/hash/foo"); printf("We set \"foo_tree/hash/foo\" to \"%s\"\n", d); |
pdb_del()
|
|
Prototype |
int pdb_del(struct pdb* dbptr, char* path); |
Description |
Delete the node at the location path/key in the specified database. This will also recursively free all children of that node (if it has any), and the data contained at that location. |
Arguments |
dbptr - A database pointer. path - The path (including the key) to the node that should be deleted. |
Return |
1 if the node was successfully deleted from memory. 0 if an error occured while attempting to delete the node. |
Example |
struct pdb* dbptr = pdb_load(NULL); /* * Assume this database structure is completely empty (this is evident because * NULL was passed to pdb_load() for the file name), it has nothing in it. * Let us create a string node in the root called "slashdot" with the data * "http://www.slashdot.org/". */ pdb_set(dbptr, "slashdot", "http://www.slashdot.org/"); /* * Query the node and output the data. If printf() says the data is NULL, * we can conclude the above function call failed. */ printf("Data is \"%s\".\n", pdb_query(dbptr, "slashdot")); /* * Now delete it. * Even if pdb_set() failed, we can still safely attempt to delete it, * pdb should return 0 telling us it could not find the node to be deleted. * But we assume that it was added successfully, and we should get a 1 returned * on this line. */ int ret = pdb_del(dbptr, "slashdot"); printf("pdb_del() returned %i.\n", ret); |
pdb_trace()
|
||
Prototype |
char* pdb_trace(struct pdb_node_t* nptr); |
|
Description |
This function accepts a database node pointer and will return an allocated string which holds the full path to that node, relative to the database root. For example, assume the database root has a node called A, A has a node called B, B has a node called C, and C has a node called D. If pdb_trace() is called with a pointer to the node D, pdb_trace() will return the string: A/B/C/D
|
|
Arguments |
nptr - A database node pointer. |
|
Return |
The full path to the specified node relative to the database root where it resides. |
|
Example |
struct pdb* dbptr = pdb_load("bork.pdb"); /* * Assume bork.pdb looks like: * * "a" { * "b" { * "c" { * "d" { * "e" '1'; * } * } * } * } * * We have a pointer to "e", but we want to know * exactly where in the database it resides. */ struct pdb_node_t* eptr = 0xDEADBEEF /* we already have this */ char* path = pdb_trace(dbptr, eptr); /* * The following line will output: * The path to e is "/a/b/c/d/e". * * The preceeding slash is acceptable, it just means * a is in the root. This is the same as "a/b/c/d/e". */ printf("The path to e is \"%s\".\n", path); /* * Be sure to free this! */ free(path); |
pdb_set_write_interval()
|
||
Prototype |
void pdb_set_write_interval(struct pdb* dbptr, int seconds); |
|
Description |
Databases can be written to disk on given intervals. This function allows you to set the time in seconds between these writes.
|
|
Arguments |
dbptr - A database pointer. seconds - The number of seconds between write intervals. |
|
Return |
No return. |
|
Example |
struct pdb* dbptr = pdb_load("websites.pdb"); /* * Set the database write interval to 2 minutes (120 seconds). */ pdb_set_write_interval(dbptr, 120); |
pdb_need_write()
|
|
Prototype |
int pdb_need_write(struct pdb* dbptr); |
Description |
Determine if it has been X seconds since the last disk write of the specified database, and if the database requires a write due to a change; both of these conditions must be met for this function to return 1. X is either 120 seconds (defined by PDB_DEFAULT_WRITE_INTERVAL in pdb.h), or whatever was set with pdb_set_write_interval(). |
Arguments |
dbptr - A database pointer. |
Return |
1 if the specified database requires a disk write. 0 if no disk write is needed. |
Example |
struct pdb* dbptr = pdb_load("websites.pdb"); /* * If it has been 120 seconds since the last write, and the * database was changed, write it. */ if (pdb_need_write(dbptr)) pdb_write(dbptr, "/home/XXX/websites.pdb"); /* * Now add a node called "foo" to the root. */ if (pdb_set(dbptr, "foo", "bar")) { /* * The database was changed, wait 120 seconds. */ sleep(120); if (pdb_need_write(dbptr)) { /* * This will be true, unless the write interval was changed to something * longer than 120 seconds via pdb_set_write_interval(). * Write the database again. */ pdb_write(dbptr, "/home/XXX/websites.pdb"); } else { /* * This will only occur if the write interval is longer than 120 seconds. */ } } else { /* * The database was not changed, if we wait 120 seconds, assuming there are no * changes during that time, no write will be needed. */ sleep(120); printf("Write needed? %i\n", pdb_need_write(dbptr)); /* should be 0 */ } |
pdb_write()
|
||
Prototype |
int pdb_write(struct pdb* dbptr, char* file); |
|
Description |
Write the specified database to the specified file. This can be done at any time, and it is not required to wait X seconds to write to disk. It is a good idea, however, to use pdb_need_write() to check if a disk write is needed, otherwise there will be wasted cycles.
|
|
Arguments |
dbptr - A database pointer. file - The file to write the database to. |
|
Return |
1 if the disk write was successful. 0 if the disk write failed. |
|
Example |
struct pdb* dbptr = pdb_load("websites.pdb"); /* * Add "foo" to the root and save the altered database to disk. * Note that here we are forcing a write, as we are not checking * if a write is actually needed with pdb_need_write(). * See pdb_need_write() if you wish to see an example of this. */ pdb_write(dbptr, "websites.pdb"); |
pdb_set_free_method()
|
||
Prototype |
void pdb_set_free_method(struct pdb* dbptr, struct pdb_node_t* nptr, void* free_cb); |
|
Description |
From time to time it may be necessary use a custom function to free a given database node. This is especially useful for abstract node types (ABSTRACT_NODE_TYPE). The callback should be in the form of: void free_cb(void* dptr); That is, a function which returns void, and accepts only one parameter, a void pointer. The function should do whatever necessary to deallocate the data at the pointer. This function will be invoked by pdb when the set node is deleted, and the parameter passed to the callback will be the data set at that node.
|
|
Arguments |
dbptr - A database pointer. nptr - The node to set the free method for. free_cb - A callback to free the data. |
|
Return |
No return. |
|
Example |
Assume this structure is defined: struct foo_t { char* a; char* b; }; Assume this function is defined: void free_foo(void* dptr) { struct foo_t* f = dptr; free(f->a); free(f->b); free(f); } Example struct foo_t* d = (struct foo_t*)malloc(sizeof(struct foo_t)); d->a = strdup("foo"); d->b = strdup("bar"); struct pdb* dbptr = pdb_load("asdf.pdb"); /* * Create an abstract node called "foo" in the database root. */ struct pdb_node_t* bt = pdb_set_node(dbptr, "/", "foo", d, ABSTRACT_NODE_TYPE); /* * If the next line is never called, there will be three memory leaks. * One for not deallocating d->a, one for d->b, and finally one for d itself. */ pdb_set_free_method(dbptr, bt, (void*)&foo_free); /* * Unload the database. * When pdb unloads the "foo" node, it will call free_foo with a pointer that points * to the structure which it was set to (in this case "d"). */ pdb_unload(dbptr); |
pdb_count_children()
|
|
Prototype |
int pdb_count_children(struct pdb* dbptr, char* path); |
Description |
Count the number of children the specified node has. Note that this is not recursive, it will only return the number of chilren the specified node has, not the specified node's number of children in addition to all the childrens children. |
Arguments |
dbptr - A database pointer. path - The path (including the key) to the node that has the children to be counted. |
Return |
The number of children a node has. |
Example |
struct pdb* dbptr = pdb_load("websites.pdb"); /* * Assume websites.pdb looks like: * * "http" { * "slashdot" "slashdot.org"; * "google" "google.com"; * "misc" { * "qmm" "quake3mm.net"; * "finop" "finop.net"; * } * } * * The following lines will output: * "http" has 3 children. * "slashdot", "google", and "misc" are counted. */ int c = pdb_count_children(dbptr, "http"); printf("\"http\" has %i children.\n", c); |
pdb
was written by Michael Laforest (Para) <paralizer -AT- users -DOT- sourceforge -DOT- net> Copyright 2005 Michael Laforest, all rights reserved. pdb is released under the GPL. |