00001 //---------------------------------------------------------------------------- 00002 /** @file HashDB.hpp 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 /** Class supports Pack(), Unpack(), and PackedSize(). */ 00027 template<class T> 00028 struct PackableConcept 00029 { 00030 void constraints() 00031 { 00032 const T t; 00033 int size = t.PackedSize(); 00034 size = 42; // to avoid non-used warning 00035 byte* d = t.Pack(); 00036 00037 T a = t; 00038 a.Unpack(d); 00039 } 00040 }; 00041 00042 /** Concept of a state in a HashDB. */ 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 /** Front end for a Berkely DB hash table. */ 00057 template<class T> 00058 class HashDB 00059 { 00060 BOOST_CLASS_REQUIRE(T, benzene, HashDBStateConcept); 00061 00062 public: 00063 /** Opens database, creates it if it does not exist. */ 00064 HashDB(const std::string& filename, const std::string& type); 00065 00066 /** Closes database. */ 00067 ~HashDB(); 00068 00069 /** Returns true if hash exists in database. */ 00070 bool Exists(hash_t hash) const; 00071 00072 /** Returns true if get is successful. */ 00073 bool Get(hash_t hash, T& data) const; 00074 00075 /** Returns true if put is successful. */ 00076 bool Put(hash_t hash, const T& data); 00077 00078 /** Generic Put; for adding non (hash, value) pairs. */ 00079 bool Put(void* k, int ksize, void* d, int dsize); 00080 00081 /** Generic Get. */ 00082 bool Get(void* k, int ksize, void* d, int dsize) const; 00083 00084 /** Flush the db to disk. */ 00085 void Flush(); 00086 00087 /** Returns statistics of the berkeley db. */ 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; // always null-terminated 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 /** Name of database file. */ 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