KDbm Class Reference

[kdbm Index] [kdbm Hierarchy]


KDbm<T> provides a database based on char* keys and T* data More...

#include <kdbm.h>

Inherits: KGDbm

Public Members


Detailed Description

KDbm<T> provides a database based on char* keys and T* data.

An Introduction to the KDBM Package

The KDBM package is a C++ interface to the GNU database library (gdbm). This package includes the following classes: KGdbm, KGdbmIterator (abstract base classes), KDbm, KIntDbm (database classes), KDbmIterator and KIntDbmIterator (iterator classes).

The main reason to use KDBM in place of the gdbm native interface is, apart from the C++ interface, the safer memory management model that helps to prevent memory leaks (see section Memory Management).

KDbm is the class that should be used for databases based on char*, whereas KIntDbm should be used for databases based on long keys.

The following example shows how to create a database and store some items in it:

   KDbm db;

  db.create("mydata");

  db.insert("john", "scott");   db.insert("bill", "red");   db.insert("bob", "white");   db.insert("steve", "purple");

  db.close();

In the next example we open the database and perform some operations with the stored items:

   KDbm db;
   db.open("mydata");

  cout << db["john"] << endl;   cout << db["bill"] << endl;

  if (!db.exists("henry"))   cout << "henry does not exist!" << endl;

  db.replace("bob", "black");   db.delete("steve");

  db.close();

The GNU Database Library: Basic Concepts

The GNU database library (gdbm) is a library of database functions that use extendible hashing and works similar to the standard UNIX `dbm' functions. However, gdbm is not a complete database package for an end user. The basic use of `gdbm' is to store key/data pairs in a data file. Each key must be unique and each key is paired with only one data item. Both key and data items can be arbitrary sized.

The key/data pairs are stored in a `gdbm' disk file, called a `gdbm' database. An application must open a `gdbm' database to be able manipulate the keys and data contained in the database. `gdbm' allows an application to have multiple databases open at the same time. When an application opens a `gdbm' database, it is designated as a `reader' or a `writer'. A `gdbm' database opened by at most one writer at a time. However, many readers may open the database open simultaneously. Readers and writers can not open the `gdbm' database at the same time. For more details, see gdbm documentation.

Creating a Database:

Use the create method passing the name of the file to create. Alternatively, use the open method, having previously set the force option to true. In both cases, true is returned if the database has been created successfully and subsequent calls to isOpen will return true.

Example:

   db.create("test.db");
   if (!db.isOpen())
     cerr << "Error: can't create database" << endl;

the above code fragment is equivalent to:

   db.force = true;
   db.open("test.db");
   if (!db.isOpen())
     cerr << "Error: can't create database" << endl;

Opening a Database:

If the database does not exist, see the previous section.
If the database already exists, use the open method.
true is returned if the database has been opened successfully and subsequent calls to isOpen() will return true.

Retrieving Values:

The basic way to look up values is through the [] operator, using the appropriate key. Using take it is also possible to get the size of the retrieved item.

Example:

   db.insert("bob", "white");
   cout << "bob = " << db["bob"] << endl;
 
   int size;
   char* val = db.take("bob", size);

A NULL value is returned if there is no item associated to the given key. An alternative way to check if an item has been found is through the found function, as in the following example:

   char* val = db["notsure"];
   if (db.found())
     cout << "found 'notsure' = " << val << endl;
   else
     cout << "'notsure' not found" << endl;

Inserting and Replacing Records:

The method insert can be used to add new records to the database, specifying the key and the data to be associated with the key.
The key must be a string value for KDbm and a long value for KIntDbm.
The data can be either a reference or a pointer to T, where T is the type used to instanciate the KDbm (KIntDbm) template.
An optional third parameter can be used to specify the size of the data, which defaults to sizeof(T). KDbm<char> (KIntDbm<char>) are handled in a special way, as the default size is the size of the pointed string, assuming that is zero-terminated.

If no error occurs, the value 0 is returned. No action is taken if the key already exists in the database, and the value 1 is returned. If the item was not stored in the database because the caller was not an official writer, the value -1 is returned.

