1 /*
2  * checkpoint.cpp
3  *
4  *  Created on: Jun 12, 2014
5  *      Author: minh
6  */
7 
8 #include "checkpoint.h"
9 #include "tools.h"
10 #include "timeutil.h"
11 #include "gzstream.h"
12 #include <cstdio>
13 
14 const char* CKP_HEADER =     "--- # IQ-TREE Checkpoint ver >= 1.6";
15 const char* CKP_HEADER_OLD = "--- # IQ-TREE Checkpoint";
16 
Checkpoint()17 Checkpoint::Checkpoint() {
18 	filename = "";
19     prev_dump_time = 0;
20     dump_interval = 60; // dumping at most once per 60 seconds
21     struct_name = "";
22     compression = true;
23     header = CKP_HEADER;
24 }
25 
26 
~Checkpoint()27 Checkpoint::~Checkpoint() {
28 }
29 
30 
setFileName(string filename)31 void Checkpoint::setFileName(string filename) {
32 	this->filename = filename;
33 }
34 
35 
load(istream & in)36 void Checkpoint::load(istream &in) {
37     string line;
38     string struct_name;
39     size_t pos;
40     int listid = 0;
41     while (!in.eof()) {
42         safeGetline(in, line);
43         pos = line.find('#');
44         if (pos != string::npos)
45             line.erase(pos);
46         line.erase(line.find_last_not_of("\n\r\t")+1);
47 //            trimString(line);
48         if (line.empty()) continue;
49         if (line[0] != ' ') {
50             struct_name = "";
51         }
52 //            trimString(line);
53         line.erase(0, line.find_first_not_of(" \n\r\t"));
54         if (line.empty()) continue;
55         pos = line.find(": ");
56         if (pos != string::npos) {
57             // mapping
58             (*this)[struct_name + line.substr(0, pos)] = line.substr(pos+2);
59         } else if (line[line.length()-1] == ':') {
60             // start a new struct
61             line.erase(line.length()-1);
62             trimString(line);
63             struct_name = line + CKP_SEP;
64             listid = 0;
65             continue;
66         } else {
67             // collection
68             (*this)[struct_name + convertIntToString(listid)] = line;
69             listid++;
70         }
71     }
72 }
73 
74 
load()75 bool Checkpoint::load() {
76 	ASSERT(filename != "");
77     if (!fileExists(filename)) return false;
78     try {
79         igzstream in;
80         // set the failbit and badbit
81         in.exceptions(ios::failbit | ios::badbit);
82         in.open(filename.c_str());
83         // remove the failbit
84         in.exceptions(ios::badbit);
85         string line;
86         if (!safeGetline(in, line)) {
87             in.close();
88             return false;
89         }
90         if (line == CKP_HEADER_OLD)
91             throw "Incompatible checkpoint file from version 1.5.X or older.\nEither overwrite it with -redo option or run older version";
92         if (line != header)
93         	throw ("Invalid checkpoint file " + filename);
94         // call load from the stream
95         load(in);
96         in.clear();
97         // set the failbit again
98         in.exceptions(ios::failbit | ios::badbit);
99         in.close();
100         return true;
101     } catch (ios::failure &) {
102         outError(ERR_READ_INPUT);
103     } catch (const char *str) {
104         outError(str);
105     } catch (string &str) {
106         outError(str);
107     }
108     return false;
109 }
110 
setCompression(bool compression)111 void Checkpoint::setCompression(bool compression) {
112     this->compression = compression;
113 }
114 
115 /**
116     set the header line to overwrite the default header
117     @param header header line
118 */
setHeader(string header)119 void Checkpoint::setHeader(string header) {
120     this->header = "--- # " + header;
121 }
122 
setDumpInterval(double interval)123 void Checkpoint::setDumpInterval(double interval) {
124     dump_interval = interval;
125 }
126 
dump(ostream & out)127 void Checkpoint::dump(ostream &out) {
128     string struct_name;
129     size_t pos;
130     int listid = 0;
131     for (iterator i = begin(); i != end(); i++) {
132         if ((pos = i->first.find(CKP_SEP)) != string::npos) {
133             if (struct_name != i->first.substr(0, pos)) {
134                 struct_name = i->first.substr(0, pos);
135                 out << struct_name << ':' << endl;
136                 listid = 0;
137             }
138             // check if key is a collection
139             out << ' ' << i->first.substr(pos+1) << ": " << i->second << endl;
140         } else
141             out << i->first << ": " << i->second << endl;
142     }
143 }
144 
dump(bool force)145 void Checkpoint::dump(bool force) {
146     if (filename == "")
147         return;
148 
149     if (!force && getRealTime() < prev_dump_time + dump_interval) {
150         return;
151     }
152     prev_dump_time = getRealTime();
153     string filename_tmp = filename + ".tmp";
154     if (fileExists(filename_tmp)) {
155         outWarning("IQ-TREE was killed while writing temporary checkpoint file " + filename_tmp);
156         outWarning("You should increase checkpoint interval from the default 60 seconds");
157         outWarning("via -cptime option to avoid too frequent checkpoint for large datasets");
158     }
159     try {
160         ostream *out;
161         if (compression)
162             out = new ogzstream(filename_tmp.c_str());
163         else
164             out = new ofstream(filename_tmp.c_str());
165         out->exceptions(ios::failbit | ios::badbit);
166         *out << header << endl;
167         // call dump stream
168         dump(*out);
169         if (compression)
170             ((ogzstream*)out)->close();
171         else
172             ((ofstream*)out)->close();
173         delete out;
174 //        cout << "Checkpoint dumped" << endl;
175         if (fileExists(filename)) {
176             if (std::remove(filename.c_str()) != 0)
177                 outError("Cannot remove file ", filename);
178         }
179         if (std::rename(filename_tmp.c_str(), filename.c_str()) != 0)
180             outError("Cannot rename file ", filename_tmp);
181     } catch (ios::failure &) {
182         outError(ERR_WRITE_OUTPUT, filename.c_str());
183     }
184     // check that the dumping time is too long and increase dump_interval if necessary
185     double dump_time = getRealTime() - prev_dump_time;
186     if (dump_time*20 > dump_interval) {
187         dump_interval = ceil(dump_time*20);
188         cout << "NOTE: " << dump_time << " seconds to dump checkpoint file, increase to "
189         << dump_interval << endl;
190     }
191 }
192 
hasKey(string key)193 bool Checkpoint::hasKey(string key) {
194 	return (find(struct_name + key) != end());
195 }
196 
hasKeyPrefix(string key_prefix)197 bool Checkpoint::hasKeyPrefix(string key_prefix) {
198     string prefix = key_prefix;
199     if (!struct_name.empty())
200         prefix = struct_name + key_prefix;
201 	auto i = lower_bound(prefix);
202     if (i != end()) {
203         if (i->first.compare(0, prefix.size(), prefix) == 0)
204             return true;
205     }
206     return false;
207 }
208 
eraseKeyPrefix(string key_prefix)209 int Checkpoint::eraseKeyPrefix(string key_prefix) {
210     int count = 0;
211     iterator first_it = lower_bound(key_prefix);
212     iterator i;
213 	for (i = first_it; i != end(); i++) {
214         if (i->first.compare(0, key_prefix.size(), key_prefix) == 0)
215             count++;
216         else
217             break;
218 
219     }
220     if (count)
221         erase(first_it, i);
222     return count;
223 }
224 
keepKeyPrefix(string key_prefix)225 int Checkpoint::keepKeyPrefix(string key_prefix) {
226     map<string,string> newckp;
227     int count = 0;
228     erase(begin(), lower_bound(key_prefix));
229 
230     for (iterator i = begin(); i != end(); i++) {
231         if (i->first.compare(0, key_prefix.size(), key_prefix) == 0)
232             count++;
233         else {
234             erase(i, end());
235             break;
236         }
237 
238     }
239     return count;
240 }
241 
242 /*-------------------------------------------------------------
243  * series of get function to get value of a key
244  *-------------------------------------------------------------*/
245 
getBool(string key,bool & ret)246 bool Checkpoint::getBool(string key, bool &ret) {
247     string value;
248     if (!get(key, value)) return false;
249 	if (value == "true")
250         ret = true;
251     else if (value == "false")
252         ret = false;
253     else
254         outError("Invalid boolean value " + value + " for key " + key);
255     return true;
256 }
257 
getBool(string key)258 bool Checkpoint::getBool(string key) {
259     bool ret;
260     if (!getBool(key, ret))
261         return false;
262     return ret;
263 }
264 
265 /*-------------------------------------------------------------
266  * series of put function to put pair of (key,value)
267  *-------------------------------------------------------------*/
268 
putBool(string key,bool value)269 void Checkpoint::putBool(string key, bool value) {
270     if (value)
271         put(key, "true");
272     else
273         put(key, "false");
274 }
275 
276 
277 /*-------------------------------------------------------------
278  * nested structures
279  *-------------------------------------------------------------*/
startStruct(string name)280 void Checkpoint::startStruct(string name) {
281     struct_name = struct_name + name + CKP_SEP;
282 }
283 
284 /**
285     end the current struct
286 */
endStruct()287 void Checkpoint::endStruct() {
288     size_t pos = struct_name.find_last_of(CKP_SEP, struct_name.length()-2);
289     if (pos == string::npos)
290         struct_name = "";
291     else
292         struct_name.erase(pos+1);
293 }
294 
startList(int nelem)295 void Checkpoint::startList(int nelem) {
296     list_element.push_back(-1);
297     if (nelem > 0)
298         list_element_precision.push_back((int)ceil(log10(nelem)));
299     else
300         list_element_precision.push_back(0);
301 }
302 
setListElement(int id)303 void Checkpoint::setListElement(int id) {
304     list_element.back() = id;
305     stringstream ss;
306     ss << setw(list_element_precision.back()) << setfill('0') << list_element.back();
307     struct_name += ss.str() + CKP_SEP;
308 }
309 
addListElement()310 void Checkpoint::addListElement() {
311     list_element.back()++;
312     if (list_element.back() > 0) {
313         size_t pos = struct_name.find_last_of(CKP_SEP, struct_name.length()-2);
314         ASSERT(pos != string::npos);
315         struct_name.erase(pos+1);
316     }
317     stringstream ss;
318     ss << setw(list_element_precision.back()) << setfill('0') << list_element.back();
319 //    ss << list_element.back();
320     struct_name += ss.str() + CKP_SEP;
321 }
322 
endList()323 void Checkpoint::endList() {
324     ASSERT(!list_element.empty());
325 
326     if (list_element.back() >= 0) {
327         size_t pos = struct_name.find_last_of(CKP_SEP, struct_name.length()-2);
328         ASSERT(pos != string::npos);
329         struct_name.erase(pos+1);
330     }
331 
332     list_element.pop_back();
333     list_element_precision.pop_back();
334 
335 }
336 
getSubCheckpoint(Checkpoint * target,string partial_key)337 void Checkpoint::getSubCheckpoint(Checkpoint *target, string partial_key) {
338     int len = partial_key.length();
339     for (auto it = lower_bound(partial_key); it != end() && it->first.substr(0, len) == partial_key; it++) {
340         target->put(it->first.substr(len+1), it->second);
341     }
342 }
343 
putSubCheckpoint(Checkpoint * source,string partial_key)344 void Checkpoint::putSubCheckpoint(Checkpoint *source, string partial_key) {
345     if (!partial_key.empty())
346         startStruct(partial_key);
347     for (auto it = source->begin(); it != source->end(); it++) {
348         put(it->first, it->second);
349     }
350     if (!partial_key.empty())
351         endStruct();
352 }
353 
transferSubCheckpoint(Checkpoint * target,string partial_key,bool overwrite)354 void Checkpoint::transferSubCheckpoint(Checkpoint *target, string partial_key, bool overwrite) {
355     int len = partial_key.length();
356     for (auto it = lower_bound(partial_key); it != end() && it->first.substr(0, len) == partial_key; it++) {
357         if (overwrite || !target->hasKey(it->first))
358             target->put(it->first, it->second);
359     }
360 }
361 
362 
363 /*-------------------------------------------------------------
364  * CheckpointFactory
365  *-------------------------------------------------------------*/
366 
CheckpointFactory()367 CheckpointFactory::CheckpointFactory() {
368     checkpoint = NULL;
369 }
370 
setCheckpoint(Checkpoint * checkpoint)371 void CheckpointFactory::setCheckpoint(Checkpoint *checkpoint) {
372     this->checkpoint = checkpoint;
373 }
374 
getCheckpoint()375 Checkpoint *CheckpointFactory::getCheckpoint() {
376     return checkpoint;
377 }
378 
startCheckpoint()379 void CheckpointFactory::startCheckpoint() {
380     checkpoint->startStruct("CheckpointFactory");
381 }
382 
saveCheckpoint()383 void CheckpointFactory::saveCheckpoint() {
384     // do nothing
385 }
386 
restoreCheckpoint()387 void CheckpointFactory::restoreCheckpoint() {
388     // do nothing
389 }
390 
endCheckpoint()391 void CheckpointFactory::endCheckpoint() {
392     checkpoint->endStruct();
393 }
394 
395