00001
00002
00003
00004
00005
00006 #ifndef HASHDB_H
00007 #define HASHDB_H
00008
00009 #include <boost/concept_check.hpp>
00010
00011 #include <cstdio>
00012 #include <cstring>
00013 #include <string>
00014
00015 #include <db.h>
00016
00017 #include "Benzene.hpp"
00018 #include "Types.hpp"
00019 #include "Hash.hpp"
00020 #include "BenzeneException.hpp"
00021
00022 _BEGIN_BENZENE_NAMESPACE_
00023
00024
00025
00026
00027 template<class T>
00028 struct PackableConcept
00029 {
00030 void constraints()
00031 {
00032 const T t;
00033 int size = t.PackedSize();
00034 size = 42;
00035 byte* d = t.Pack();
00036
00037 T a = t;
00038 a.Unpack(d);
00039 }
00040 };
00041
00042
00043 template<class T>
00044 struct HashDBStateConcept
00045 {
00046 void constraints()
00047 {
00048 boost::function_requires< boost::DefaultConstructibleConcept<T> >();
00049 boost::function_requires< boost::AssignableConcept<T> >();
00050 boost::function_requires< PackableConcept<T> >();
00051 }
00052 };
00053
00054
00055
00056
00057 template<class T>
00058 class HashDB
00059 {
00060 BOOST_CLASS_REQUIRE(T, benzene, HashDBStateConcept);
00061
00062 public:
00063
00064 HashDB(const std::string& filename, const std::string& type);
00065
00066
00067 ~HashDB();
00068
00069
00070 bool Exists(hash_t hash) const;
00071
00072
00073 bool Get(hash_t hash, T& data) const;
00074
00075
00076 bool Put(hash_t hash, const T& data);
00077
00078
00079 bool Put(void* k, int ksize, void* d, int dsize);
00080
00081
00082 bool Get(void* k, int ksize, void* d, int dsize) const;
00083
00084
00085 void Flush();
00086
00087
00088 std::string BDBStatistics();
00089
00090 private:
00091
00092 static const int PERMISSION_FLAGS = 0664;
00093
00094 static const int CLOSE_FLAGS = 0;
00095
00096 struct Header
00097 {
00098 static const int MAX_LENGTH = 32;
00099
00100 char m_type[MAX_LENGTH];
00101
00102 Header()
00103 {
00104 memset(m_type, 0, MAX_LENGTH);
00105 }
00106
00107 Header(const std::string& type)
00108 {
00109 strncpy(m_type, type.c_str(), MAX_LENGTH - 1);
00110 m_type[MAX_LENGTH - 1] = 0;
00111 }
00112
00113 bool operator==(const Header& other) const
00114 {
00115 return strcmp(m_type, other.m_type) == 0;
00116 }
00117
00118 bool operator!=(const Header& other) const
00119 {
00120 return !operator==(other);
00121 }
00122 };
00123
00124 DB* m_db;
00125
00126
00127 std::string m_filename;
00128
00129 bool GetHeader(Header& header) const;
00130
00131 void PutHeader(Header& header);
00132 };
00133
00134 template<class T>
00135 HashDB<T>::HashDB(const std::string& filename, const std::string& type)
00136 : m_db(0),
00137 m_filename(filename)
00138 {
00139 int ret;
00140 if ((ret = db_create(&m_db, NULL, 0)) != 0)
00141 {
00142 fprintf(stderr, "db_create: %s\n", db_strerror(ret));
00143 throw BenzeneException("HashDB: opening/creating db!");
00144 }
00145 if ((ret = m_db->open(m_db, NULL, filename.c_str(), NULL,
00146 DB_HASH, DB_CREATE, PERMISSION_FLAGS)) != 0)
00147 {
00148 m_db->err(m_db, ret, "%s", m_filename.c_str());
00149 throw BenzeneException("HashDB: error opening db!");
00150 }
00151 Header newHeader(type);
00152 Header oldHeader;
00153 if (GetHeader(oldHeader))
00154 {
00155 if (newHeader != oldHeader)
00156 throw BenzeneException()
00157 << "HashDB: Conflicting database types. "
00158 << "old: '" << oldHeader.m_type << "' "
00159 << "new: '" << newHeader.m_type << "'\n";
00160 }
00161 else
00162 PutHeader(newHeader);
00163 }
00164
00165 template<class T>
00166 HashDB<T>::~HashDB()
00167 {
00168 int ret;
00169 if ((ret = m_db->close(m_db, CLOSE_FLAGS)) != 0)
00170 {
00171 m_db->err(m_db, ret, "%s", m_filename.c_str());
00172 throw BenzeneException("HashDB: error closing db!");
00173 }
00174 m_db = 0;
00175 }
00176
00177 template<class T>
00178 bool HashDB<T>::GetHeader(Header& header) const
00179 {
00180 static char key[32] = "dbtype";
00181 return Get(key, sizeof(key), &header, sizeof(header));
00182 }
00183
00184 template<class T>
00185 void HashDB<T>::PutHeader(Header& header)
00186 {
00187 static char key[32] = "dbtype";
00188 Put(key, sizeof(key), &header, sizeof(header));
00189 }
00190
00191 template<class T>
00192 bool HashDB<T>::Exists(hash_t hash) const
00193 {
00194 DBT key, data;
00195 memset(&key, 0, sizeof(key));
00196 memset(&data, 0, sizeof(data));
00197 key.data = &hash;
00198 key.size = sizeof(hash);
00199
00200 int ret = m_db->get(m_db, NULL, &key, &data, 0);
00201 switch(ret) {
00202 case 0:
00203 return true;
00204
00205 case DB_NOTFOUND:
00206 return false;
00207
00208 default:
00209 m_db->err(m_db, ret, "%s", m_filename.c_str());
00210 throw BenzeneException("HashDB: error in Exists()!");
00211 }
00212
00213 return true;
00214 }
00215
00216 template<class T>
00217 bool HashDB<T>::Get(hash_t hash, T& d) const
00218 {
00219 DBT key, data;
00220 memset(&key, 0, sizeof(key));
00221 memset(&data, 0, sizeof(data));
00222
00223 key.data = &hash;
00224 key.size = sizeof(hash);
00225
00226 int ret = m_db->get(m_db, NULL, &key, &data, 0);
00227 switch(ret) {
00228 case 0:
00229 d.Unpack(static_cast<byte*>(data.data));
00230 return true;
00231
00232 case DB_NOTFOUND:
00233 return false;
00234
00235 default:
00236 m_db->err(m_db, ret, "%s", m_filename.c_str());
00237 throw BenzeneException("HashDB: error in Get()!");
00238 }
00239
00240 return false;
00241 }
00242
00243 template<class T>
00244 bool HashDB<T>::Get(void* k, int ksize, void* d, int dsize) const
00245 {
00246 DBT key, data;
00247 memset(&key, 0, sizeof(key));
00248 memset(&data, 0, sizeof(data));
00249
00250 key.data = k;
00251 key.size = ksize;
00252
00253 int ret = m_db->get(m_db, NULL, &key, &data, 0);
00254 switch(ret) {
00255 case 0:
00256 memcpy(d, data.data, dsize);
00257 return true;
00258
00259 case DB_NOTFOUND:
00260 return false;
00261
00262 default:
00263 m_db->err(m_db, ret, "%s", m_filename.c_str());
00264 throw BenzeneException("HashDB: error in general Get()!");
00265 }
00266
00267 return false;
00268 }
00269
00270 template<class T>
00271 bool HashDB<T>::Put(hash_t hash, const T& d)
00272 {
00273 DBT key, data;
00274 memset(&key, 0, sizeof(key));
00275 memset(&data, 0, sizeof(data));
00276
00277 key.data = &hash;
00278 key.size = sizeof(hash);
00279
00280 data.data = d.Pack();
00281 data.size = d.PackedSize();
00282
00283 int ret;
00284 if ((ret = m_db->put(m_db, NULL, &key, &data, 0)) != 0) {
00285 m_db->err(m_db, ret, "%s", m_filename.c_str());
00286 throw BenzeneException("HashDB: error in Put()!");
00287 }
00288
00289 return true;
00290 }
00291
00292 template<class T>
00293 bool HashDB<T>::Put(void* k, int ksize, void* d, int dsize)
00294 {
00295 DBT key, data;
00296 memset(&key, 0, sizeof(key));
00297 memset(&data, 0, sizeof(data));
00298
00299 key.data = k;
00300 key.size = ksize;
00301
00302 data.data = d;
00303 data.size = dsize;
00304
00305 int ret;
00306 if ((ret = m_db->put(m_db, NULL, &key, &data, 0)) != 0) {
00307 m_db->err(m_db, ret, "%s", m_filename.c_str());
00308 throw BenzeneException("HashDB: error in general Put()!");
00309 }
00310
00311 return true;
00312 }
00313
00314 template<class T>
00315 void HashDB<T>::Flush()
00316 {
00317 m_db->sync(m_db, 0);
00318 }
00319
00320 template<class T>
00321 std::string HashDB<T>::BDBStatistics()
00322 {
00323 DB_HASH_STAT* stats_ptr;
00324 int ret;
00325 if ((ret = m_db->stat(m_db, NULL, &stats_ptr, 0)) != 0) {
00326 m_db->err(m_db, ret, "%s", m_filename.c_str());
00327 return "Error; no stats returned.";
00328 }
00329 DB_HASH_STAT& stats = *stats_ptr;
00330 std::ostringstream os;
00331 os << "[\n";
00332 os << "magic=" << stats.hash_magic << '\n';
00333 os << "version=" << stats.hash_version << '\n';
00334 os << "metaflags=" << stats.hash_metaflags << '\n';
00335 os << "nkeys=" << stats.hash_nkeys << '\n';
00336 os << "ndata=" << stats.hash_ndata << '\n';
00337 os << "pagesize=" << stats.hash_pagesize << '\n';
00338 os << "fillfactor=" << stats.hash_ffactor << '\n';
00339 os << "buckets=" << stats.hash_buckets << '\n';
00340 os << "freepages=" << stats.hash_free << '\n';
00341 os << "bytesfree=" << stats.hash_bfree << '\n';
00342 os << "bigpages=" << stats.hash_bigpages << '\n';
00343 os << "bytesbfree=" << stats.hash_big_bfree << '\n';
00344 os << "overflowpages=" << stats.hash_overflows << '\n';
00345 os << "ovfl_free=" << stats.hash_ovfl_free << '\n';
00346 os << "dup_pages=" << stats.hash_dup << '\n';
00347 os << "dup_free=" << stats.hash_dup_free << '\n';
00348 os << ']';
00349 return os.str();
00350 }
00351
00352
00353
00354 _END_BENZENE_NAMESPACE_
00355
00356 #endif // HASHDB_H
00357