1 #include <iostream>
2 #include <sstream>
3 #include "boxm2_lru_cache1.h"
4 //:
5 // \file
6 #include <boxm2/boxm2_block_metadata.h>
7 #ifdef _MSC_VER
8 #  include "vcl_msvc_warnings.h"
9 #endif
10 
11 //: PUBLIC create method, for creating singleton instance of boxm2_cache1
create(const boxm2_scene_sptr & scene,BOXM2_IO_FS_TYPE fs_type)12 void boxm2_lru_cache1::create(const boxm2_scene_sptr& scene, BOXM2_IO_FS_TYPE fs_type)
13 {
14   if (boxm2_cache1::exists())
15   {
16     if(boxm2_cache1::instance_->get_scene()->xml_path() == scene->xml_path())
17     {
18       std::cout << "boxm2_lru_cache1:: boxm2_cache1 singleton already created" << std::endl;
19     }
20     else
21     {
22       std::cout << "boxm2_lru_cache1:: destroying current cache and creating new one: " << scene->data_path() << std::endl;
23       instance_ = new boxm2_lru_cache1(scene, fs_type);
24     }
25   }
26   else {
27     instance_ = new boxm2_lru_cache1(scene, fs_type);
28   }
29 }
30 
31 //: constructor, set the directory path
boxm2_lru_cache1(const boxm2_scene_sptr & scene,BOXM2_IO_FS_TYPE fs_type)32 boxm2_lru_cache1::boxm2_lru_cache1(const boxm2_scene_sptr& scene, BOXM2_IO_FS_TYPE fs_type) : boxm2_cache1(scene,fs_type)
33 {
34   scene_dir_ = scene->data_path();
35 }
36 
37 //: destructor flushes the memory for currently ongoing asynchronous requests
~boxm2_lru_cache1()38 boxm2_lru_cache1::~boxm2_lru_cache1()
39 {
40   // save the data and delete
41   for (auto & iter : cached_data_)
42   {
43     for (auto it = iter.second.begin(); it != iter.second.end(); it++) {
44       boxm2_block_id id = it->first;
45 #if 0 // Currently causing some blocks to not save
46       if (!it->second->read_only_) {
47         boxm2_sio_mgr::save_block_data_base(scene_dir_, it->first, it->second, iter->first);
48       }
49 #endif
50       // now throw it away
51       delete it->second;
52     }
53     iter.second.clear();
54   }
55 
56   for (auto & cached_block : cached_blocks_)
57   {
58     boxm2_block_id id = cached_block.first;
59 #if 0 // Currently causing some blocks to not save
60     if (!iter->second->read_only())
61       boxm2_sio_mgr::save_block(scene_dir_, iter->second);
62 #endif
63     delete cached_block.second;
64   }
65 }
66 
67 //: delete all the memory
68 //  Caution: make sure to call write to disk methods not to loose writable data
clear_cache()69 void boxm2_lru_cache1::clear_cache()
70 {
71   // delete
72   for (auto & iter : cached_data_)
73   {
74     for (auto it = iter.second.begin(); it != iter.second.end(); it++)
75       delete it->second;
76     iter.second.clear();
77   }
78   cached_data_.clear();
79 
80   for (auto & cached_block : cached_blocks_)
81     delete cached_block.second;
82   cached_blocks_.clear();
83 }
84 
85 //: realization of abstract "get_block(block_id)"
get_block(boxm2_block_id id)86 boxm2_block* boxm2_lru_cache1::get_block(boxm2_block_id id)
87 {
88   boxm2_block_metadata data = scene_->get_block_metadata(id);
89 
90   // then look for the block you're requesting
91   if ( cached_blocks_.find(id) != cached_blocks_.end() )
92   {
93 #ifdef DEBUG
94     std::cout<<"CACHE HIT :)"<<std::endl;
95 #endif
96     return cached_blocks_[id];
97   }
98 
99 #ifdef DEBUG
100   std::cout<<"Cache miss :("<<std::endl;
101 #endif
102   // otherwise load it from disk with blocking and update cache
103   boxm2_block* loaded = boxm2_sio_mgr::load_block(scene_dir_, id, data, filesystem_);
104 
105   // if the block is null then initialize an empty one
106   if (!loaded && scene_->block_exists(id)) {
107     std::cout<<"boxm2_lru_cache1::initializing empty block "<<id<<std::endl;
108 
109     loaded = new boxm2_block(data);
110   }
111 
112   // update cache before returning the block
113   cached_blocks_[id] = loaded;
114   return loaded;
115 }
116 
117 //: get data by type and id
get_data_base(boxm2_block_id id,std::string type,std::size_t num_bytes,bool read_only)118 boxm2_data_base* boxm2_lru_cache1::get_data_base(boxm2_block_id id, std::string type, std::size_t num_bytes, bool read_only)
119 {
120   // grab a reference to the map of cached_data_
121   std::map<boxm2_block_id, boxm2_data_base*>& data_map =
122     this->cached_data_map(type);
123 
124   // then look for the block you're requesting
125   auto iter = data_map.find(id);
126   if ( iter != data_map.end() )
127   {
128     // congrats you've found the data block in cache, update cache and return block
129     if (!read_only)  // write-enable is enforced
130       iter->second->enable_write();
131     return iter->second;
132   }
133 
134   // grab from disk
135   boxm2_data_base* loaded = boxm2_sio_mgr::load_block_data_generic(scene_dir_, id, type, filesystem_);
136   boxm2_block_metadata data = scene_->get_block_metadata(id);
137 
138   // if num_bytes is greater than zero, then you're guaranteed to return a data size with that many bytes
139   if (num_bytes > 0) {
140     // if loaded from disk is good and it matches size, you found it, return
141     if (loaded && loaded->buffer_length()==num_bytes) {
142       // update data map
143       data_map[id] = loaded;
144       if (!read_only)  // write-enable is enforced
145         loaded->enable_write();
146       return loaded;
147     }
148 
149     // requesting a specific number of bytes, and not found it on disk
150     std::cout<<"boxm2_lru_cache1::initializing empty data "<<id
151             <<" type: "<<type
152             <<" to size: "<<num_bytes<<" bytes"<<std::endl;
153     loaded = new boxm2_data_base(new char[num_bytes], num_bytes, id, read_only);
154     loaded->set_default_value(type, data);
155   }
156   else {
157     // otherwise it's a miss, load sync from disk, update cache
158     // loaded = boxm2_sio_mgr::load_block_data_generic(scene_dir_, id, type, filesystem_);
159     if (!loaded && scene_->block_exists(id)) {
160       std::cout<<"boxm2_lru_cache1::initializing empty data "<<id<<" type: "<<type<<std::endl;
161       loaded = new boxm2_data_base(data, type, read_only);
162     }
163   }
164 
165   // update data map
166   data_map[id] = loaded;
167   if(!read_only)
168     loaded->enable_write();
169   return loaded;
170 }
171 
172 //: returns a data_base pointer which is initialized to the default value of the type.
173 //  If a block for this type exists on the cache, it is removed and replaced with the new one.
174 //  This method does not check whether a block of this type already exists on the disk nor writes it to the disk
get_data_base_new(boxm2_block_id id,std::string type,std::size_t num_bytes,bool read_only)175 boxm2_data_base* boxm2_lru_cache1::get_data_base_new(boxm2_block_id id, std::string type, std::size_t num_bytes, bool read_only)
176 {
177   boxm2_data_base* block_data;
178   if (num_bytes > 0)   {
179     boxm2_block_metadata data = scene_->get_block_metadata(id);
180     // requesting a specific number of bytes,
181     //std::cout<<"boxm2_lru_cache1::initializing empty data "<<id
182     //        <<" type: "<<type
183     //        <<" to size: "<<num_bytes<<" bytes"<<std::endl;
184     std::cout<<id<<" init empty "<<type<<std::endl;
185     block_data = new boxm2_data_base(new char[num_bytes], num_bytes, id, read_only);
186     block_data->set_default_value(type, data);
187   }
188   else {
189     // initialize an empty block
190     //std::cout<<"boxm2_lru_cache1::initializing empty data "<<id<<" type: "<<type<<std::endl;
191     std::cout<<id<<" init empty "<<type<<std::endl;
192     boxm2_block_metadata data = scene_->get_block_metadata(id);
193     // the following constructor also sets the default values
194     block_data = new boxm2_data_base(data, type, read_only);
195   }
196 
197   // grab a reference to the map of cached_data_
198   std::map<boxm2_block_id, boxm2_data_base*>& data_map = this->cached_data_map(type);
199 
200   // then look for the block you're requesting
201   auto iter = data_map.find(id);
202   if ( iter != data_map.end() )
203   {
204     // congrats you've found the data block in cache, now throw it away
205     delete iter->second;
206     data_map.erase(iter);
207   }
208 
209   // now store the block in the cache
210   data_map[id] = block_data;
211   return block_data;
212 }
213 
214 //: removes data from this cache (may or may not write to disk first)
remove_data_base(boxm2_block_id id,std::string type)215 void boxm2_lru_cache1::remove_data_base(boxm2_block_id id, std::string type)
216 {
217   // grab a reference to the map of cached_data_
218   std::map<boxm2_block_id, boxm2_data_base*>& data_map =
219     this->cached_data_map(type);
220   // then look for the block you're requesting
221   auto rem = data_map.find(id);
222   if ( rem != data_map.end() )
223   {
224     // found the block,
225     boxm2_data_base* litter = data_map[id];
226 
227     if (!litter->read_only_) {
228       // save it
229       std::cout<<"boxm2_lru_cache1::remove_data_base "<<type<<':'<<id<<"; saving to disk"<<std::endl;
230       boxm2_sio_mgr::save_block_data_base(scene_dir_, id, litter, type);
231     }
232     else
233       std::cout<<"boxm2_lru_cache1::remove_data_base "<<type<<':'<<id<<"; not saving to disk"<<std::endl;
234     // now throw it away
235     delete litter;
236     data_map.erase(rem);
237   }
238 }
239 
240 //: replaces data in the cache with one here
replace_data_base(boxm2_block_id id,std::string type,boxm2_data_base * replacement)241 void boxm2_lru_cache1::replace_data_base(boxm2_block_id id, std::string type, boxm2_data_base* replacement)
242 {
243   // grab a reference to the map of cached_data_
244   std::map<boxm2_block_id, boxm2_data_base*>& data_map =
245     this->cached_data_map(type);
246 
247   // find old data base and copy it's read_only/write status
248   auto rem = data_map.find(id);
249   if ( rem != data_map.end() )
250   {
251     // found the block,
252     boxm2_data_base* litter = data_map[id];
253     replacement->read_only_ = litter->read_only_;
254 #if 0 // don't need to write on a replace data...
255     if (!litter->read_only_) {
256       boxm2_sio_mgr::save_block_data_base(scene_dir_, id, litter, type);
257     }
258 #endif
259     // now throw it away
260     delete litter;
261     data_map.erase(rem);
262   }
263 
264   // this->remove_data_base(id, type);
265   // put the new one in there
266   data_map[id] = replacement;
267 }
268 
269 //: helper method returns a reference to correct data map (ensures one exists)
cached_data_map(const std::string & prefix)270 std::map<boxm2_block_id, boxm2_data_base*>& boxm2_lru_cache1::cached_data_map(const std::string& prefix)
271 {
272   // if map for this particular data type doesn't exist, initialize it
273   if ( cached_data_.find(prefix) == cached_data_.end() )
274   {
275     std::map<boxm2_block_id, boxm2_data_base*> dmap;
276     cached_data_[prefix] = dmap;
277   }
278 
279   // grab a reference to the map of cached_data_ and return it
280   std::map<boxm2_block_id, boxm2_data_base*>& data_map = cached_data_[prefix];
281   return data_map;
282 }
283 
284 //: helper method says whether or not block id is valid
is_valid_id(const boxm2_block_id & id)285 bool boxm2_lru_cache1::is_valid_id(const boxm2_block_id& id)
286 {
287   // use scene here to determine if this id is valid
288   return scene_->block_exists(id);
289 }
290 
291 
292 //: Summarizes this cache's data
to_string()293 std::string boxm2_lru_cache1::to_string()
294 {
295   std::stringstream stream;
296   stream << "boxm2_lru_cache1:: scene dir="<<scene_dir_<<'\n'
297          << "  blocks: ";
298   std::map<boxm2_block_id, boxm2_block*>::iterator blk_iter;
299   for (blk_iter = cached_blocks_.begin(); blk_iter != cached_blocks_.end(); ++blk_iter) {
300     boxm2_block_id id = blk_iter->first;
301     stream << '(' << id /* << ',' << blk_iter->second */ << ")  ";
302   }
303 
304   std::map<std::string, std::map<boxm2_block_id, boxm2_data_base*> >::iterator dat_iter;
305   for (dat_iter = cached_data_.begin(); dat_iter != cached_data_.end(); ++dat_iter)
306   {
307     std::string data_type = dat_iter->first;
308     stream<< "\n  data: "<<data_type<<' ';
309     std::map<boxm2_block_id, boxm2_data_base*> dmap = dat_iter->second;
310     std::map<boxm2_block_id, boxm2_data_base*>::iterator it;
311     for (it = dmap.begin(); it != dmap.end(); ++it)
312     {
313       boxm2_block_id id = it->first;
314       stream<< '(' << id /*<< ',' <<it->second */<< ")  ";
315     }
316   }
317   return stream.str();
318 }
319 
320 //: dumps all data onto disk
write_to_disk()321 void boxm2_lru_cache1::write_to_disk()
322 {
323    // save the data and delete
324   for (auto & iter : cached_data_)
325   {
326     for (auto it = iter.second.begin(); it != iter.second.end(); it++) {
327       boxm2_block_id id = it->first;
328       // if (!it->second->read_only_)
329         boxm2_sio_mgr::save_block_data_base(scene_dir_, it->first, it->second, iter.first);
330     }
331   }
332 
333   for (auto & cached_block : cached_blocks_)
334   {
335     boxm2_block_id id = cached_block.first;
336     // if (!iter->second->read_only())
337     boxm2_sio_mgr::save_block(scene_dir_, cached_block.second);
338   }
339 }
340 
341 //: shows elements in cache
operator <<(std::ostream & s,boxm2_lru_cache1 & scene)342 std::ostream& operator<<(std::ostream &s, boxm2_lru_cache1& scene)
343 {
344   return s << scene.to_string();
345 }
346