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