The method replace is similar to invoke, but it replaces the database if it already exist, otherwise works like insert.

Both insert and replace methods require that the database is open in read-write mode.

Memory Management:

KDbm and KIntDbm classes can be used in two modes.
In the first mode (the default one), a buffer is used to store values that are retrieved from the database. This way the user does not have to free the pointer to the retrieved item once it is no more needed. However, since the buffer is reused every time a new value is retrieved, the user must copy a value from the buffer in case it is needed for further computation. The second mode, with the buffer disabled, leaves to the user the responsibility to free the pointer to any retrieved item. This approach is more efficient in case many retrieved values must be kept (e.g., to store them in a memory table), but the user must be careful to avoid memory leaks.

The two rules concerning memory management are:

(a) you can always free pointers passed to KDBM classes and,
(b) unless you have explicitly disabled the buffer, you should never release memory returned by KDBM functions.

Methods related to buffer management are: disableBuffer, enableBuffer, bufferIsEnabled, resizeBuffer.

The following example shows a case in which a buffer is useful to make the code more compact:

   cout << "bob = " << db["bob"] << endl;
   cout << "bill = " << db["bill"] << endl;
   cout << "steve = " << db["steve"] << endl;

In the next example, instead, the buffer is disabled since the pointers to the retrieved items are pushed onto a stack, so we don't want that the memory area in which the items are stored is reused:

   db.disableBuffer();

  for (int i = 0; i < 100; i++) {   char* key = keys[i];   stack.push(db[key]);   }

Once the items are no more needed, the user must free them. Alternatively, the above code could be written with the buffer enabled in the following way:

  for (int i = 0; i < 100; i++) {
    char* key = keys[i];
    char* item = strdup(db[key]);
    stack.push(item);
  }

Note that the above code is less efficient, because each item is copied in a newly allocated ared in the call to strdup().

Variable-length Structures:

When a new item is inserted into a KDbm database, the default size assumed for insert and replace is sizeof(T). To store variable size structures, the insert and replace methods can be called with an optional third parameter, corresponding to the actual size of the item that must be stored. To look up a given key, take should be used rather than the [] operator. Example:

   typedef struct VarObjStruct {
     int a;
     int count;
     char var[0];    // `var' can have an arbitrary length
   } VarObj;

  VarObj* item = (VarObj*) malloc(sizeof(VarObj) + 20);   ...   db.insert("varsize", item, sizeof(VarObj) + 20);

Design Considerations:

The interface for KGDbm, KDbm, ... classes resembles as much as possible the interface of QGDict, QDict, ... (Qt dictionary), for coherence with KDE design. However, the KDBM package is not depending on any other KDE file, thus it can be used without any problem for other projects.
Efficiency and a simple interface have been primary design goals.
For comments and suggestions, please e-mail me ( Pietro Iglio).


KDbm()

Default constructor.

bool exists(char* _key)

Return true if the given key exists in the database, false otherwise.

int insert(char* _key, T* _val, const int _size = sizeof(T)

Insert a new record into the database.

See Inserting and Replacing Records.

int insert(char* _key, T& _val, const int _size = sizeof(T)

Insert a new record into the database. See Inserting and Replacing Records.

int replace(char* _key, T* val, const int _size = sizeof(T)

Replace a record in the database.

See Inserting and Replacing Records.

int replace(char* _key, T& val, const int _size = sizeof(T)

Replace a record in the database.

See Inserting and Replacing Records.

T* operator[](char* _key)

Look up a given `key' and return the information associated with that key. NOTE: for T is a variable-length structure, you should use take.

T* take(char* _key, int& _size)

Like [], but returns puts in `size' the size of the retrieved item. Useful for variable-length structures, since the size of the returned item may vary.

bool remove(char* _key)

Remove the item associated with 'key' from the database.

Returns:
true if the item was removed successfully, false otherwise (for example, if the item does not exist).
See Also:
exists

  • Author: Pietro Iglio (iglio@kde.org)
  • Documentation generated by iglio@localhost on Thu Oct 15 19:05:38 GMT 1998
Kdoc