僕の主戦場はもっぱらWindowsですけど、Linux由来のコードやライブラリのお世話になることも少なくありません。先日もファイルに保存可能な辞書(key-value store)を探してて、GDBM:GNU Database ManagerのWindows版を見つけました。マジなDatabase使うほど大層なものではなく、キーと値の組をファイルに保存し、キーから値が検索できれば十分というシチュエーションだったので、このWindows-port版GDBM、ありがたく使わせていただきました。
GDBMをハダカで使う
GDBMのトリセツはman pageを一読していただくことにして、まずはそのまま、C-interfaceでお試しコードを書いてみましょう。
#include <iostream>
#include <cassert>
#include <array>
#include <cstring> // strlen
#include <cstdlib> // free
#define GDBM_STATIC
#include <gdbm.h>
using namespace std;
void store() {
GDBM_FILE dbf = gdbm_open("trial.db", 512, GDBM_NEWDB, 0644, 0);
assert( dbf != nullptr );
array<const char*,4> keys = { "apple", "orange", "grape", "apple" };
array<const char*,4> contents = { "まっく", "みかん", "ぶどう", "りんご" };
datum key;
datum content;
for ( size_t i = 0; i < keys.size(); ++i ) {
// キーと値をdatumにセット
key.dptr = const_cast<char*>(keys[i]);
key.dsize = strlen(keys[i]) + 1;
content.dptr = const_cast<char*>(contents[i]);
content.dsize = strlen(contents[i]) + 1;
// キーと値のペアを登録
// ※ GDBM_REPLACEにより、appleに紐づく値は
// ("まっく"から"りんご"に)上書きされます
int result = gdbm_store(dbf, key, content, GDBM_REPLACE);
assert( result == 0 );
}
gdbm_close(dbf);
cout << "3 entries stored." << endl;
}
void fetch() {
GDBM_FILE dbf = gdbm_open("trial.db", 512, GDBM_WRCREAT, 0644, 0);
assert( dbf != nullptr );
array<const char*,4> keys = { "apple", "orange", "grape", "peach" };
datum key;
datum content;
for ( const char* target : keys ) {
// キーをdatumにセットし、
key.dptr = const_cast<char*>(target);
key.dsize = strlen(target) + 1;
// キーに紐づいた値を検索
content = gdbm_fetch(dbf, key);
if ( content.dptr == nullptr ) {
cout << target << " not found..." << endl;
} else {
cout << target << " = " << content.dptr << endl;
free(content.dptr);
}
}
// Database内の全エントリを列挙する
cout << "--- list all entries ---" << endl;
key = gdbm_firstkey(dbf);
while ( key.dptr != nullptr ) {
char* tmp = key.dptr;
content = gdbm_fetch(dbf, key);
cout << key.dptr << " = " << content.dptr << endl;
free(content.dptr);
key = gdbm_nextkey(dbf, key);
free(tmp);
}
}
int main() {
store();
fetch();
}
関数 store()ではキーと値のペアの登録、fetch()ではキーに紐づいた値の検索と、全エントリの列挙を行っています。GDBMが扱うデータはキー/値のいずれもデータの先頭アドレスとデータの長さ、すなわちバイト列を対象としていますから、(ここでは文字列をキー/値としていますけど)任意の型をキー/値として利用できます。気をつけなきゃならないのは、GDBMから帰ってきたdatum内のdptrが指すのはmalloc()で確保されたメモリ・ブロックであり、これをfree()するのは利用者の責任であることです。free()を忘れると少しずつメモリを食いつぶすことになります。
