libmoost
|
00001 /* vim:set ts=3 sw=3 sts=3 et: */ 00028 #ifndef MOOST_IO_MAP_STORE_HPP__ 00029 #define MOOST_IO_MAP_STORE_HPP__ 00030 00031 #include <string> 00032 #include <stdexcept> 00033 #include <iostream> 00034 00035 #include <boost/archive/binary_iarchive.hpp> 00036 #include <boost/archive/binary_oarchive.hpp> 00037 #include <boost/filesystem/path.hpp> 00038 #include <boost/filesystem/operations.hpp> 00039 #include <boost/functional/hash.hpp> 00040 #include <boost/thread/mutex.hpp> 00041 #include <boost/scoped_ptr.hpp> 00042 00043 #include "../container/sparse_hash_map.hpp" 00044 00045 #include "variable_store.hpp" 00046 00047 namespace moost { namespace io { 00048 00051 template<class Key, class HashFcn = boost::hash< Key > > 00052 class map_store 00053 { 00054 private: 00055 00056 struct location 00057 { 00058 location(size_t block_size_, size_t index_) : block_size(block_size_), index(index_) {} 00059 location() : block_size(0), index(0) {} 00060 size_t block_size; 00061 size_t index; 00062 }; 00063 00064 typedef moost::container::sparse_hash_map<Key, location, HashFcn > hm_key_location; 00065 00066 std::string m_key_location_path; 00067 variable_store m_variable_store; 00068 hm_key_location m_key_location; 00069 boost::mutex m_mutex; 00070 00073 void get(boost::scoped_ptr< variable_store::scoped_block > & p, 00074 const Key & key) 00075 { 00076 boost::mutex::scoped_lock lock(m_mutex); 00077 // if key is found, loads index into the scoped block 00078 typename hm_key_location::const_iterator it = m_key_location.find(key); 00079 00080 if (it == m_key_location.end()) 00081 return; 00082 00083 p.reset( new variable_store::scoped_block(m_variable_store, it->second.block_size, it->second.index) ); 00084 } 00085 00088 void alloc(boost::scoped_ptr< variable_store::scoped_block > & p, 00089 const Key & key, 00090 size_t block_size) 00091 { 00092 boost::mutex::scoped_lock lock(m_mutex); 00093 // don't support reallocing for now 00094 typename hm_key_location::const_iterator it = m_key_location.find(key); 00095 00096 if (it != m_key_location.end()) 00097 throw std::invalid_argument("realloc not supported"); 00098 00099 p.reset( new variable_store::scoped_block(m_variable_store, block_size) ); 00100 m_key_location[key] = location(p->block_size(), p->index()); 00101 } 00102 00104 void free(const Key & key) 00105 { 00106 boost::mutex::scoped_lock lock(m_mutex); 00107 m_key_location.erase(key); 00108 } 00109 00110 public: 00111 00112 // TODO: maybe an auto_scoped_block that uses a stringstream instead, resizes automatically depending on what you do to it 00113 00115 class scoped_block 00116 { 00117 private: 00118 map_store<Key, HashFcn> & m_map_store; 00119 const Key m_key; 00120 boost::scoped_ptr< variable_store::scoped_block > m_pscoped_block; 00121 bool m_free; 00122 public: 00123 scoped_block(map_store<Key, HashFcn> & map_store_, 00124 const Key & key) 00125 : m_map_store(map_store_), 00126 m_key(key), 00127 m_free(false) 00128 { 00129 map_store_.get(m_pscoped_block, key); 00130 } 00131 scoped_block(map_store<Key, HashFcn> & map_store_, 00132 const Key & key, 00133 size_t block_size) 00134 : m_map_store(map_store_), 00135 m_key(key), 00136 m_free(false) 00137 { 00138 map_store_.alloc(m_pscoped_block, key, block_size); 00139 } 00140 ~scoped_block() 00141 { 00142 if (m_free) 00143 m_map_store.free(m_key); 00144 } 00145 void free() 00146 { 00147 m_pscoped_block->free(); 00148 m_free = true; 00149 } 00150 std::fstream & operator * () { return m_pscoped_block->operator * (); } 00151 std::fstream * operator ->() { return m_pscoped_block->operator ->(); } 00152 size_t index() { return m_pscoped_block->index(); } 00153 size_t block_size() { return m_pscoped_block->block_size(); } 00154 operator bool () const { return m_pscoped_block; } 00155 bool operator! () const { return m_pscoped_block.operator !(); } 00156 }; 00157 00160 map_store(const std::string & base_path, 00161 size_t min_block_size = 64, 00162 size_t max_block_size = 16777216, 00163 size_t streams_per_block_size = 8) 00164 : m_key_location_path((boost::filesystem::path(base_path) / boost::filesystem::path("key_locations")).string()), 00165 m_variable_store(base_path, min_block_size, max_block_size, streams_per_block_size) 00166 { 00167 std::ifstream in(m_key_location_path.c_str(), std::ios::binary); 00168 00169 if (!in.is_open()) 00170 return; 00171 00172 size_t size = 0; 00173 boost::archive::binary_iarchive ia(in, boost::archive::no_header); 00174 ia >> size; 00175 location loc; 00176 while (size-- != 0) 00177 { 00178 Key key; // avoid weirdness with boost::serialization and strings... need to reinit each time? scary 00179 ia >> key; 00180 ia >> loc.block_size; 00181 ia >> loc.index; 00182 m_key_location[key] = loc; 00183 } 00184 } 00185 00188 ~map_store() 00189 { 00190 size_t size = m_key_location.size(); 00191 if ( size <= 0 ) 00192 return; 00193 00194 try 00195 { 00196 std::ofstream out(m_key_location_path.c_str(), std::ios::binary); 00197 if ( !out.is_open() ) 00198 throw std::runtime_error("Cannot open file <" + m_key_location_path + "> for output!"); 00199 boost::archive::binary_oarchive oa(out, boost::archive::no_header); 00200 oa << size; 00201 for (typename hm_key_location::const_iterator it = m_key_location.begin(); it != m_key_location.end(); ++it) 00202 { 00203 oa << it->first; 00204 oa << it->second.block_size; 00205 oa << it->second.index; 00206 } 00207 } 00208 catch (const std::exception& e) 00209 { 00210 std::cerr << "ERROR: " << e.what() << std::endl; 00211 } 00212 } 00213 00217 void set_deleted_key(const Key & key) 00218 { 00219 m_key_location.set_deleted_key(key); 00220 } 00221 }; 00222 00223 }} // moost::io 00224 00225 #endif // MOOST_IO_MAP_STORE_HPP__