libmoost
/home/mhx/git/github/libmoost/include/moost/io/map_store.hpp
Go to the documentation of this file.
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__